summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2023-10-27 17:02:53 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2023-10-27 17:04:08 +0200
commit3dce9b5818576f04ce21cec4b3686eda012e5b65 (patch)
treefe3d59c6da3e62c74563710ba63996585293c743 /chromium/chrome/browser
parent5a424f4a7b188b75da63eb697f63558af0b17f6f (diff)
BASELINE: Update Chromium to 118.0.5993.24
Diffstat (limited to 'chromium/chrome/browser')
-rw-r--r--chromium/chrome/browser/3pcd/heuristics/BUILD.gn (renamed from chromium/chrome/browser/3pcd_heuristics/BUILD.gn)0
-rw-r--r--chromium/chrome/browser/BUILD.gn411
-rw-r--r--chromium/chrome/browser/accessibility/DEPS6
-rw-r--r--chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.cc80
-rw-r--r--chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.h8
-rw-r--r--chromium/chrome/browser/accessibility/accessibility_prefs/OWNERS3
-rw-r--r--chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_android_policy_browsertest.cc43
-rw-r--r--chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.cc46
-rw-r--r--chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.h34
-rw-r--r--chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller_browsertest.cc43
-rw-r--r--chromium/chrome/browser/accessibility/ax_screen_ai_annotator.cc48
-rw-r--r--chromium/chrome/browser/accessibility/ax_screen_ai_annotator.h11
-rw-r--r--chromium/chrome/browser/accessibility/hierarchysnapshotter/android/BUILD.gn2
-rw-r--r--chromium/chrome/browser/accessibility/image_annotation_browsertest.cc8
-rw-r--r--chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.cc5
-rw-r--r--chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.h2
-rw-r--r--chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc200
-rw-r--r--chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h17
-rw-r--r--chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc220
-rw-r--r--chromium/chrome/browser/accessibility/live_caption/live_caption_test_util.cc4
-rw-r--r--chromium/chrome/browser/accessibility/live_translate_controller_factory.cc5
-rw-r--r--chromium/chrome/browser/accessibility/live_translate_controller_factory.h2
-rw-r--r--chromium/chrome/browser/accessibility/pdf_ocr_controller.cc44
-rw-r--r--chromium/chrome/browser/accessibility/pdf_ocr_controller.h3
-rw-r--r--chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.cc6
-rw-r--r--chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.h2
-rw-r--r--chromium/chrome/browser/accessibility/screen_ai_service_browsertest.cc4
-rw-r--r--chromium/chrome/browser/accessibility/service/accessibility_service_router.cc13
-rw-r--r--chromium/chrome/browser/accessibility/service/accessibility_service_router.h6
-rw-r--r--chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.cc14
-rw-r--r--chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.h4
-rw-r--r--chromium/chrome/browser/android/browserservices/intents/BUILD.gn3
-rw-r--r--chromium/chrome/browser/android/browserservices/verification/BUILD.gn2
-rw-r--r--chromium/chrome/browser/android/customtabs/branding/BUILD.gn2
-rw-r--r--chromium/chrome/browser/android/httpclient/BUILD.gn1
-rw-r--r--chromium/chrome/browser/android/intents/BUILD.gn27
-rw-r--r--chromium/chrome/browser/android/vr/BUILD.gn2
-rw-r--r--chromium/chrome/browser/android/webapk/BUILD.gn7
-rw-r--r--chromium/chrome/browser/android/webapk/proto/BUILD.gn11
-rw-r--r--chromium/chrome/browser/apps/almanac_api_client/BUILD.gn12
-rw-r--r--chromium/chrome/browser/apps/app_deduplication_service/BUILD.gn73
-rw-r--r--chromium/chrome/browser/apps/app_discovery_service/BUILD.gn85
-rw-r--r--chromium/chrome/browser/apps/app_discovery_service/almanac_api/BUILD.gn26
-rw-r--r--chromium/chrome/browser/apps/app_discovery_service/recommended_arc_apps/BUILD.gn84
-rw-r--r--chromium/chrome/browser/apps/app_preload_service/BUILD.gn9
-rw-r--r--chromium/chrome/browser/apps/app_provisioning_service/BUILD.gn19
-rw-r--r--chromium/chrome/browser/apps/app_service/BUILD.gn38
-rw-r--r--chromium/chrome/browser/apps/app_shim/BUILD.gn5
-rw-r--r--chromium/chrome/browser/apps/link_capturing/BUILD.gn52
-rw-r--r--chromium/chrome/browser/apps/platform_apps/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ash/BUILD.gn574
-rw-r--r--chromium/chrome/browser/ash/crosapi/BUILD.gn45
-rw-r--r--chromium/chrome/browser/ash/fusebox/BUILD.gn4
-rw-r--r--chromium/chrome/browser/ash/input_method/mojom/BUILD.gn13
-rw-r--r--chromium/chrome/browser/ash/input_method/mojom/editor.mojom40
-rw-r--r--chromium/chrome/browser/ash/login/oobe_quick_start/BUILD.gn4
-rw-r--r--chromium/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn13
-rw-r--r--chromium/chrome/browser/ash/nearby/BUILD.gn11
-rw-r--r--chromium/chrome/browser/ash/system_extensions/api/managed_device_health_services/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ash/system_web_apps/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ash/system_web_apps/apps/BUILD.gn63
-rw-r--r--chromium/chrome/browser/ash/system_web_apps/test_support/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ash/telemetry_extension/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ash/telemetry_extension/common/BUILD.gn31
-rw-r--r--chromium/chrome/browser/ash/telemetry_extension/events/BUILD.gn3
-rw-r--r--chromium/chrome/browser/ash/telemetry_extension/routines/BUILD.gn48
-rw-r--r--chromium/chrome/browser/ash/telemetry_extension/telemetry/BUILD.gn21
-rw-r--r--chromium/chrome/browser/autofill/BUILD.gn25
-rw-r--r--chromium/chrome/browser/autofill/android/BUILD.gn56
-rw-r--r--chromium/chrome/browser/autofill/test/BUILD.gn7
-rw-r--r--chromium/chrome/browser/back_press/android/BUILD.gn3
-rw-r--r--chromium/chrome/browser/banners/android/BUILD.gn2
-rw-r--r--chromium/chrome/browser/bluetooth/android/BUILD.gn3
-rw-r--r--chromium/chrome/browser/breadcrumbs/BUILD.gn1
-rw-r--r--chromium/chrome/browser/browser_resources.grd25
-rw-r--r--chromium/chrome/browser/browsing_data/android/BUILD.gn19
-rw-r--r--chromium/chrome/browser/chrome_for_testing/BUILD.gn45
-rw-r--r--chromium/chrome/browser/chrome_notification_types.h15
-rw-r--r--chromium/chrome/browser/chromeos/BUILD.gn110
-rw-r--r--chromium/chrome/browser/chromeos/extensions/telemetry/api/BUILD.gn7
-rw-r--r--chromium/chrome/browser/chromeos/extensions/telemetry/api/common/BUILD.gn41
-rw-r--r--chromium/chrome/browser/chromeos/extensions/telemetry/api/diagnostics/BUILD.gn5
-rw-r--r--chromium/chrome/browser/chromeos/extensions/telemetry/api/events/BUILD.gn69
-rw-r--r--chromium/chrome/browser/chromeos/extensions/telemetry/api/routines/BUILD.gn138
-rw-r--r--chromium/chrome/browser/chromeos/extensions/telemetry/api/telemetry/BUILD.gn23
-rw-r--r--chromium/chrome/browser/chromeos/office_web_app/BUILD.gn4
-rw-r--r--chromium/chrome/browser/commerce/android/BUILD.gn4
-rw-r--r--chromium/chrome/browser/commerce/merchant_viewer/android/BUILD.gn2
-rw-r--r--chromium/chrome/browser/companion/core/mojom/companion.mojom15
-rw-r--r--chromium/chrome/browser/content_creation/notes/internal/android/BUILD.gn1
-rw-r--r--chromium/chrome/browser/creator/android/BUILD.gn4
-rw-r--r--chromium/chrome/browser/devtools/BUILD.gn15
-rw-r--r--chromium/chrome/browser/devtools/aida_client.cc168
-rw-r--r--chromium/chrome/browser/devtools/aida_client.h58
-rw-r--r--chromium/chrome/browser/devtools/aida_client_unittest.cc241
-rw-r--r--chromium/chrome/browser/devtools/buildflags.gni5
-rw-r--r--chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc11
-rw-r--r--chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h2
-rw-r--r--chromium/chrome/browser/devtools/chrome_devtools_session.cc4
-rw-r--r--chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc10
-rw-r--r--chromium/chrome/browser/devtools/device/devtools_device_discovery.cc9
-rw-r--r--chromium/chrome/browser/devtools/devtools_browsertest.cc75
-rw-r--r--chromium/chrome/browser/devtools/devtools_dock_tile_mac.mm4
-rw-r--r--chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc4
-rw-r--r--chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.h4
-rw-r--r--chromium/chrome/browser/devtools/devtools_interactive_browsertest.cc79
-rw-r--r--chromium/chrome/browser/devtools/devtools_ui_bindings.cc40
-rw-r--r--chromium/chrome/browser/devtools/devtools_ui_bindings.h19
-rw-r--r--chromium/chrome/browser/devtools/devtools_window.cc21
-rw-r--r--chromium/chrome/browser/devtools/protocol/autofill_handler.cc204
-rw-r--r--chromium/chrome/browser/devtools/protocol/autofill_handler.h36
-rw-r--r--chromium/chrome/browser/devtools/protocol/browser_handler.cc9
-rw-r--r--chromium/chrome/browser/devtools/protocol/cast_handler.cc4
-rw-r--r--chromium/chrome/browser/devtools/protocol/devtools_autofill_browsertest.cc251
-rw-r--r--chromium/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc293
-rw-r--r--chromium/chrome/browser/devtools/protocol/page_handler.cc16
-rw-r--r--chromium/chrome/browser/devtools/protocol/page_handler.h1
-rw-r--r--chromium/chrome/browser/devtools/protocol/security_handler.cc1
-rw-r--r--chromium/chrome/browser/devtools/protocol/system_info_handler.cc12
-rw-r--r--chromium/chrome/browser/devtools/protocol/target_handler.cc12
-rw-r--r--chromium/chrome/browser/dips/BUILD.gn6
-rw-r--r--chromium/chrome/browser/download/android/BUILD.gn1
-rw-r--r--chromium/chrome/browser/download/internal/android/BUILD.gn7
-rw-r--r--chromium/chrome/browser/engagement/android/BUILD.gn1
-rw-r--r--chromium/chrome/browser/enterprise/connectors/device_trust/attestation/ash/BUILD.gn11
-rw-r--r--chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/BUILD.gn20
-rw-r--r--chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn1
-rw-r--r--chromium/chrome/browser/enterprise/connectors/device_trust/key_management/common/BUILD.gn7
-rw-r--r--chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/BUILD.gn1
-rw-r--r--chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/BUILD.gn4
-rw-r--r--chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/BUILD.gn6
-rw-r--r--chromium/chrome/browser/enterprise/connectors/device_trust/test/BUILD.gn7
-rw-r--r--chromium/chrome/browser/enterprise/data_controls/BUILD.gn5
-rw-r--r--chromium/chrome/browser/enterprise/platform_auth/BUILD.gn2
-rw-r--r--chromium/chrome/browser/error_reporting/BUILD.gn2
-rw-r--r--chromium/chrome/browser/extensions/BUILD.gn32
-rw-r--r--chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc19
-rw-r--r--chromium/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc405
-rw-r--r--chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h52
-rw-r--r--chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api_unittest.cc140
-rw-r--r--chromium/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc39
-rw-r--r--chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h6
-rw-r--r--chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h2
-rw-r--r--chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc72
-rw-r--r--chromium/chrome/browser/extensions/api/automation/automation_apitest.cc152
-rw-r--r--chromium/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc1
-rw-r--r--chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc66
-rw-r--r--chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc13
-rw-r--r--chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc12
-rw-r--r--chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc49
-rw-r--r--chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h5
-rw-r--r--chromium/chrome/browser/extensions/api/commands/command_service.cc1
-rw-r--r--chromium/chrome/browser/extensions/api/content_settings/content_settings_api.cc24
-rw-r--r--chromium/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/context_menus/extension_context_menu_browsertest.cc95
-rw-r--r--chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/debugger/debugger_api.cc72
-rw-r--r--chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar_delegate.h3
-rw-r--r--chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc71
-rw-r--r--chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/developer_private_api.cc206
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h59
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc299
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc20
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc68
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.h11
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc161
-rw-r--r--chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc13
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc17
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_lacros_apitest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc9
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc16
-rw-r--r--chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc18
-rw-r--r--chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc72
-rw-r--r--chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/file_system/file_entry_picker.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/font_settings/font_settings_api.cc10
-rw-r--r--chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc192
-rw-r--r--chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h48
-rw-r--r--chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc58
-rw-r--r--chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc96
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_api.cc10
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_api.h11
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_apitest.cc277
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc43
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h4
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_constants.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_constants.h2
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h2
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc34
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc64
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h10
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_private_api.cc28
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_private_api.h29
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_private_apitest.cc69
-rw-r--r--chromium/chrome/browser/extensions/api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h30
-rw-r--r--chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc260
-rw-r--r--chromium/chrome/browser/extensions/api/identity/web_auth_flow.h62
-rw-r--r--chromium/chrome/browser/extensions/api/identity/web_auth_flow_browsertest.cc317
-rw-r--r--chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.cc11
-rw-r--r--chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h9
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc1
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc20
-rw-r--r--chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/input_ime/input_ime_apitest_chromeos.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc13
-rw-r--r--chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc10
-rw-r--r--chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc120
-rw-r--r--chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.h12
-rw-r--r--chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc1
-rw-r--r--chromium/chrome/browser/extensions/api/management/management_api_unittest.cc14
-rw-r--r--chromium/chrome/browser/extensions/api/management/management_apitest.cc64
-rw-r--r--chromium/chrome/browser/extensions/api/mdns/mdns_api.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc20
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/messaging_apitest.cc25
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc37
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc11
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.h6
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc80
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h2
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc31
-rw-r--r--chromium/chrome/browser/extensions/api/page_capture/page_capture_api.h6
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc56
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.h16
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc216
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc128
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h79
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc89
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h32
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc30
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h5
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc193
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h30
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc696
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils.h6
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc79
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.h35
-rw-r--r--chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.h3
-rw-r--r--chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h2
-rw-r--r--chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc1
-rw-r--r--chromium/chrome/browser/extensions/api/preference/preference_api.cc105
-rw-r--r--chromium/chrome/browser/extensions/api/preference/preference_api.h2
-rw-r--r--chromium/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc298
-rw-r--r--chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc10
-rw-r--r--chromium/chrome/browser/extensions/api/preference/preference_apitest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/preference/preference_helpers.h1
-rw-r--r--chromium/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc16
-rw-r--r--chromium/chrome/browser/extensions/api/printing/fake_print_job_controller_ash.h5
-rw-r--r--chromium/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc13
-rw-r--r--chromium/chrome/browser/extensions/api/printing/printing_api_utils.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/printing/printing_api_utils_unittest.cc57
-rw-r--r--chromium/chrome/browser/extensions/api/printing/printing_apitest.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/proxy/proxy_api.cc9
-rw-r--r--chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc29
-rw-r--r--chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc56
-rw-r--r--chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc29
-rw-r--r--chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc43
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/OWNERS2
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_api.cc266
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_api.h133
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.cc17
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.h20
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_api_unittest.cc538
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_apitest.cc67
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.cc143
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.h67
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_util.cc24
-rw-r--r--chromium/chrome/browser/extensions/api/reading_list/reading_list_util.h18
-rw-r--r--chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h2
-rw-r--r--chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/scripting/scripting_api.cc195
-rw-r--r--chromium/chrome/browser/extensions/api/scripting/scripting_api.h8
-rw-r--r--chromium/chrome/browser/extensions/api/scripting/scripting_apitest.cc27
-rw-r--r--chromium/chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/generated_prefs.h12
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h2
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc31
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h2
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc11
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.h5
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h2
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_unittest.cc160
-rw-r--r--chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.h6
-rw-r--r--chromium/chrome/browser/extensions/api/storage/policy_value_store.h3
-rw-r--r--chromium/chrome/browser/extensions/api/storage/setting_sync_data.h5
-rw-r--r--chromium/chrome/browser/extensions/api/storage/settings_sync_processor.h3
-rw-r--r--chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h2
-rw-r--r--chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_api.cc70
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_apitest.cc17
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_constants.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_constants.h3
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/windows_event_router.h2
-rw-r--r--chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc52
-rw-r--r--chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc11
-rw-r--r--chromium/chrome/browser/extensions/api/virtual_keyboard_private/virtual_keyboard_private_apitest.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc17
-rw-r--r--chromium/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc17
-rw-r--r--chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc39
-rw-r--r--chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc83
-rw-r--r--chromium/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc24
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc93
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h46
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc28
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc15
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc50
-rw-r--r--chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.h2
-rw-r--r--chromium/chrome/browser/favicon/BUILD.gn1
-rw-r--r--chromium/chrome/browser/feature_engagement/BUILD.gn13
-rw-r--r--chromium/chrome/browser/feed/android/BUILD.gn29
-rw-r--r--chromium/chrome/browser/feedback/android/BUILD.gn1
-rw-r--r--chromium/chrome/browser/first_run/android/BUILD.gn1
-rw-r--r--chromium/chrome/browser/flags/BUILD.gn7
-rw-r--r--chromium/chrome/browser/gcm/gcm_profile_service_factory.cc18
-rw-r--r--chromium/chrome/browser/gcm/instance_id/instance_id_profile_service_factory.cc28
-rw-r--r--chromium/chrome/browser/headless/BUILD.gn12
-rw-r--r--chromium/chrome/browser/history_clusters/BUILD.gn1
-rw-r--r--chromium/chrome/browser/image_descriptions/BUILD.gn4
-rw-r--r--chromium/chrome/browser/image_editor/BUILD.gn1
-rw-r--r--chromium/chrome/browser/incognito/BUILD.gn6
-rw-r--r--chromium/chrome/browser/lacros/cros_apps/api/BUILD.gn28
-rw-r--r--chromium/chrome/browser/lacros/cros_apps/api/diagnostics/BUILD.gn20
-rw-r--r--chromium/chrome/browser/language/android/BUILD.gn5
-rw-r--r--chromium/chrome/browser/lens/BUILD.gn2
-rw-r--r--chromium/chrome/browser/loading_modal/android/BUILD.gn1
-rw-r--r--chromium/chrome/browser/long_screenshots/BUILD.gn2
-rw-r--r--chromium/chrome/browser/mac/BUILD.gn2
-rw-r--r--chromium/chrome/browser/mandatory_reauth/android/internal/BUILD.gn2
-rw-r--r--chromium/chrome/browser/media/effects/BUILD.gn32
-rw-r--r--chromium/chrome/browser/media/router/BUILD.gn11
-rw-r--r--chromium/chrome/browser/media/router/discovery/BUILD.gn1
-rw-r--r--chromium/chrome/browser/media/webrtc/audio_debug_recordings_handler.h3
-rw-r--r--chromium/chrome/browser/media/webrtc/capture_handle_browsertest.cc7
-rw-r--r--chromium/chrome/browser/media/webrtc/capture_policy_utils.cc6
-rw-r--r--chromium/chrome/browser/media/webrtc/capture_policy_utils_browsertest.cc (renamed from chromium/chrome/browser/media/webrtc/multi_capture_browsertest.cc)0
-rw-r--r--chromium/chrome/browser/media/webrtc/conditional_focus_browsertest.cc4
-rw-r--r--chromium/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc2
-rw-r--r--chromium/chrome/browser/media/webrtc/desktop_capturer_wrapper.cc41
-rw-r--r--chromium/chrome/browser/media/webrtc/desktop_capturer_wrapper.h41
-rw-r--r--chromium/chrome/browser/media/webrtc/desktop_media_list_ash.cc7
-rw-r--r--chromium/chrome/browser/media/webrtc/desktop_media_list_ash_unittest.cc17
-rw-r--r--chromium/chrome/browser/media/webrtc/desktop_media_list_base.cc25
-rw-r--r--chromium/chrome/browser/media/webrtc/desktop_media_list_base.h7
-rw-r--r--chromium/chrome/browser/media/webrtc/desktop_media_picker_factory_impl.cc29
-rw-r--r--chromium/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc108
-rw-r--r--chromium/chrome/browser/media/webrtc/fake_desktop_media_picker_factory.h5
-rw-r--r--chromium/chrome/browser/media/webrtc/get_all_screen_media_browsertest.cc132
-rw-r--r--chromium/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h8
-rw-r--r--chromium/chrome/browser/media/webrtc/media_device_salt_service_factory.cc40
-rw-r--r--chromium/chrome/browser/media/webrtc/media_device_salt_service_factory.h39
-rw-r--r--chromium/chrome/browser/media/webrtc/media_device_salt_service_factory_unittest.cc30
-rw-r--r--chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.cc145
-rw-r--r--chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.h21
-rw-r--r--chromium/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc35
-rw-r--r--chromium/chrome/browser/media/webrtc/media_stream_device_permission_context_unittest.cc31
-rw-r--r--chromium/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc89
-rw-r--r--chromium/chrome/browser/media/webrtc/native_desktop_media_list.cc195
-rw-r--r--chromium/chrome/browser/media/webrtc/native_desktop_media_list.h10
-rw-r--r--chromium/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc38
-rw-r--r--chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc42
-rw-r--r--chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h3
-rw-r--r--chromium/chrome/browser/media/webrtc/region_capture_browsertest.cc18
-rw-r--r--chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h22
-rw-r--r--chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm101
-rw-r--r--chromium/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.mm80
-rw-r--r--chromium/chrome/browser/media/webrtc/tab_capture_access_handler_unittest.cc2
-rw-r--r--chromium/chrome/browser/media/webrtc/tab_desktop_media_list_unittest.cc4
-rw-r--r--chromium/chrome/browser/media/webrtc/thumbnail_capturer.cc29
-rw-r--r--chromium/chrome/browser/media/webrtc/thumbnail_capturer.h114
-rw-r--r--chromium/chrome/browser/media/webrtc/thumbnail_capturer_mac.h17
-rw-r--r--chromium/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm876
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_browsertest_base.cc4
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_desktop_capture_browsertest.cc10
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc9
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common_unittest.cc11
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.cc5
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.h2
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc4
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc6
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_interactive_uitest.cc4
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_mediadevices_interactive_uitest.cc102
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc2
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc3
-rw-r--r--chromium/chrome/browser/media/webrtc/window_icon_util_mac.mm21
-rw-r--r--chromium/chrome/browser/metrics/variations/BUILD.gn4
-rw-r--r--chromium/chrome/browser/nearby_sharing/certificates/BUILD.gn1
-rw-r--r--chromium/chrome/browser/nearby_sharing/client/BUILD.gn1
-rw-r--r--chromium/chrome/browser/nearby_sharing/contacts/BUILD.gn1
-rw-r--r--chromium/chrome/browser/nearby_sharing/local_device_data/BUILD.gn1
-rw-r--r--chromium/chrome/browser/nearby_sharing/public/cpp/BUILD.gn2
-rw-r--r--chromium/chrome/browser/net/OWNERS3
-rw-r--r--chromium/chrome/browser/net/android_network_service_browsertest.cc220
-rw-r--r--chromium/chrome/browser/net/chrome_accept_encoding_header_browsertest.cc17
-rw-r--r--chromium/chrome/browser/net/chrome_network_delegate.cc6
-rw-r--r--chromium/chrome/browser/net/chrome_network_delegate_unittest.cc1
-rw-r--r--chromium/chrome/browser/net/chrome_network_service_browsertest.cc12
-rw-r--r--chromium/chrome/browser/net/chrome_shared_dictionary_browsertest.cc659
-rw-r--r--chromium/chrome/browser/net/cookie_policy_browsertest.cc294
-rw-r--r--chromium/chrome/browser/net/dns_probe_browsertest.cc7
-rw-r--r--chromium/chrome/browser/net/dns_probe_service_factory.cc17
-rw-r--r--chromium/chrome/browser/net/dns_probe_service_factory.h2
-rw-r--r--chromium/chrome/browser/net/fake_nss_service.cc2
-rw-r--r--chromium/chrome/browser/net/fake_nss_service.h2
-rw-r--r--chromium/chrome/browser/net/net_error_tab_helper_browsertest.cc4
-rw-r--r--chromium/chrome/browser/net/network_context_configuration_browsertest.cc11
-rw-r--r--chromium/chrome/browser/net/network_request_metrics_browsertest.cc27
-rw-r--r--chromium/chrome/browser/net/nss_service_factory.cc5
-rw-r--r--chromium/chrome/browser/net/nss_service_factory.h2
-rw-r--r--chromium/chrome/browser/net/nss_temp_certs_cache_chromeos_unittest.cc5
-rw-r--r--chromium/chrome/browser/net/private_network_access_browsertest.cc (renamed from chromium/chrome/browser/net/local_network_access_browsertest.cc)291
-rw-r--r--chromium/chrome/browser/net/profile_network_context_service.cc227
-rw-r--r--chromium/chrome/browser/net/profile_network_context_service.h8
-rw-r--r--chromium/chrome/browser/net/profile_network_context_service_browsertest.cc132
-rw-r--r--chromium/chrome/browser/net/profile_network_context_service_factory.cc6
-rw-r--r--chromium/chrome/browser/net/profile_network_context_service_factory.h2
-rw-r--r--chromium/chrome/browser/net/proxy_config_monitor.cc5
-rw-r--r--chromium/chrome/browser/net/sandboxed_network_change_notifier_win_browsertest.cc203
-rw-r--r--chromium/chrome/browser/net/storage_test_utils.cc41
-rw-r--r--chromium/chrome/browser/net/storage_test_utils.h24
-rw-r--r--chromium/chrome/browser/net/system_network_context_manager.cc186
-rw-r--r--chromium/chrome/browser/net/system_network_context_manager.h28
-rw-r--r--chromium/chrome/browser/net/system_network_context_manager_browsertest.cc468
-rw-r--r--chromium/chrome/browser/net/websocket_browsertest.cc51
-rw-r--r--chromium/chrome/browser/new_tab_page/modules/history_clusters/BUILD.gn6
-rw-r--r--chromium/chrome/browser/new_tab_page/modules/history_clusters/discount/BUILD.gn12
-rw-r--r--chromium/chrome/browser/new_tab_page/modules/history_clusters/discount/discount.mojom16
-rw-r--r--chromium/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom16
-rw-r--r--chromium/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_layout_type.mojom17
-rw-r--r--chromium/chrome/browser/new_tab_page/modules/v2/history_clusters/BUILD.gn19
-rw-r--r--chromium/chrome/browser/new_tab_page/modules/v2/history_clusters/history_clusters_v2.mojom41
-rw-r--r--chromium/chrome/browser/notifications/BUILD.gn2
-rw-r--r--chromium/chrome/browser/notifications/scheduler/public/BUILD.gn2
-rw-r--r--chromium/chrome/browser/optimization_guide/android/BUILD.gn3
-rw-r--r--chromium/chrome/browser/paint_preview/android/BUILD.gn2
-rw-r--r--chromium/chrome/browser/partnercustomizations/BUILD.gn12
-rw-r--r--chromium/chrome/browser/password_check/android/BUILD.gn2
-rw-r--r--chromium/chrome/browser/password_check/android/internal/BUILD.gn2
-rw-r--r--chromium/chrome/browser/password_edit_dialog/android/BUILD.gn2
-rw-r--r--chromium/chrome/browser/password_entry_edit/android/internal/BUILD.gn4
-rw-r--r--chromium/chrome/browser/password_manager/android/BUILD.gn17
-rw-r--r--chromium/chrome/browser/password_manager/android/pwd_migration/BUILD.gn12
-rw-r--r--chromium/chrome/browser/payments/android/BUILD.gn7
-rw-r--r--chromium/chrome/browser/persisted_state_db/BUILD.gn1
-rw-r--r--chromium/chrome/browser/plus_addresses/BUILD.gn26
-rw-r--r--chromium/chrome/browser/policy/BUILD.gn26
-rw-r--r--chromium/chrome/browser/policy/android/BUILD.gn1
-rw-r--r--chromium/chrome/browser/policy/messaging_layer/proto/BUILD.gn8
-rw-r--r--chromium/chrome/browser/preferences/BUILD.gn10
-rw-r--r--chromium/chrome/browser/prefetch/android/BUILD.gn2
-rw-r--r--chromium/chrome/browser/prefs/browser_prefs.cc449
-rw-r--r--chromium/chrome/browser/prefs/browser_prefs.h14
-rw-r--r--chromium/chrome/browser/prefs/chrome_pref_service_factory.cc12
-rw-r--r--chromium/chrome/browser/prefs/chrome_pref_service_unittest.cc10
-rw-r--r--chromium/chrome/browser/prefs/pref_metrics_service.cc5
-rw-r--r--chromium/chrome/browser/prefs/pref_metrics_service.h2
-rw-r--r--chromium/chrome/browser/prefs/pref_service_browsertest.cc10
-rw-r--r--chromium/chrome/browser/prefs/pref_service_incognito_allowlist.cc4
-rw-r--r--chromium/chrome/browser/printing/browser_printing_context_factory_for_test.cc15
-rw-r--r--chromium/chrome/browser/printing/browser_printing_context_factory_for_test.h15
-rw-r--r--chromium/chrome/browser/printing/pdf_to_emf_converter.cc36
-rw-r--r--chromium/chrome/browser/printing/pdf_to_emf_converter.h3
-rw-r--r--chromium/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc6
-rw-r--r--chromium/chrome/browser/printing/print_backend_browsertest.cc12
-rw-r--r--chromium/chrome/browser/printing/print_backend_service_manager.cc11
-rw-r--r--chromium/chrome/browser/printing/print_backend_service_manager.h4
-rw-r--r--chromium/chrome/browser/printing/print_backend_service_manager_unittest.cc8
-rw-r--r--chromium/chrome/browser/printing/print_browsertest.cc293
-rw-r--r--chromium/chrome/browser/printing/print_browsertest.h68
-rw-r--r--chromium/chrome/browser/printing/print_job.cc69
-rw-r--r--chromium/chrome/browser/printing/print_job.h46
-rw-r--r--chromium/chrome/browser/printing/print_job_utils_lacros.cc7
-rw-r--r--chromium/chrome/browser/printing/print_job_worker.cc8
-rw-r--r--chromium/chrome/browser/printing/print_job_worker.h17
-rw-r--r--chromium/chrome/browser/printing/print_job_worker_oop.cc36
-rw-r--r--chromium/chrome/browser/printing/print_job_worker_oop.h12
-rw-r--r--chromium/chrome/browser/printing/print_preview_context_menu_observer.h2
-rw-r--r--chromium/chrome/browser/printing/print_preview_dialog_controller.cc7
-rw-r--r--chromium/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc5
-rw-r--r--chromium/chrome/browser/printing/print_test_utils.cc31
-rw-r--r--chromium/chrome/browser/printing/print_view_manager.cc24
-rw-r--r--chromium/chrome/browser/printing/print_view_manager_base.cc194
-rw-r--r--chromium/chrome/browser/printing/print_view_manager_base.h65
-rw-r--r--chromium/chrome/browser/printing/print_view_manager_unittest.cc99
-rw-r--r--chromium/chrome/browser/printing/printer_query.cc4
-rw-r--r--chromium/chrome/browser/printing/printer_query_oop.cc9
-rw-r--r--chromium/chrome/browser/printing/system_access_process_print_browsertest.cc1843
-rw-r--r--chromium/chrome/browser/printing/test_print_job.cc91
-rw-r--r--chromium/chrome/browser/printing/test_print_job.h88
-rw-r--r--chromium/chrome/browser/printing/test_print_preview_observer.cc4
-rw-r--r--chromium/chrome/browser/printing/test_print_preview_observer.h7
-rw-r--r--chromium/chrome/browser/printing/test_print_view_manager.cc54
-rw-r--r--chromium/chrome/browser/printing/test_print_view_manager.h17
-rw-r--r--chromium/chrome/browser/privacy/BUILD.gn1
-rw-r--r--chromium/chrome/browser/privacy_guide/android/BUILD.gn20
-rw-r--r--chromium/chrome/browser/privacy_sandbox/android/BUILD.gn4
-rw-r--r--chromium/chrome/browser/promos/BUILD.gn1
-rw-r--r--chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc928
-rw-r--r--chromium/chrome/browser/push_messaging/push_messaging_service_factory.cc14
-rw-r--r--chromium/chrome/browser/push_messaging/push_messaging_service_factory.h2
-rw-r--r--chromium/chrome/browser/push_messaging/push_messaging_service_impl.cc32
-rw-r--r--chromium/chrome/browser/push_messaging/push_messaging_service_unittest.cc102
-rw-r--r--chromium/chrome/browser/quick_delete/BUILD.gn38
-rw-r--r--chromium/chrome/browser/readaloud/android/BUILD.gn151
-rw-r--r--chromium/chrome/browser/recent_tabs/BUILD.gn2
-rw-r--r--chromium/chrome/browser/recent_tabs/internal/BUILD.gn15
-rw-r--r--chromium/chrome/browser/renderer_host/BUILD.gn2
-rw-r--r--chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.mm4
-rw-r--r--chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm4
-rw-r--r--chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm11
-rw-r--r--chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm4
-rw-r--r--chromium/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc90
-rw-r--r--chromium/chrome/browser/resources/BUILD.gn17
-rw-r--r--chromium/chrome/browser/resources/access_code_cast/index.html2
-rw-r--r--chromium/chrome/browser/resources/accessibility/BUILD.gn6
-rw-r--r--chromium/chrome/browser/resources/accessibility/chromevox_helper/BUILD.gn15
-rw-r--r--chromium/chrome/browser/resources/accessibility/chromevox_helper/service_worker.ts56
-rw-r--r--chromium/chrome/browser/resources/accessibility/chromevox_helper_manifest.json.jinja28
-rw-r--r--chromium/chrome/browser/resources/accessibility/embedded_a11y_helper/BUILD.gn21
-rw-r--r--chromium/chrome/browser/resources/accessibility/embedded_a11y_helper/service_worker.ts58
-rw-r--r--chromium/chrome/browser/resources/accessibility/embedded_a11y_helper_manifest.json.jinja218
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings.gni33
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings.grd157
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_af.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_am.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ar.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_as.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_az.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_be.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_bg.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_bn.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_bs.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ca.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_cs.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_cy.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_da.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_de.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_el.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_en-GB.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_es-419.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_es.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_et.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_eu.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_fa.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_fi.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_fil.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_fr-CA.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_fr.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_gl.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_grd/IDS_SELECT_TO_SPEAK_LISTEN_CONTEXT_MENU_OPTION_TEXT.png.sha11
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_gu.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_hi.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_hr.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_hu.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_hy.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_id.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_is.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_it.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_iw.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ja.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ka.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_kk.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_km.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_kn.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ko.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ky.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_lo.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_lt.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_lv.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_mk.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ml.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_mn.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_mr.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ms.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_my.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ne.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_nl.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_no.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_or.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_pa.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_pl.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_pt-BR.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_pt-PT.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ro.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ru.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_si.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_sk.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_sl.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_sq.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_sr-Latn.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_sr.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_sv.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_sw.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ta.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_te.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_th.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_tr.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_uk.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_ur.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_uz.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_vi.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-CN.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-HK.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-TW.xtb5
-rw-r--r--chromium/chrome/browser/resources/accessibility/strings/extension_strings_zu.xtb5
-rw-r--r--chromium/chrome/browser/resources/app_service_internals/app_service_internals.ts8
-rw-r--r--chromium/chrome/browser/resources/app_settings/app.html6
-rw-r--r--chromium/chrome/browser/resources/app_settings/app.ts20
-rw-r--r--chromium/chrome/browser/resources/app_settings/web_app_settings.ts2
-rw-r--r--chromium/chrome/browser/resources/ash/settings/BUILD.gn (renamed from chromium/chrome/browser/resources/settings/chromeos/BUILD.gn)50
-rw-r--r--chromium/chrome/browser/resources/ash/settings/os_settings.gni (renamed from chromium/chrome/browser/resources/settings/chromeos/os_settings.gni)0
-rw-r--r--chromium/chrome/browser/resources/bluetooth_internals/main.js10
-rw-r--r--chromium/chrome/browser/resources/bookmarks/COMMON_METADATA3
-rw-r--r--chromium/chrome/browser/resources/bookmarks/DIR_METADATA4
-rw-r--r--chromium/chrome/browser/resources/bookmarks/store_client_mixin.ts123
-rw-r--r--chromium/chrome/browser/resources/browsing_topics/browsing_topics_internals.html3
-rw-r--r--chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.css5
-rw-r--r--chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.html6
-rw-r--r--chromium/chrome/browser/resources/chromeos/BUILD.gn5
-rw-r--r--chromium/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn6
-rw-r--r--chromium/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn18
-rw-r--r--chromium/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn15
-rw-r--r--chromium/chrome/browser/resources/chromeos/accessibility/strings/select_to_speak_strings.grdp100
-rw-r--r--chromium/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn6
-rw-r--r--chromium/chrome/browser/resources/chromeos/add_supervision/BUILD.gn61
-rw-r--r--chromium/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn64
-rw-r--r--chromium/chrome/browser/resources/chromeos/borealis_installer/BUILD.gn25
-rw-r--r--chromium/chrome/browser/resources/chromeos/cloud_upload/BUILD.gn13
-rw-r--r--chromium/chrome/browser/resources/chromeos/contact_center_insights/BUILD.gn2
-rw-r--r--chromium/chrome/browser/resources/chromeos/desk_api/BUILD.gn3
-rw-r--r--chromium/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn8
-rw-r--r--chromium/chrome/browser/resources/chromeos/enterprise_reporting/BUILD.gn2
-rw-r--r--chromium/chrome/browser/resources/chromeos/login/BUILD.gn28
-rw-r--r--chromium/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn50
-rw-r--r--chromium/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn19
-rw-r--r--chromium/chrome/browser/resources/chromeos/login/test_api/BUILD.gn2
-rw-r--r--chromium/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn9
-rw-r--r--chromium/chrome/browser/resources/chromeos/parent_access/BUILD.gn40
-rw-r--r--chromium/chrome/browser/resources/chromeos/sensor_info/BUILD.gn25
-rw-r--r--chromium/chrome/browser/resources/chromeos/status_area_internals/BUILD.gn23
-rw-r--r--chromium/chrome/browser/resources/component_extension_resources.grd6
-rw-r--r--chromium/chrome/browser/resources/device_log_ui/device_log_ui.html4
-rw-r--r--chromium/chrome/browser/resources/downloads/COMMON_METADATA3
-rw-r--r--chromium/chrome/browser/resources/downloads/DIR_METADATA4
-rw-r--r--chromium/chrome/browser/resources/downloads/constants.ts21
-rw-r--r--chromium/chrome/browser/resources/downloads/icons.html6
-rw-r--r--chromium/chrome/browser/resources/downloads/item.html54
-rw-r--r--chromium/chrome/browser/resources/downloads/item.ts165
-rw-r--r--chromium/chrome/browser/resources/downloads/manager.html2
-rw-r--r--chromium/chrome/browser/resources/engagement/BUILD.gn4
-rw-r--r--chromium/chrome/browser/resources/engagement/COMMON_METADATA3
-rw-r--r--chromium/chrome/browser/resources/engagement/DIR_METADATA4
-rw-r--r--chromium/chrome/browser/resources/engagement/app.html107
-rw-r--r--chromium/chrome/browser/resources/engagement/app.ts297
-rw-r--r--chromium/chrome/browser/resources/engagement/site_engagement.html108
-rw-r--r--chromium/chrome/browser/resources/engagement/site_engagement.ts281
-rw-r--r--chromium/chrome/browser/resources/extensions/COMMON_METADATA4
-rw-r--r--chromium/chrome/browser/resources/extensions/DIR_METADATA5
-rw-r--r--chromium/chrome/browser/resources/extensions/detail_view.html73
-rw-r--r--chromium/chrome/browser/resources/extensions/detail_view.ts76
-rw-r--r--chromium/chrome/browser/resources/extensions/drag_and_drop_handler.ts3
-rw-r--r--chromium/chrome/browser/resources/extensions/icons.html6
-rw-r--r--chromium/chrome/browser/resources/extensions/item.html11
-rw-r--r--chromium/chrome/browser/resources/extensions/item.ts38
-rw-r--r--chromium/chrome/browser/resources/extensions/item_list.html22
-rw-r--r--chromium/chrome/browser/resources/extensions/item_list.ts39
-rw-r--r--chromium/chrome/browser/resources/extensions/item_util.ts39
-rw-r--r--chromium/chrome/browser/resources/extensions/load_error.html6
-rw-r--r--chromium/chrome/browser/resources/extensions/load_error.ts32
-rw-r--r--chromium/chrome/browser/resources/extensions/manager.html1
-rw-r--r--chromium/chrome/browser/resources/extensions/manager.ts44
-rw-r--r--chromium/chrome/browser/resources/extensions/review_panel.html60
-rw-r--r--chromium/chrome/browser/resources/extensions/review_panel.ts149
-rw-r--r--chromium/chrome/browser/resources/extensions/service.ts33
-rw-r--r--chromium/chrome/browser/resources/extensions/shared_style.css12
-rw-r--r--chromium/chrome/browser/resources/extensions/shared_vars.css2
-rw-r--r--chromium/chrome/browser/resources/extensions/sidebar.html61
-rw-r--r--chromium/chrome/browser/resources/extensions/sidebar.ts79
-rw-r--r--chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.html3
-rw-r--r--chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.ts11
-rw-r--r--chromium/chrome/browser/resources/extensions/toolbar.ts8
-rw-r--r--chromium/chrome/browser/resources/feedback/BUILD.gn3
-rw-r--r--chromium/chrome/browser/resources/feedback/css/feedback.css79
-rw-r--r--chromium/chrome/browser/resources/feedback/css/logs_info.css (renamed from chromium/chrome/browser/resources/feedback/css/assistant_logs_info.css)10
-rw-r--r--chromium/chrome/browser/resources/feedback/html/assistant_logs_info.html13
-rw-r--r--chromium/chrome/browser/resources/feedback/html/autofill_metadata_info.html7
-rw-r--r--chromium/chrome/browser/resources/feedback/html/bluetooth_logs_info.html10
-rw-r--r--chromium/chrome/browser/resources/feedback/html/default.html23
-rw-r--r--chromium/chrome/browser/resources/feedback/js/autofill_metadata.ts2
-rw-r--r--chromium/chrome/browser/resources/feedback/js/feedback.ts10
-rw-r--r--chromium/chrome/browser/resources/feedback/js/jelly_colors.ts25
-rw-r--r--chromium/chrome/browser/resources/feedback/js/questionnaire.ts9
-rw-r--r--chromium/chrome/browser/resources/gaia_auth_host/BUILD.gn1
-rw-r--r--chromium/chrome/browser/resources/gaia_auth_host/authenticator.js34
-rw-r--r--chromium/chrome/browser/resources/hangout_services/thunk.js54
-rw-r--r--chromium/chrome/browser/resources/hats/BUILD.gn17
-rw-r--r--chromium/chrome/browser/resources/hats/DIR_METADATA3
-rw-r--r--chromium/chrome/browser/resources/hats/OWNERS1
-rw-r--r--chromium/chrome/browser/resources/hats/hats.html19
-rw-r--r--chromium/chrome/browser/resources/hats/hats.ts7
-rw-r--r--chromium/chrome/browser/resources/history/app.ts2
-rw-r--r--chromium/chrome/browser/resources/history/history_item.html2
-rw-r--r--chromium/chrome/browser/resources/history/history_item.ts15
-rw-r--r--chromium/chrome/browser/resources/history/router.ts2
-rw-r--r--chromium/chrome/browser/resources/identity_scope_approval_dialog/OWNERS1
-rw-r--r--chromium/chrome/browser/resources/identity_scope_approval_dialog/background.js30
-rw-r--r--chromium/chrome/browser/resources/identity_scope_approval_dialog/inject.js18
-rw-r--r--chromium/chrome/browser/resources/identity_scope_approval_dialog/manifest.json29
-rw-r--r--chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.css49
-rw-r--r--chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.html13
-rw-r--r--chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.js74
-rw-r--r--chromium/chrome/browser/resources/inline_login/welcome_page_app.ts39
-rw-r--r--chromium/chrome/browser/resources/inspect/inspect.css2
-rw-r--r--chromium/chrome/browser/resources/inspect/inspect.html4
-rw-r--r--chromium/chrome/browser/resources/inspect/inspect.js9
-rw-r--r--chromium/chrome/browser/resources/intro/BUILD.gn13
-rw-r--r--chromium/chrome/browser/resources/intro/default_browser/OWNERS1
-rw-r--r--chromium/chrome/browser/resources/intro/default_browser/app.html154
-rw-r--r--chromium/chrome/browser/resources/intro/default_browser/app.ts73
-rw-r--r--chromium/chrome/browser/resources/intro/default_browser/browser_proxy.ts38
-rw-r--r--chromium/chrome/browser/resources/intro/default_browser/default_browser.html (renamed from chromium/chrome/browser/resources/waffle/waffle.html)10
-rw-r--r--chromium/chrome/browser/resources/intro/images/default_browser_frame.svg1
-rw-r--r--chromium/chrome/browser/resources/intro/images/default_browser_frame_dark.svg1
-rw-r--r--chromium/chrome/browser/resources/invalidations/about_invalidations.js8
-rw-r--r--chromium/chrome/browser/resources/location_internals/BUILD.gn21
-rw-r--r--chromium/chrome/browser/resources/location_internals/diagnose_info_table.html12
-rw-r--r--chromium/chrome/browser/resources/location_internals/diagnose_info_table.ts61
-rw-r--r--chromium/chrome/browser/resources/location_internals/diagnose_info_view.html19
-rw-r--r--chromium/chrome/browser/resources/location_internals/diagnose_info_view.ts297
-rw-r--r--chromium/chrome/browser/resources/location_internals/location_internals.css13
-rw-r--r--chromium/chrome/browser/resources/location_internals/location_internals.html4
-rw-r--r--chromium/chrome/browser/resources/location_internals/location_internals.ts134
-rw-r--r--chromium/chrome/browser/resources/management/icons.html1
-rw-r--r--chromium/chrome/browser/resources/management/management_browser_proxy.ts18
-rw-r--r--chromium/chrome/browser/resources/management/management_ui.html44
-rw-r--r--chromium/chrome/browser/resources/management/management_ui.ts52
-rw-r--r--chromium/chrome/browser/resources/media_router/internals/media_router_internals.html8
-rw-r--r--chromium/chrome/browser/resources/media_router/internals/media_router_internals.ts14
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/BUILD.gn25
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/OWNERS2
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/cross_device_internals.html152
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/cross_device_internals.js359
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/cross_device_logs_browser_proxy.js (renamed from chromium/chrome/browser/resources/nearby_internals/nearby_logs_browser_proxy.js)1
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/index.html14
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/log_types.html28
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/log_types.js81
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/logging_tab.html90
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/logging_tab.js128
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/nearby_presence_browser_proxy.js12
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/nearby_ui_trigger_browser_proxy.js7
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/np_list_object.html41
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/np_list_object.js15
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/shared_style.css165
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/types.js36
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.html6
-rw-r--r--chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.js9
-rw-r--r--chromium/chrome/browser/resources/nearby_share/BUILD.gn2
-rw-r--r--chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.html28
-rw-r--r--chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.ts26
-rw-r--r--chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.html182
-rw-r--r--chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.ts125
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/BUILD.gn2
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.html61
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.ts245
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/nearby_device.html113
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/nearby_page_template.html28
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/nearby_preview.html15
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/nearby_progress.html17
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/nearby_share_progress_bar_jelly.json1
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/nearby_share_pulse_animation_jelly.json1
-rw-r--r--chromium/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html8
-rw-r--r--chromium/chrome/browser/resources/net_internals/BUILD.gn1
-rw-r--r--chromium/chrome/browser/resources/net_internals/browser_bridge.js19
-rw-r--r--chromium/chrome/browser/resources/net_internals/index.html90
-rw-r--r--chromium/chrome/browser/resources/net_internals/main.css13
-rw-r--r--chromium/chrome/browser/resources/net_internals/main.js2
-rw-r--r--chromium/chrome/browser/resources/net_internals/shared_dictionary_view.js90
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/BUILD.gn7
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/COMMON_METADATA3
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/DIR_METADATA4
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/app.html114
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/app.ts70
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/background_manager.ts3
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/image_processor.ts203
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/lazy_load.ts6
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/lens_form.ts45
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.html1
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.ts9
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/cart/discount_consent_card.html4
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/cart/module.html11
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/drive/module.html3
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html37
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts40
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.html49
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts17
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts17
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/module_header.html2
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/module_header.ts2
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.html6
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts17
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/modules.gni4
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/modules.html1
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/photos/module.html5
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/recipes/module.html1
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.html144
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.ts58
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.html10
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.ts72
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/cart/cart_tile.html262
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/cart/cart_tile.ts147
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.html154
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.ts109
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/history_clusters.gni7
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/history_clusters_proxy.ts32
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.html166
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.ts212
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module_header.html97
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module_header.ts74
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/suggest_tile.html49
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.html157
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.ts25
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/icons.html36
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/module_header.html79
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/module_header.ts87
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.html29
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.ts403
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/new_tab_page.gni5
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/new_tab_page.html3
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/new_tab_page.ts2
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/realbox/realbox.html2
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/realbox/realbox.ts7
-rw-r--r--chromium/chrome/browser/resources/new_tab_page/transparency.ts122
-rw-r--r--chromium/chrome/browser/resources/ntp4/BUILD.gn57
-rw-r--r--chromium/chrome/browser/resources/ntp4/app_info.js45
-rw-r--r--chromium/chrome/browser/resources/ntp4/apps_page.css114
-rw-r--r--chromium/chrome/browser/resources/ntp4/apps_page.js906
-rw-r--r--chromium/chrome/browser/resources/ntp4/card_slider.js691
-rw-r--r--chromium/chrome/browser/resources/ntp4/command.js306
-rw-r--r--chromium/chrome/browser/resources/ntp4/context_menu_handler.js323
-rw-r--r--chromium/chrome/browser/resources/ntp4/cr_deprecated.js203
-rw-r--r--chromium/chrome/browser/resources/ntp4/dot_list.js78
-rw-r--r--chromium/chrome/browser/resources/ntp4/images/error_yellow900.svg1
-rw-r--r--chromium/chrome/browser/resources/ntp4/images/trash.pngbin150 -> 0 bytes
-rw-r--r--chromium/chrome/browser/resources/ntp4/menu.js407
-rw-r--r--chromium/chrome/browser/resources/ntp4/menu_button.js352
-rw-r--r--chromium/chrome/browser/resources/ntp4/menu_item.js309
-rw-r--r--chromium/chrome/browser/resources/ntp4/nav_dot.css95
-rw-r--r--chromium/chrome/browser/resources/ntp4/nav_dot.js261
-rw-r--r--chromium/chrome/browser/resources/ntp4/new_tab.css275
-rw-r--r--chromium/chrome/browser/resources/ntp4/new_tab.html109
-rw-r--r--chromium/chrome/browser/resources/ntp4/new_tab.js372
-rw-r--r--chromium/chrome/browser/resources/ntp4/page_list_view.js736
-rw-r--r--chromium/chrome/browser/resources/ntp4/page_switcher.js159
-rw-r--r--chromium/chrome/browser/resources/ntp4/position_util.js236
-rw-r--r--chromium/chrome/browser/resources/ntp4/tile_page.css189
-rw-r--r--chromium/chrome/browser/resources/ntp4/tile_page.js1359
-rw-r--r--chromium/chrome/browser/resources/ntp4/touch_handler.js878
-rw-r--r--chromium/chrome/browser/resources/ntp4/trash.css59
-rw-r--r--chromium/chrome/browser/resources/ntp4/trash.js71
-rw-r--r--chromium/chrome/browser/resources/ntp4/ui.js216
-rw-r--r--chromium/chrome/browser/resources/ntp4/util.js18
-rw-r--r--chromium/chrome/browser/resources/omnibox/omnibox_output.ts7
-rw-r--r--chromium/chrome/browser/resources/omnibox_popup/app.ts18
-rw-r--r--chromium/chrome/browser/resources/omnibox_popup/omnibox_popup.html5
-rw-r--r--chromium/chrome/browser/resources/password_manager/BUILD.gn11
-rw-r--r--chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.html7
-rw-r--r--chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.ts18
-rw-r--r--chromium/chrome/browser/resources/password_manager/dialogs/add_password_dialog.ts5
-rw-r--r--chromium/chrome/browser/resources/password_manager/images/password_sharing_family_banner.svg1
-rw-r--r--chromium/chrome/browser/resources/password_manager/images/password_sharing_progress_bar.svg1
-rw-r--r--chromium/chrome/browser/resources/password_manager/images/password_sharing_secure_shield.svg1
-rw-r--r--chromium/chrome/browser/resources/password_manager/passkeys_browser_proxy.ts10
-rw-r--r--chromium/chrome/browser/resources/password_manager/password_details_section.html12
-rw-r--r--chromium/chrome/browser/resources/password_manager/password_details_section.ts4
-rw-r--r--chromium/chrome/browser/resources/password_manager/password_manager.html5
-rw-r--r--chromium/chrome/browser/resources/password_manager/password_manager.ts4
-rw-r--r--chromium/chrome/browser/resources/password_manager/password_manager_app.html3
-rw-r--r--chromium/chrome/browser/resources/password_manager/password_manager_app.ts8
-rw-r--r--chromium/chrome/browser/resources/password_manager/password_manager_proxy.ts32
-rw-r--r--chromium/chrome/browser/resources/password_manager/passwords_section.html3
-rw-r--r--chromium/chrome/browser/resources/password_manager/passwords_section.ts22
-rw-r--r--chromium/chrome/browser/resources/password_manager/settings_section.html11
-rw-r--r--chromium/chrome/browser/resources/password_manager/settings_section.ts17
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_confirmation_dialog.html240
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_confirmation_dialog.ts173
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_dialog_header.html22
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_dialog_header.ts43
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_error_dialog.html18
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_error_dialog.ts52
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_family_picker_dialog.html71
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_family_picker_dialog.ts110
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_flow.html38
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_flow.ts126
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_loading_dialog.html14
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_loading_dialog.ts36
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_no_members_dialog.html33
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_no_members_dialog.ts52
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_recipient.html135
-rw-r--r--chromium/chrome/browser/resources/password_manager/sharing/share_password_recipient.ts96
-rw-r--r--chromium/chrome/browser/resources/password_manager/site_favicon.ts2
-rw-r--r--chromium/chrome/browser/resources/password_manager/toolbar.html2
-rw-r--r--chromium/chrome/browser/resources/pdf/BUILD.gn2
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/icons.html1
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-attachment-bar.html20
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-attachment-bar.ts50
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-attachment.html45
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-attachment.ts81
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.html42
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.ts147
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.html3
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.ts30
-rw-r--r--chromium/chrome/browser/resources/pdf/index.html3
-rw-r--r--chromium/chrome/browser/resources/pdf/metrics.ts6
-rw-r--r--chromium/chrome/browser/resources/pdf/pdf_viewer.html10
-rw-r--r--chromium/chrome/browser/resources/pdf/pdf_viewer.ts3
-rw-r--r--chromium/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts4
-rw-r--r--chromium/chrome/browser/resources/print_preview/BUILD.gn2
-rw-r--r--chromium/chrome/browser/resources/print_preview/COMMON_METADATA3
-rw-r--r--chromium/chrome/browser/resources/print_preview/DIR_METADATA4
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/cdd.ts14
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/destination_store.ts10
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/document_info.ts22
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/margins.ts25
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/model.ts99
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/printer_status_cros.ts75
-rw-r--r--chromium/chrome/browser/resources/print_preview/metrics.ts31
-rw-r--r--chromium/chrome/browser/resources/print_preview/native_layer.ts6
-rw-r--r--chromium/chrome/browser/resources/print_preview/native_layer_cros.ts10
-rw-r--r--chromium/chrome/browser/resources/print_preview/print_preview.html3
-rw-r--r--chromium/chrome/browser/resources/print_preview/print_preview.ts6
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/app.ts2
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.html14
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.ts83
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.html12
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.ts14
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.html12
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.ts18
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/destination_settings.ts10
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/icons.html25
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/media_size_settings.html13
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/media_size_settings.ts49
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/media_type_settings.html14
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/media_type_settings.ts78
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/preview_area.html30
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/preview_area.ts59
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/print_preview_vars.css14
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/printer_setup_info_cros.html77
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/printer_setup_info_cros.ts148
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/sidebar.html5
-rw-r--r--chromium/chrome/browser/resources/print_preview/ui/sidebar.ts12
-rw-r--r--chromium/chrome/browser/resources/privacy_sandbox/DIR_METADATA2
-rw-r--r--chromium/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_mixin.ts7
-rw-r--r--chromium/chrome/browser/resources/safe_browsing/DIR_METADATA3
-rw-r--r--chromium/chrome/browser/resources/safe_browsing/OWNERS1
-rw-r--r--chromium/chrome/browser/resources/safe_browsing/README.md228
-rw-r--r--chromium/chrome/browser/resources/search_engine_choice/BUILD.gn (renamed from chromium/chrome/browser/resources/waffle/BUILD.gn)14
-rw-r--r--chromium/chrome/browser/resources/search_engine_choice/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/resources/search_engine_choice/OWNERS1
-rw-r--r--chromium/chrome/browser/resources/search_engine_choice/app.html270
-rw-r--r--chromium/chrome/browser/resources/search_engine_choice/app.ts109
-rw-r--r--chromium/chrome/browser/resources/search_engine_choice/browser_proxy.ts42
-rw-r--r--chromium/chrome/browser/resources/search_engine_choice/search_engine_choice.html28
-rw-r--r--chromium/chrome/browser/resources/settings/BUILD.gn55
-rw-r--r--chromium/chrome/browser/resources/settings_shared/controls/settings_slider.html3
-rw-r--r--chromium/chrome/browser/resources/settings_shared/controls/settings_slider.ts5
-rw-r--r--chromium/chrome/browser/resources/settings_shared/people_page/sync_browser_proxy.ts20
-rw-r--r--chromium/chrome/browser/resources/settings_shared/privacy_page/secure_dns.html6
-rw-r--r--chromium/chrome/browser/resources/side_panel/bookmarks/bookmarks_list.ts8
-rw-r--r--chromium/chrome/browser/resources/side_panel/bookmarks/icons.html26
-rw-r--r--chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html3
-rw-r--r--chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.ts14
-rw-r--r--chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts11
-rw-r--r--chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_drag_manager.ts71
-rw-r--r--chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html349
-rw-r--r--chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts518
-rw-r--r--chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_service.ts75
-rw-r--r--chromium/chrome/browser/resources/side_panel/commerce/app.html11
-rw-r--r--chromium/chrome/browser/resources/side_panel/commerce/app.ts41
-rw-r--r--chromium/chrome/browser/resources/side_panel/commerce/history_graph.html2
-rw-r--r--chromium/chrome/browser/resources/side_panel/commerce/history_graph.ts61
-rw-r--r--chromium/chrome/browser/resources/side_panel/commerce/price_tracking_section.html3
-rw-r--r--chromium/chrome/browser/resources/side_panel/companion/companion.ts29
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/BUILD.gn6
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/app.html28
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/app.ts9
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.html71
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.ts92
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/categories.html91
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/categories.ts25
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.html5
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.ts6
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.html22
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.ts43
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/color.html87
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/color.ts86
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/color_utils.ts47
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/colors.html147
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/colors.ts268
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/hover_button.html15
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/icons/BUILD.gn1
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_corner_new_tab_page.svg1
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_mini_new_tab_page.svg2
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/shortcuts.html1
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.html25
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.ts8
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/themes.html3
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/themes.ts21
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search.html12
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search.ts37
-rw-r--r--chromium/chrome/browser/resources/side_panel/customize_chrome/window_proxy.ts28
-rw-r--r--chromium/chrome/browser/resources/side_panel/history_clusters/BUILD.gn8
-rw-r--r--chromium/chrome/browser/resources/side_panel/history_clusters/app.html30
-rw-r--r--chromium/chrome/browser/resources/side_panel/history_clusters/app.ts7
-rw-r--r--chromium/chrome/browser/resources/side_panel/history_clusters/history_clusters.html5
-rw-r--r--chromium/chrome/browser/resources/side_panel/read_anything/BUILD.gn8
-rw-r--r--chromium/chrome/browser/resources/side_panel/read_anything/app.html27
-rw-r--r--chromium/chrome/browser/resources/side_panel/read_anything/app.ts257
-rw-r--r--chromium/chrome/browser/resources/side_panel/read_anything/icons.html92
-rw-r--r--chromium/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts67
-rw-r--r--chromium/chrome/browser/resources/side_panel/read_anything/read_anything.html8
-rw-r--r--chromium/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html183
-rw-r--r--chromium/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts377
-rw-r--r--chromium/chrome/browser/resources/side_panel/reading_list/app.html5
-rw-r--r--chromium/chrome/browser/resources/side_panel/reading_list/reading_list_item.html2
-rw-r--r--chromium/chrome/browser/resources/side_panel/shared/sp_shared_style.css26
-rw-r--r--chromium/chrome/browser/resources/signin/BUILD.gn16
-rw-r--r--chromium/chrome/browser/resources/signin/dice_web_signin_intercept/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/resources/signin/dice_web_signin_intercept/OWNERS2
-rw-r--r--chromium/chrome/browser/resources/signin/enterprise_profile_welcome/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/resources/signin/enterprise_profile_welcome/enterprise_profile_welcome_app.html8
-rw-r--r--chromium/chrome/browser/resources/signin/profile_customization/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/resources/signin/profile_customization/OWNERS2
-rw-r--r--chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.html24
-rw-r--r--chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.ts48
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/BUILD.gn2
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/drag_drop_reorder_tile_list_delegate.ts491
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/ensure_lazy_loaded.ts3
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/lazy_load.ts1
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.ts28
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/profile_card.html4
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/profile_card.ts10
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.html260
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.ts345
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.html9
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.ts14
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html4
-rw-r--r--chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.ts67
-rw-r--r--chromium/chrome/browser/resources/signin/sync_confirmation/OWNERS1
-rw-r--r--chromium/chrome/browser/resources/suggest_internals/app.ts32
-rw-r--r--chromium/chrome/browser/resources/suggest_internals/request.html9
-rw-r--r--chromium/chrome/browser/resources/suggest_internals/request.ts30
-rw-r--r--chromium/chrome/browser/resources/support_tool/data_collectors.html2
-rw-r--r--chromium/chrome/browser/resources/support_tool/data_export_done.html4
-rw-r--r--chromium/chrome/browser/resources/support_tool/issue_details.html14
-rw-r--r--chromium/chrome/browser/resources/support_tool/issue_details.ts12
-rw-r--r--chromium/chrome/browser/resources/support_tool/pii_selection.html25
-rw-r--r--chromium/chrome/browser/resources/support_tool/spinner_page.html4
-rw-r--r--chromium/chrome/browser/resources/support_tool/support_tool.html10
-rw-r--r--chromium/chrome/browser/resources/support_tool/support_tool_container.html2
-rw-r--r--chromium/chrome/browser/resources/support_tool/support_tool_page_mixin.ts9
-rw-r--r--chromium/chrome/browser/resources/support_tool/url_generator.html18
-rw-r--r--chromium/chrome/browser/resources/support_tool/url_generator.ts14
-rw-r--r--chromium/chrome/browser/resources/tab_search/BUILD.gn1
-rw-r--r--chromium/chrome/browser/resources/tab_search/app.html101
-rw-r--r--chromium/chrome/browser/resources/tab_search/app.ts83
-rw-r--r--chromium/chrome/browser/resources/tab_search/infinite_list.html9
-rw-r--r--chromium/chrome/browser/resources/tab_search/infinite_list.ts16
-rw-r--r--chromium/chrome/browser/resources/tab_search/tab_search.ts1
-rw-r--r--chromium/chrome/browser/resources/tab_search/tab_search_search_field.html84
-rw-r--r--chromium/chrome/browser/resources/tab_search/tab_search_search_field.ts114
-rw-r--r--chromium/chrome/browser/resources/waffle/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/resources/waffle/OWNERS1
-rw-r--r--chromium/chrome/browser/resources/waffle/app.html141
-rw-r--r--chromium/chrome/browser/resources/waffle/app.ts60
-rw-r--r--chromium/chrome/browser/resources/waffle/browser_proxy.ts36
-rw-r--r--chromium/chrome/browser/resources/web_app_internals/web_app_internals.html11
-rw-r--r--chromium/chrome/browser/resources/web_app_internals/web_app_internals.ts77
-rw-r--r--chromium/chrome/browser/resources/webapks/about_webapks.ts5
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/BUILD.gn1
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/app.ts5
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/demos/cr_chip/cr_chip_demo.html30
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/demos/cr_chip/cr_chip_demo.ts26
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/demos/cr_icons/cr_icons_demo.ts2
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/demos/cr_input/cr_input_demo.html10
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/demos/cr_radio/cr_radio_demo.html5
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/demos/cr_url_list_item/cr_url_list_item_demo.html6
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/demos/md_select/md_select_demo.ts2
-rw-r--r--chromium/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.html70
-rw-r--r--chromium/chrome/browser/safe_browsing/BUILD.gn13
-rw-r--r--chromium/chrome/browser/safe_browsing/android/BUILD.gn11
-rw-r--r--chromium/chrome/browser/safety_check/android/BUILD.gn4
-rw-r--r--chromium/chrome/browser/screen_ai/BUILD.gn8
-rw-r--r--chromium/chrome/browser/screenshot_monitor/BUILD.gn26
-rw-r--r--chromium/chrome/browser/search_engines/android/BUILD.gn21
-rw-r--r--chromium/chrome/browser/segmentation_platform/BUILD.gn1
-rw-r--r--chromium/chrome/browser/selection/android/BUILD.gn1
-rw-r--r--chromium/chrome/browser/settings/BUILD.gn4
-rw-r--r--chromium/chrome/browser/share/BUILD.gn2
-rw-r--r--chromium/chrome/browser/share/android/BUILD.gn22
-rw-r--r--chromium/chrome/browser/share/android/java_sources.gni7
-rw-r--r--chromium/chrome/browser/share/android/test_java_sources.gni1
-rw-r--r--chromium/chrome/browser/signin/account_consistency_mode_manager.cc5
-rw-r--r--chromium/chrome/browser/signin/account_consistency_mode_manager_factory.cc5
-rw-r--r--chromium/chrome/browser/signin/account_consistency_mode_manager_factory.h2
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.cc33
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h35
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc178
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h89
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl_unittest.cc577
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.cc21
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h15
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer_unittest.cc61
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h29
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.cc47
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory_unittest.cc184
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.cc367
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.h101
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl_unittest.cc488
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_params.proto (renamed from chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_params.proto)9
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage.cc156
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage.h67
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage_unittest.cc176
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util.cc23
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util.h19
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util_unittest.cc18
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.cc26
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h11
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc232
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h72
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc519
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher.h30
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.cc198
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.h73
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl_unittest.cc224
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.cc137
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h58
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param_unittest.cc220
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_request_throttled_listener_browser_impl_unittest.cc24
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.cc32
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h19
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.cc47
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.h19
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher_unittest.cc77
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.cc50
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.h35
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper_unittest.cc59
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper.cc94
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper.h69
-rw-r--r--chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper_unittest.cc108
-rw-r--r--chromium/chrome/browser/signin/chrome_device_id_helper.cc5
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_client.cc98
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_client.h33
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_client_unittest.cc118
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_helper.cc77
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_helper.h1
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_helper_unittest.cc4
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc9
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.cc5
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.h4
-rw-r--r--chromium/chrome/browser/signin/dice_browsertest.cc126
-rw-r--r--chromium/chrome/browser/signin/dice_response_handler.cc25
-rw-r--r--chromium/chrome/browser/signin/dice_response_handler_unittest.cc25
-rw-r--r--chromium/chrome/browser/signin/dice_signed_in_profile_creator.cc25
-rw-r--r--chromium/chrome/browser/signin/dice_signed_in_profile_creator.h11
-rw-r--r--chromium/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc146
-rw-r--r--chromium/chrome/browser/signin/dice_tab_helper.cc81
-rw-r--r--chromium/chrome/browser/signin/dice_tab_helper.h54
-rw-r--r--chromium/chrome/browser/signin/dice_tab_helper_unittest.cc42
-rw-r--r--chromium/chrome/browser/signin/dice_web_signin_interceptor.cc139
-rw-r--r--chromium/chrome/browser/signin/dice_web_signin_interceptor.h37
-rw-r--r--chromium/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc96
-rw-r--r--chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.cc5
-rw-r--r--chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.h2
-rw-r--r--chromium/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc126
-rw-r--r--chromium/chrome/browser/signin/e2e_tests/live_sign_in_test.cc4
-rw-r--r--chromium/chrome/browser/signin/e2e_tests/live_test.cc3
-rw-r--r--chromium/chrome/browser/signin/e2e_tests/test_accounts_util.cc4
-rw-r--r--chromium/chrome/browser/signin/force_signin_verifier.cc24
-rw-r--r--chromium/chrome/browser/signin/force_signin_verifier.h18
-rw-r--r--chromium/chrome/browser/signin/header_modification_delegate_impl.cc50
-rw-r--r--chromium/chrome/browser/signin/header_modification_delegate_impl.h1
-rw-r--r--chromium/chrome/browser/signin/identity_manager_factory.cc9
-rw-r--r--chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.cc21
-rw-r--r--chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.h14
-rw-r--r--chromium/chrome/browser/signin/mirror_browsertest.cc58
-rw-r--r--chromium/chrome/browser/signin/mirror_interactive_uitest.cc77
-rw-r--r--chromium/chrome/browser/signin/primary_account_policy_manager.cc10
-rw-r--r--chromium/chrome/browser/signin/primary_account_policy_manager_factory.cc5
-rw-r--r--chromium/chrome/browser/signin/primary_account_policy_manager_factory.h2
-rw-r--r--chromium/chrome/browser/signin/process_dice_header_delegate_impl.cc86
-rw-r--r--chromium/chrome/browser/signin/process_dice_header_delegate_impl.h13
-rw-r--r--chromium/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc110
-rw-r--r--chromium/chrome/browser/signin/profile_token_web_signin_interceptor.cc22
-rw-r--r--chromium/chrome/browser/signin/profile_token_web_signin_interceptor.h2
-rw-r--r--chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.cc5
-rw-r--r--chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.h2
-rw-r--r--chromium/chrome/browser/signin/services/android/BUILD.gn2
-rw-r--r--chromium/chrome/browser/signin/signin_browser_test_base.h29
-rw-r--r--chromium/chrome/browser/signin/signin_features.cc66
-rw-r--r--chromium/chrome/browser/signin/signin_features.h45
-rw-r--r--chromium/chrome/browser/signin/signin_manager_android_factory.cc5
-rw-r--r--chromium/chrome/browser/signin/signin_manager_android_factory.h2
-rw-r--r--chromium/chrome/browser/signin/signin_manager_unittest.cc1
-rw-r--r--chromium/chrome/browser/signin/signin_profile_attributes_updater.h5
-rw-r--r--chromium/chrome/browser/signin/signin_promo.cc12
-rw-r--r--chromium/chrome/browser/signin/signin_promo.h21
-rw-r--r--chromium/chrome/browser/signin/signin_promo_unittest.cc6
-rw-r--r--chromium/chrome/browser/signin/signin_ui_delegate_impl_lacros.cc3
-rw-r--r--chromium/chrome/browser/signin/signin_ui_util.cc22
-rw-r--r--chromium/chrome/browser/signin/signin_ui_util.h7
-rw-r--r--chromium/chrome/browser/signin/signin_ui_util_unittest.cc45
-rw-r--r--chromium/chrome/browser/signin/signin_util.cc191
-rw-r--r--chromium/chrome/browser/signin/signin_util.h70
-rw-r--r--chromium/chrome/browser/signin/signin_util_unittest.cc560
-rw-r--r--chromium/chrome/browser/signin/token_managed_profile_creator.cc20
-rw-r--r--chromium/chrome/browser/signin/token_managed_profile_creator.h24
-rw-r--r--chromium/chrome/browser/signin/token_managed_profile_creator_unittest.cc154
-rw-r--r--chromium/chrome/browser/signin/wait_for_network_callback_helper_ash.cc36
-rw-r--r--chromium/chrome/browser/signin/wait_for_network_callback_helper_ash.h22
-rw-r--r--chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome.cc53
-rw-r--r--chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome.h47
-rw-r--r--chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome_unittest.cc74
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_factory.cc7
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_factory.h2
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_mac_view_interactive_uitest.mm4
-rw-r--r--chromium/chrome/browser/supervised_user/BUILD.gn4
-rw-r--r--chromium/chrome/browser/sync/test/integration/sync_integration_tests_sources.gni5
-rw-r--r--chromium/chrome/browser/tab/BUILD.gn1
-rw-r--r--chromium/chrome/browser/tab_contents/navigation_metrics_recorder.cc3
-rw-r--r--chromium/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc2
-rw-r--r--chromium/chrome/browser/tab_contents/view_source_browsertest.cc6
-rw-r--r--chromium/chrome/browser/tab_contents/web_contents_collection.cc4
-rw-r--r--chromium/chrome/browser/tab_contents/web_contents_collection.h1
-rw-r--r--chromium/chrome/browser/tabmodel/BUILD.gn5
-rw-r--r--chromium/chrome/browser/test_dummy/internal/BUILD.gn1
-rw-r--r--chromium/chrome/browser/touch_to_fill/android/BUILD.gn8
-rw-r--r--chromium/chrome/browser/touch_to_fill/android/internal/BUILD.gn5
-rw-r--r--chromium/chrome/browser/touch_to_fill/common/android/BUILD.gn4
-rw-r--r--chromium/chrome/browser/touch_to_fill/password_generation/android/BUILD.gn6
-rw-r--r--chromium/chrome/browser/touch_to_fill/password_generation/android/internal/BUILD.gn20
-rw-r--r--chromium/chrome/browser/touch_to_fill/payments/android/internal/BUILD.gn4
-rw-r--r--chromium/chrome/browser/ui/BUILD.gn610
-rw-r--r--chromium/chrome/browser/ui/actions/BUILD.gn27
-rw-r--r--chromium/chrome/browser/ui/android/appmenu/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ui/android/appmenu/internal/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ui/android/appmenu/test/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ui/android/autofill/internal/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ui/android/cars/BUILD.gn47
-rw-r--r--chromium/chrome/browser/ui/android/device_lock/BUILD.gn19
-rw-r--r--chromium/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn5
-rw-r--r--chromium/chrome/browser/ui/android/hats/BUILD.gn78
-rw-r--r--chromium/chrome/browser/ui/android/hats/internal/BUILD.gn64
-rw-r--r--chromium/chrome/browser/ui/android/logo/BUILD.gn6
-rw-r--r--chromium/chrome/browser/ui/android/management/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ui/android/multiwindow/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ui/android/night_mode/BUILD.gn3
-rw-r--r--chromium/chrome/browser/ui/android/omnibox/BUILD.gn41
-rw-r--r--chromium/chrome/browser/ui/android/page_insights/BUILD.gn78
-rw-r--r--chromium/chrome/browser/ui/android/quickactionsearchwidget/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ui/android/signin/BUILD.gn5
-rw-r--r--chromium/chrome/browser/ui/android/strings/android_chrome_strings.grd434
-rw-r--r--chromium/chrome/browser/ui/android/theme/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ui/android/toolbar/BUILD.gn14
-rw-r--r--chromium/chrome/browser/ui/android/webid/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ui/android/webid/internal/BUILD.gn12
-rw-r--r--chromium/chrome/browser/ui/color/BUILD.gn12
-rw-r--r--chromium/chrome/browser/ui/messages/android/BUILD.gn1
-rw-r--r--chromium/chrome/browser/ui/quick_answers/BUILD.gn7
-rw-r--r--chromium/chrome/browser/ui/views/editor_menu/BUILD.gn70
-rw-r--r--chromium/chrome/browser/ui/web_applications/BUILD.gn25
-rw-r--r--chromium/chrome/browser/ui/web_applications/diagnostics/BUILD.gn5
-rw-r--r--chromium/chrome/browser/ui/webui/BUILD.gn5
-rw-r--r--chromium/chrome/browser/ui/webui/about_ui.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/about_ui.h1
-rw-r--r--chromium/chrome/browser/ui/webui/access_code_cast/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.h3
-rw-r--r--chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h4
-rw-r--r--chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/app_home/DEPS5
-rw-r--r--chromium/chrome/browser/ui/webui/app_home/app_home_page_handler.cc11
-rw-r--r--chromium/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc59
-rw-r--r--chromium/chrome/browser/ui/webui/app_launcher_page_ui.cc201
-rw-r--r--chromium/chrome/browser/ui/webui/app_launcher_page_ui.h38
-rw-r--r--chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.cc239
-rw-r--r--chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.h22
-rw-r--r--chromium/chrome/browser/ui/webui/app_management/app_management_page_handler_unittest.cc743
-rw-r--r--chromium/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.cc41
-rw-r--r--chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h7
-rw-r--r--chromium/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc37
-rw-r--r--chromium/chrome/browser/ui/webui/ash/DEPS1
-rw-r--r--chromium/chrome/browser/ui/webui/ash/add_supervision/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_metrics_recorder.h5
-rw-r--r--chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui.cc18
-rw-r--r--chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui_browsertest.cc22
-rw-r--r--chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.cc94
-rw-r--r--chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.h35
-rw-r--r--chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler_unittest.cc281
-rw-r--r--chromium/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/ash/borealis_installer/BUILD.gn14
-rw-r--r--chromium/chrome/browser/ui/webui/ash/borealis_installer/OWNERS5
-rw-r--r--chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer.mojom40
-rw-r--r--chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.cc142
-rw-r--r--chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.h64
-rw-r--r--chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.cc99
-rw-r--r--chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.h70
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/ash/certificate_manager_dialog_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/chrome_untrusted_web_ui_configs_chromeos.cc22
-rw-r--r--chromium/chrome/browser/ui/webui/ash/chrome_web_ui_configs_chromeos.cc20
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom10
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc674
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h67
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog_browsertest.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc130
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h34
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager_unittest.cc91
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.cc15
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h1
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.cc66
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc159
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h112
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc304
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h59
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc316
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc170
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h22
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc278
-rw-r--r--chromium/chrome/browser/ui/webui/ash/cros_components_browsertest.cc10
-rw-r--r--chromium/chrome/browser/ui/webui/ash/drive_internals_ui.cc330
-rw-r--r--chromium/chrome/browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emoji/BUILD.gn5
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.cc50
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.h1
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.h16
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.cc23
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.h29
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.mojom13
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.h7
-rw-r--r--chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_ui.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/enterprise_reporting/OWNERS (renamed from chromium/chrome/browser/ui/webui/waffle/OWNERS)4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/in_session_password_change/password_change_ui.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/ash/internet_config_dialog.cc14
-rw-r--r--chromium/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_browsertest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_network_ui.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc32
-rw-r--r--chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/add_child_screen_handler.cc45
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/add_child_screen_handler.h49
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.cc87
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h31
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/choobe_screen_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.cc94
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h81
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc16
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.h23
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/display_size_screen_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.cc23
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.h4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc105
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h3
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.h8
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.cc19
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.h8
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc161
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.h50
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/l10n_util_test_util.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/local_password_setup_handler.cc34
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/local_password_setup_handler.h51
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.h4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/network_state_informer.cc27
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/network_state_informer.h3
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/oobe_ui.cc58
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/password_selection_screen_handler.cc36
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/password_selection_screen_handler.h54
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/signin_userlist_unittest.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.cc85
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/ash/mako/OWNERS3
-rw-r--r--chromium/chrome/browser/ui/webui/ash/mako/mako_source.cc81
-rw-r--r--chromium/chrome/browser/ui/webui/ash/mako/mako_source.h31
-rw-r--r--chromium/chrome/browser/ui/webui/ash/mako/mako_ui.cc141
-rw-r--r--chromium/chrome/browser/ui/webui/ash/mako/mako_ui.h57
-rw-r--r--chromium/chrome/browser/ui/webui/ash/mako/url_constants.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/ash/mako/url_constants.h15
-rw-r--r--chromium/chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/network_ui.cc9
-rw-r--r--chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.h11
-rw-r--r--chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog_browsertest.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc19
-rw-r--r--chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h13
-rw-r--r--chromium/chrome/browser/ui/webui/ash/scalable_iph/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/ui/webui/ash/scalable_iph/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.cc69
-rw-r--r--chromium/chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.h44
-rw-r--r--chromium/chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.cc30
-rw-r--r--chromium/chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.h37
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/OWNERS10
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA (renamed from chromium/chrome/browser/ui/webui/settings/ash/app_management/DIR_METADATA)0
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/app_management/OWNERS (renamed from chromium/chrome/browser/ui/webui/settings/ash/app_management/OWNERS)0
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/app_management/app_management_uma.h)12
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/calculator/size_calculator.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/calculator/size_calculator.cc)22
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/calculator/size_calculator.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/calculator/size_calculator.h)6
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/calculator/size_calculator_test_api.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/calculator/size_calculator_test_api.h)8
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/constants/constants_util.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/constants/constants_util.cc)41
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/constants/constants_util.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/constants/constants_util.h)10
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/mojom/BUILD.gn (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/BUILD.gn)4
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/mojom/OWNERS (renamed from chromium/chrome/browser/ui/webui/settings/chromeos/constants/OWNERS)0
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/search.mojom)6
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/mojom/search_result_icon.mojom (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom)1
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/mojom/user_action_recorder.mojom (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom)2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/search_concept.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/search_concept.h)14
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/search_handler.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/search_handler.cc)42
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/search_handler.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/search_handler.h)10
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/search_handler_unittest.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/search_handler_unittest.cc)93
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/search_tag_registry.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.cc)28
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h)7
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/search/search_tag_registry_unittest.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/search_tag_registry_unittest.cc)6
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/test_support/BUILD.gn (renamed from chromium/chrome/browser/ui/webui/settings/ash/BUILD.gn)0
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_browser_test_mixin.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.cc)2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_browser_test_mixin.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.h)6
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.cc)15
-rw-r--r--chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.h)17
-rw-r--r--chromium/chrome/browser/ui/webui/ash/smb_shares/smb_credentials_dialog.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.cc35
-rw-r--r--chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.h12
-rw-r--r--chromium/chrome/browser/ui/webui/ash/smb_shares/smb_share_dialog.cc19
-rw-r--r--chromium/chrome/browser/ui/webui/ash/status_area_internals/DEPS3
-rw-r--r--chromium/chrome/browser/ui/webui/ash/status_area_internals/OWNERS2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.cc101
-rw-r--r--chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.h48
-rw-r--r--chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler_unittest.cc129
-rw-r--r--chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.cc46
-rw-r--r--chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.h31
-rw-r--r--chromium/chrome/browser/ui/webui/ash/sync/os_sync_handler_unittest.cc11
-rw-r--r--chromium/chrome/browser/ui/webui/ash/sys_internals/sys_internals_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/ash/system_web_dialog_browsertest.cc10
-rw-r--r--chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.h2
-rw-r--r--chromium/chrome/browser/ui/webui/autofill_and_password_manager_internals/password_manager_internals_ui_browsertest.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.cc65
-rw-r--r--chromium/chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.h23
-rw-r--r--chromium/chrome/browser/ui/webui/browser_command/DEPS4
-rw-r--r--chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.cc43
-rw-r--r--chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.h4
-rw-r--r--chromium/chrome/browser/ui/webui/browser_command/browser_command_handler_unittest.cc67
-rw-r--r--chromium/chrome/browser/ui/webui/certificate_viewer_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/certificate_viewer_webui.cc28
-rw-r--r--chromium/chrome/browser/ui/webui/certificates_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_desktop.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc31
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_web_contents_handler.cc18
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc71
-rw-r--r--chromium/chrome/browser/ui/webui/commerce/shopping_insights_side_panel_ui.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/commerce/shopping_ui_handler_delegate.cc17
-rw-r--r--chromium/chrome/browser/ui/webui/components/components_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/cookies_tree_model_util.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc24
-rw-r--r--chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h6
-rw-r--r--chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/OWNERS4
-rw-r--r--chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.cc95
-rw-r--r--chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.h (renamed from chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.h)21
-rw-r--r--chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.cc279
-rw-r--r--chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h91
-rw-r--r--chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler_unittest.cc646
-rw-r--r--chromium/chrome/browser/ui/webui/device_log_ui.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/discards/graph_dump_impl.h3
-rw-r--r--chromium/chrome/browser/ui/webui/discards/graph_dump_impl_unittest.cc25
-rw-r--r--chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/downloads/BUILD.gn5
-rw-r--r--chromium/chrome/browser/ui/webui/downloads/downloads.mojom11
-rw-r--r--chromium/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc29
-rw-r--r--chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.cc32
-rw-r--r--chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.h8
-rw-r--r--chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker_unittest.cc49
-rw-r--r--chromium/chrome/browser/ui/webui/downloads/downloads_ui.cc39
-rw-r--r--chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc10
-rw-r--r--chromium/chrome/browser/ui/webui/extension_control_handler.cc30
-rw-r--r--chromium/chrome/browser/ui/webui/extension_control_handler.h7
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc97
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_test_base.cc89
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_test_base.h (renamed from chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h)36
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extensions_internals_source.cc108
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc23
-rw-r--r--chromium/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/feedback/feedback_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/feedback/feedback_ui.cc10
-rw-r--r--chromium/chrome/browser/ui/webui/flags/flags_ui.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/flags/flags_ui_handler.cc43
-rw-r--r--chromium/chrome/browser/ui/webui/flags/flags_ui_handler.h3
-rw-r--r--chromium/chrome/browser/ui/webui/flags/flags_ui_handler_unittest.cc111
-rw-r--r--chromium/chrome/browser/ui/webui/hats/DIR_METADATA3
-rw-r--r--chromium/chrome/browser/ui/webui/hats/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/hats/hats_ui.cc41
-rw-r--r--chromium/chrome/browser/ui/webui/hats/hats_ui.h37
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_mac.h7
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_mac.mm37
-rw-r--r--chromium/chrome/browser/ui/webui/history_clusters/history_clusters_handler_browsertest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/identity_internals_ui.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/inspect_ui_browsertest.cc60
-rw-r--r--chromium/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc25
-rw-r--r--chromium/chrome/browser/ui/webui/intro/intro_handler.cc47
-rw-r--r--chromium/chrome/browser/ui/webui/intro/intro_handler.h21
-rw-r--r--chromium/chrome/browser/ui/webui/intro/intro_ui.cc47
-rw-r--r--chromium/chrome/browser/ui/webui/intro/intro_ui.h24
-rw-r--r--chromium/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc11
-rw-r--r--chromium/chrome/browser/ui/webui/invalidations/invalidations_ui.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/local_state/local_state_ui.cc18
-rw-r--r--chromium/chrome/browser/ui/webui/location_internals/BUILD.gn12
-rw-r--r--chromium/chrome/browser/ui/webui/location_internals/location_internals.mojom19
-rw-r--r--chromium/chrome/browser/ui/webui/location_internals/location_internals_handler.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/location_internals/location_internals_handler.h34
-rw-r--r--chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.h9
-rw-r--r--chromium/chrome/browser/ui/webui/management/management_ui.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/management/management_ui_browsertest.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/management/management_ui_handler.cc163
-rw-r--r--chromium/chrome/browser/ui/webui/management/management_ui_handler.h5
-rw-r--r--chromium/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc205
-rw-r--r--chromium/chrome/browser/ui/webui/management/management_ui_pwa_browsertest.cc102
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/cast_feedback_ui.cc16
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h1
-rw-r--r--chromium/chrome/browser/ui/webui/memory_internals_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/DEPS3
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_contact_handler.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc10
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.h20
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_prefs_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.cc120
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.h6
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.cc87
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.h4
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h2
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui_browsertest.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/nearby_share/shared_resources.cc17
-rw-r--r--chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.cc145
-rw-r--r--chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc105
-rw-r--r--chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom4
-rw-r--r--chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc83
-rw-r--r--chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h1
-rw-r--r--chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc80
-rw-r--r--chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc89
-rw-r--r--chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h10
-rw-r--r--chromium/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc11
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_icon_webui_handler.cc16
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc16
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.h3
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.h2
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/new_tab_ui_browsertest.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h2
-rw-r--r--chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc28
-rw-r--r--chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h10
-rw-r--r--chromium/chrome/browser/ui/webui/password_manager/password_manager_ui.cc73
-rw-r--r--chromium/chrome/browser/ui/webui/password_manager/promo_card.cc22
-rw-r--r--chromium/chrome/browser/ui/webui/password_manager/promo_card.h4
-rw-r--r--chromium/chrome/browser/ui/webui/password_manager/promo_card_unittest.cc25
-rw-r--r--chromium/chrome/browser/ui/webui/policy/policy_test_ui_browsertest.cc773
-rw-r--r--chromium/chrome/browser/ui/webui/policy/policy_ui.cc128
-rw-r--r--chromium/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc46
-rw-r--r--chromium/chrome/browser/ui/webui/policy/policy_ui_handler.cc130
-rw-r--r--chromium/chrome/browser/ui/webui/policy/policy_ui_handler.h14
-rw-r--r--chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/prefs_internals_source.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc50
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc48
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_unittest.cc14
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.cc43
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.h14
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc50
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.h19
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos_unittest.cc172
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc91
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.cc107
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.h36
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.cc62
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.h18
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_utils_unittest.cc81
-rw-r--r--chromium/chrome/browser/ui/webui/privacy_sandbox/DIR_METADATA2
-rw-r--r--chromium/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/profile_helper.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/profile_internals/profile_internals_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/realbox/realbox_handler.cc63
-rw-r--r--chromium/chrome/browser/ui/webui/realbox/realbox_handler.h13
-rw-r--r--chromium/chrome/browser/ui/webui/realbox/realbox_handler_browsertest.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/realbox/realbox_handler_unittest.cc73
-rw-r--r--chromium/chrome/browser/ui/webui/sandbox/sandbox_handler.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/search_engine_choice/BUILD.gn (renamed from chromium/chrome/browser/ui/webui/waffle/BUILD.gn)4
-rw-r--r--chromium/chrome/browser/ui/webui/search_engine_choice/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/ui/webui/search_engine_choice/OWNERS (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/OWNERS)4
-rw-r--r--chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom25
-rw-r--r--chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.cc34
-rw-r--r--chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.h36
-rw-r--r--chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc136
-rw-r--r--chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h60
-rw-r--r--chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui_browsertest.cc164
-rw-r--r--chromium/chrome/browser/ui/webui/settings/about_handler.cc15
-rw-r--r--chromium/chrome/browser/ui/webui/settings/about_handler_unittest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/DEPS5
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/DIR_METADATA2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/OWNERS6
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/about_section.cc89
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/about_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_browsertest.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_unittest.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.cc113
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/account_manager_ui_handler_browsertest.cc56
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/apps_section.cc96
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/apps_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.cc35
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.h5
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/crostini_section.cc77
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/crostini_section.h12
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.cc42
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.h15
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler_unittest.cc9
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/date_time_section.cc53
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/date_time_section.h3
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/device_section.cc307
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/device_section.h6
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/device_section_unittest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler_unittest.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_sections.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/fast_pair_saved_devices_handler_unittest.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_handler_browsertest.cc128
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.cc88
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.h6
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/files_page/mojom/google_drive_handler.mojom15
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/files_page/one_drive_page_handler.cc99
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/files_section.cc107
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/files_section.h3
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/fingerprint_handler.cc24
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/hierarchy.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/hierarchy.h6
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/hierarchy_unittest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/internet_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/internet_section.cc37
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/internet_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/languages_section.cc51
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/languages_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/main_section.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/main_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler.h8
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler_unittest.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler.cc17
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler_unittest.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.h10
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/multidevice_section_unittest.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.h5
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler_unittest.cc25
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/BUILD.gn3
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_auto_screen_lock_browsertest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.h8
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util_unittest.cc91
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_identifier.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_authentication_browsertest.cc26
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager.cc14
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.h2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_unittest.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.cc89
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.h9
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_notification_settings_browsertest.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_password_setup_browsertest.cc60
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_pin_setup_browsertest.cc474
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_recovery_browsertest.cc14
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.h16
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_section_unittest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.cc34
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.h2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.cc17
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/pdf_ocr_handler_unittest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/people_section.cc51
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/people_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.cc)126
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.h (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h)29
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker_unittest.cc (renamed from chromium/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker_unittest.cc)582
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/personalization_hub_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/personalization_section.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/personalization_section.h5
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/printing_section.cc28
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/printing_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.cc67
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.h16
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler_unittest.cc65
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/privacy_section.cc29
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/privacy_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/reset_section.cc90
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/reset_section.h8
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/search_engines_handler_unittest.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/search_section.cc58
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/search_section.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/select_to_speak_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.cc10
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.h8
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker_unittest.cc156
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/system_preferences_section.cc90
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/system_preferences_section.h52
-rw-r--r--chromium/chrome/browser/ui/webui/settings/ash/tts_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/settings/captions_handler.cc28
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/constants/BUILD.gn27
-rwxr-xr-xchromium/chrome/browser/ui/webui/settings/chromeos/constants/gen_routes.py78
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom327
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc42
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.h17
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util_unittest.cc30
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom305
-rw-r--r--chromium/chrome/browser/ui/webui/settings/hats_handler.cc161
-rw-r--r--chromium/chrome/browser/ui/webui/settings/hats_handler.h6
-rw-r--r--chromium/chrome/browser/ui/webui/settings/hats_handler_unittest.cc48
-rw-r--r--chromium/chrome/browser/ui/webui/settings/mac_system_settings_handler.cc32
-rw-r--r--chromium/chrome/browser/ui/webui/settings/mac_system_settings_handler.h32
-rw-r--r--chromium/chrome/browser/ui/webui/settings/people_handler.cc26
-rw-r--r--chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc23
-rw-r--r--chromium/chrome/browser/ui/webui/settings/performance_handler.cc16
-rw-r--r--chromium/chrome/browser/ui/webui/settings/performance_handler.h13
-rw-r--r--chromium/chrome/browser/ui/webui/settings/performance_settings_interactive_uitest.cc302
-rw-r--r--chromium/chrome/browser/ui/webui/settings/privacy_sandbox_handler_unittest.cc36
-rw-r--r--chromium/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc31
-rw-r--r--chromium/chrome/browser/ui/webui/settings/safety_hub_handler.cc198
-rw-r--r--chromium/chrome/browser/ui/webui/settings/safety_hub_handler.h37
-rw-r--r--chromium/chrome/browser/ui/webui/settings/safety_hub_handler_unittest.cc272
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc795
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler_unittest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.cc32
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_ui.cc171
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_ui.h50
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_utils_mac.mm10
-rw-r--r--chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc60
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc221
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_handler.h199
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc1183
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_helper.cc424
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_helper.h80
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_helper_unittest.cc183
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc10
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.h2
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.cc38
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.h9
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/customize_chrome/OWNERS4
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom36
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_browsertest.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.cc36
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc150
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h17
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc220
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc47
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h17
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h10
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/DEPS2
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_browsertest.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_read_aloud_browsertest.cc75
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc79
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.cc25
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.h24
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc (renamed from chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.cc)132
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h (renamed from chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.h)44
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc (renamed from chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.cc)62
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.h (renamed from chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.h)30
-rw-r--r--chromium/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler_unittest.cc149
-rw-r--r--chromium/chrome/browser/ui/webui/signin/OWNERS4
-rw-r--r--chromium/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc14
-rw-r--r--chromium/chrome/browser/ui/webui/signin/ash/signin_helper_browsertest.cc26
-rw-r--r--chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc29
-rw-r--r--chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler_unittest.cc105
-rw-r--r--chromium/chrome/browser/ui/webui/signin/enterprise_profile_welcome_handler.cc17
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_service.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.h4
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.cc77
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h39
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_customization_handler.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_customization_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_customization_ui.cc37
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_customization_ui.h22
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_picker_handler.cc124
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_picker_handler.h6
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_picker_handler_unittest.cc54
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_picker_ui.cc57
-rw-r--r--chromium/chrome/browser/ui/webui/signin/profile_picker_ui.h27
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_error_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_reauth_ui.cc20
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.h3
-rw-r--r--chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_unittest.cc14
-rw-r--r--chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals.mojom13
-rw-r--r--chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.cc22
-rw-r--r--chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.h7
-rw-r--r--chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.cc116
-rw-r--r--chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.h2
-rw-r--r--chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.cc163
-rw-r--r--chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.h22
-rw-r--r--chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils_unittest.cc47
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc59
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h9
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler_unittest.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc20
-rw-r--r--chromium/chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.cc117
-rw-r--r--chromium/chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.h57
-rw-r--r--chromium/chrome/browser/ui/webui/version/version_handler_chromeos.cc54
-rw-r--r--chromium/chrome/browser/ui/webui/version/version_handler_chromeos.h9
-rw-r--r--chromium/chrome/browser/ui/webui/version/version_ui.cc19
-rw-r--r--chromium/chrome/browser/ui/webui/waffle/DIR_METADATA1
-rw-r--r--chromium/chrome/browser/ui/webui/waffle/waffle.mojom19
-rw-r--r--chromium/chrome/browser/ui/webui/waffle/waffle_handler.cc26
-rw-r--r--chromium/chrome/browser/ui/webui/waffle/waffle_handler.h31
-rw-r--r--chromium/chrome/browser/ui/webui/waffle/waffle_ui.cc97
-rw-r--r--chromium/chrome/browser/ui/webui/waffle/waffle_ui.h50
-rw-r--r--chromium/chrome/browser/ui/webui/web_app_internals/BUILD.gn3
-rw-r--r--chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals.mojom10
-rw-r--r--chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.cc109
-rw-r--r--chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.h10
-rw-r--r--chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_ui.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/webapks/webapks_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/webui_load_timer_browsertest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/webui_webview_browsertest.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/welcome/welcome_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/whats_new/whats_new_handler.cc20
-rw-r--r--chromium/chrome/browser/ui/webui/whats_new/whats_new_util.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/whats_new/whats_new_util.h4
-rw-r--r--chromium/chrome/browser/ui/webui/whats_new/whats_new_util_unittest.cc51
-rw-r--r--chromium/chrome/browser/updater/BUILD.gn1
-rw-r--r--chromium/chrome/browser/usb/android/BUILD.gn3
-rw-r--r--chromium/chrome/browser/util/BUILD.gn1
-rw-r--r--chromium/chrome/browser/vr/BUILD.gn135
-rw-r--r--chromium/chrome/browser/vr/features.gni13
-rw-r--r--chromium/chrome/browser/vr/vector_icons/BUILD.gn11
-rw-r--r--chromium/chrome/browser/web_applications/BUILD.gn118
-rw-r--r--chromium/chrome/browser/web_applications/app_service/BUILD.gn11
-rw-r--r--chromium/chrome/browser/web_applications/extensions/BUILD.gn7
-rw-r--r--chromium/chrome/browser/webauthn/android/BUILD.gn2
-rw-r--r--chromium/chrome/browser/xsurface/BUILD.gn7
-rw-r--r--chromium/chrome/browser/xsurface_provider/BUILD.gn51
1942 files changed, 60069 insertions, 33294 deletions
diff --git a/chromium/chrome/browser/3pcd_heuristics/BUILD.gn b/chromium/chrome/browser/3pcd/heuristics/BUILD.gn
index 3517461039e..3517461039e 100644
--- a/chromium/chrome/browser/3pcd_heuristics/BUILD.gn
+++ b/chromium/chrome/browser/3pcd/heuristics/BUILD.gn
diff --git a/chromium/chrome/browser/BUILD.gn b/chromium/chrome/browser/BUILD.gn
index 01194da83fe..9ab6c039d18 100644
--- a/chromium/chrome/browser/BUILD.gn
+++ b/chromium/chrome/browser/BUILD.gn
@@ -17,6 +17,7 @@ import("//chrome/common/features.gni")
import("//chrome/services/speech/buildflags/buildflags.gni")
import("//chromeos/ash/components/assistant/assistant.gni")
import("//components/captive_portal/core/features.gni")
+import("//components/enterprise/buildflags/buildflags.gni")
import("//components/feed/features.gni")
import("//components/lens/features.gni")
import("//components/nacl/features.gni")
@@ -138,12 +139,12 @@ source_set("browser_process") {
# require many files from it. This makes linking more efficient.
static_library("browser") {
sources = [
- "3pcd_heuristics/opener_heuristic_metrics.cc",
- "3pcd_heuristics/opener_heuristic_metrics.h",
- "3pcd_heuristics/opener_heuristic_tab_helper.cc",
- "3pcd_heuristics/opener_heuristic_tab_helper.h",
- "3pcd_heuristics/opener_heuristic_utils.cc",
- "3pcd_heuristics/opener_heuristic_utils.h",
+ "3pcd/heuristics/opener_heuristic_metrics.cc",
+ "3pcd/heuristics/opener_heuristic_metrics.h",
+ "3pcd/heuristics/opener_heuristic_tab_helper.cc",
+ "3pcd/heuristics/opener_heuristic_tab_helper.h",
+ "3pcd/heuristics/opener_heuristic_utils.cc",
+ "3pcd/heuristics/opener_heuristic_utils.h",
"about_flags.cc",
"about_flags.h",
"accessibility/accessibility_labels_service.cc",
@@ -279,6 +280,8 @@ static_library("browser") {
"chrome_browser_main.cc",
"chrome_browser_main.h",
"chrome_browser_main_extra_parts.h",
+ "chrome_browser_main_extra_parts_nacl_deprecation.cc",
+ "chrome_browser_main_extra_parts_nacl_deprecation.h",
"chrome_content_browser_client.cc",
"chrome_content_browser_client.h",
"chrome_content_browser_client_binder_policies.cc",
@@ -299,8 +302,6 @@ static_library("browser") {
"commerce/shopping_service_factory.h",
"complex_tasks/task_tab_helper.cc",
"complex_tasks/task_tab_helper.h",
- "component_updater/chrome_client_side_phishing_component_installer.cc",
- "component_updater/chrome_client_side_phishing_component_installer.h",
"component_updater/chrome_component_updater_configurator.cc",
"component_updater/chrome_component_updater_configurator.h",
"component_updater/chrome_origin_trials_component_installer.cc",
@@ -319,6 +320,8 @@ static_library("browser") {
"component_updater/desktop_sharing_hub_component_remover.h",
"component_updater/first_party_sets_component_installer.cc",
"component_updater/first_party_sets_component_installer.h",
+ "component_updater/masked_domain_list_component_installer.cc",
+ "component_updater/masked_domain_list_component_installer.h",
"component_updater/mei_preload_component_installer.cc",
"component_updater/mei_preload_component_installer.h",
"component_updater/pki_metadata_component_installer.cc",
@@ -337,6 +340,8 @@ static_library("browser") {
"component_updater/ssl_error_assistant_component_installer.h",
"component_updater/subresource_filter_component_installer.cc",
"component_updater/subresource_filter_component_installer.h",
+ "component_updater/tpcd_metadata_component_installer.cc",
+ "component_updater/tpcd_metadata_component_installer.h",
"component_updater/trust_token_key_commitments_component_installer.cc",
"component_updater/trust_token_key_commitments_component_installer.h",
"consent_auditor/consent_auditor_factory.cc",
@@ -367,8 +372,6 @@ static_library("browser") {
"custom_handlers/protocol_handler_registry_factory.h",
"data_saver/data_saver.cc",
"data_saver/data_saver.h",
- "data_use_measurement/chrome_data_use_measurement.cc",
- "data_use_measurement/chrome_data_use_measurement.h",
"defaults.cc",
"defaults.h",
"dips/cookie_access_filter.cc",
@@ -383,8 +386,6 @@ static_library("browser") {
"dips/dips_cleanup_service_factory.h",
"dips/dips_database.cc",
"dips/dips_database.h",
- "dips/dips_features.cc",
- "dips/dips_features.h",
"dips/dips_redirect_info.cc",
"dips/dips_redirect_info.h",
"dips/dips_service.cc",
@@ -631,10 +632,12 @@ static_library("browser") {
"interstitials/enterprise_util.h",
"invalidation/profile_invalidation_provider_factory.cc",
"invalidation/profile_invalidation_provider_factory.h",
- "ip_protection/blind_sign_http_impl.cc",
- "ip_protection/blind_sign_http_impl.h",
- "ip_protection/ip_protection_auth_token_getter.cc",
- "ip_protection/ip_protection_auth_token_getter.h",
+ "ip_protection/ip_protection_config_http.cc",
+ "ip_protection/ip_protection_config_http.h",
+ "ip_protection/ip_protection_config_provider.cc",
+ "ip_protection/ip_protection_config_provider.h",
+ "ip_protection/ip_protection_config_provider_factory.cc",
+ "ip_protection/ip_protection_config_provider_factory.h",
"k_anonymity_service/k_anonymity_service_client.cc",
"k_anonymity_service/k_anonymity_service_client.h",
"k_anonymity_service/k_anonymity_service_factory.cc",
@@ -690,6 +693,8 @@ static_library("browser") {
"lookalikes/safety_tip_web_contents_observer.h",
"media/audio_service_util.cc",
"media/audio_service_util.h",
+ "media/chrome_media_session_client.cc",
+ "media/chrome_media_session_client.h",
"media/history/media_history_contents_observer.cc",
"media/history/media_history_contents_observer.h",
"media/history/media_history_images_table.cc",
@@ -712,8 +717,6 @@ static_library("browser") {
"media/history/media_history_table_base.h",
"media/media_access_handler.cc",
"media/media_access_handler.h",
- "media/media_device_id_salt.cc",
- "media/media_device_id_salt.h",
"media/media_engagement_contents_observer.cc",
"media/media_engagement_contents_observer.h",
"media/media_engagement_preloaded_list.cc",
@@ -738,6 +741,8 @@ static_library("browser") {
"media/webrtc/chrome_camera_pan_tilt_zoom_permission_context_delegate.h",
"media/webrtc/chrome_screen_enumerator.cc",
"media/webrtc/chrome_screen_enumerator.h",
+ "media/webrtc/desktop_capturer_wrapper.cc",
+ "media/webrtc/desktop_capturer_wrapper.h",
"media/webrtc/desktop_media_list.cc",
"media/webrtc/desktop_media_list.h",
"media/webrtc/desktop_media_list_base.cc",
@@ -750,6 +755,8 @@ static_library("browser") {
"media/webrtc/desktop_media_picker_manager.h",
"media/webrtc/media_capture_devices_dispatcher.cc",
"media/webrtc/media_capture_devices_dispatcher.h",
+ "media/webrtc/media_device_salt_service_factory.cc",
+ "media/webrtc/media_device_salt_service_factory.h",
"media/webrtc/media_stream_capture_indicator.cc",
"media/webrtc/media_stream_capture_indicator.h",
"media/webrtc/media_stream_device_permission_context.cc",
@@ -762,6 +769,8 @@ static_library("browser") {
"media/webrtc/permission_bubble_media_access_handler.h",
"media/webrtc/same_origin_observer.cc",
"media/webrtc/same_origin_observer.h",
+ "media/webrtc/thumbnail_capturer.cc",
+ "media/webrtc/thumbnail_capturer.h",
"media/webrtc/webrtc_event_log_history.cc",
"media/webrtc/webrtc_event_log_history.h",
"media/webrtc/webrtc_event_log_manager.cc",
@@ -799,8 +808,6 @@ static_library("browser") {
"memory/enterprise_memory_limit_evaluator.h",
"memory/enterprise_memory_limit_pref_observer.cc",
"memory/enterprise_memory_limit_pref_observer.h",
- "memory/memory_ablation_study.cc",
- "memory/memory_ablation_study.h",
"memory_details.cc",
"memory_details.h",
"metrics/chrome_browser_main_extra_parts_metrics.cc",
@@ -860,6 +867,14 @@ static_library("browser") {
"navigation_predictor/navigation_predictor_metrics_document_data.h",
"navigation_predictor/navigation_predictor_preconnect_client.cc",
"navigation_predictor/navigation_predictor_preconnect_client.h",
+ "navigation_predictor/preloading_model_executor.cc",
+ "navigation_predictor/preloading_model_executor.h",
+ "navigation_predictor/preloading_model_handler.cc",
+ "navigation_predictor/preloading_model_handler.h",
+ "navigation_predictor/preloading_model_keyed_service.cc",
+ "navigation_predictor/preloading_model_keyed_service.h",
+ "navigation_predictor/preloading_model_keyed_service_factory.cc",
+ "navigation_predictor/preloading_model_keyed_service_factory.h",
"navigation_predictor/search_engine_preconnector.cc",
"navigation_predictor/search_engine_preconnector.h",
"net/chrome_mojo_proxy_resolver_factory.cc",
@@ -986,6 +1001,8 @@ static_library("browser") {
"origin_trials/origin_trials_factory.h",
"page_info/about_this_site_service_factory.cc",
"page_info/about_this_site_service_factory.h",
+ "page_info/about_this_site_tab_helper.cc",
+ "page_info/about_this_site_tab_helper.h",
"page_info/chrome_about_this_site_service_client.cc",
"page_info/chrome_about_this_site_service_client.h",
"page_info/page_info_features.cc",
@@ -1014,6 +1031,8 @@ static_library("browser") {
"page_load_metrics/observers/https_engagement_metrics/https_engagement_service_factory.h",
"page_load_metrics/observers/javascript_frameworks_ukm_observer.cc",
"page_load_metrics/observers/javascript_frameworks_ukm_observer.h",
+ "page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc",
+ "page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.h",
"page_load_metrics/observers/loading_predictor_page_load_metrics_observer.cc",
"page_load_metrics/observers/loading_predictor_page_load_metrics_observer.h",
"page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc",
@@ -1072,8 +1091,12 @@ static_library("browser") {
"password_manager/password_manager_settings_service_factory.h",
"password_manager/password_manager_settings_service_impl.cc",
"password_manager/password_manager_settings_service_impl.h",
+ "password_manager/password_receiver_service_factory.cc",
+ "password_manager/password_receiver_service_factory.h",
"password_manager/password_reuse_manager_factory.cc",
"password_manager/password_reuse_manager_factory.h",
+ "password_manager/password_sender_service_factory.cc",
+ "password_manager/password_sender_service_factory.h",
"password_manager/password_store_factory.cc",
"password_manager/password_store_factory.h",
"password_manager/password_store_utils.cc",
@@ -1128,8 +1151,6 @@ static_library("browser") {
"permissions/crowd_deny_preload_data.h",
"permissions/crowd_deny_safe_browsing_request.cc",
"permissions/crowd_deny_safe_browsing_request.h",
- "permissions/notification_permission_review_service_factory.cc",
- "permissions/notification_permission_review_service_factory.h",
"permissions/notifications_engagement_service_factory.cc",
"permissions/notifications_engagement_service_factory.h",
"permissions/notifications_permission_revocation_config.cc",
@@ -1152,20 +1173,20 @@ static_library("browser") {
"permissions/prediction_service_factory.h",
"permissions/prediction_service_request.cc",
"permissions/prediction_service_request.h",
- "permissions/pref_notification_permission_ui_selector.cc",
- "permissions/pref_notification_permission_ui_selector.h",
+ "permissions/pref_based_quiet_permission_ui_selector.cc",
+ "permissions/pref_based_quiet_permission_ui_selector.h",
"permissions/quiet_notification_permission_ui_config.cc",
"permissions/quiet_notification_permission_ui_config.h",
"permissions/quiet_notification_permission_ui_state.cc",
"permissions/quiet_notification_permission_ui_state.h",
- "permissions/unused_site_permissions_service_factory.cc",
- "permissions/unused_site_permissions_service_factory.h",
"picture_in_picture/picture_in_picture_window_manager.cc",
"picture_in_picture/picture_in_picture_window_manager.h",
"platform_util.h",
"platform_util_internal.h",
"plugins/pdf_iframe_navigation_throttle.cc",
"plugins/pdf_iframe_navigation_throttle.h",
+ "plus_addresses/plus_address_service_factory.cc",
+ "plus_addresses/plus_address_service_factory.h",
"policy/chrome_browser_policy_connector.cc",
"policy/chrome_browser_policy_connector.h",
"policy/chrome_policy_conversions_client.cc",
@@ -1192,8 +1213,6 @@ static_library("browser") {
"policy/homepage_location_policy_handler.h",
"policy/javascript_policy_handler.cc",
"policy/javascript_policy_handler.h",
- "policy/management_utils.cc",
- "policy/management_utils.h",
"policy/messaging_layer/public/report_client.cc",
"policy/messaging_layer/public/report_client.h",
"policy/messaging_layer/upload/dm_server_uploader.cc",
@@ -1258,16 +1277,8 @@ static_library("browser") {
"predictors/autocomplete_action_predictor_factory.h",
"predictors/autocomplete_action_predictor_table.cc",
"predictors/autocomplete_action_predictor_table.h",
- "predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_database.cc",
- "predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_database.h",
"predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc",
"predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.h",
- "predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_keyed_service.cc",
- "predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_keyed_service.h",
- "predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_keyed_service_factory.cc",
- "predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_keyed_service_factory.h",
- "predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_persister.cc",
- "predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_persister.h",
"predictors/loading_data_collector.cc",
"predictors/loading_data_collector.h",
"predictors/loading_predictor.cc",
@@ -1369,10 +1380,14 @@ static_library("browser") {
"preloading/prefetch/search_prefetch/search_prefetch_url_loader_interceptor.h",
"preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc",
"preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.h",
+ "preloading/preloading_features.cc",
+ "preloading/preloading_features.h",
"preloading/prerender/prerender_manager.cc",
"preloading/prerender/prerender_manager.h",
"preloading/prerender/prerender_utils.cc",
"preloading/prerender/prerender_utils.h",
+ "preloading/prerender/prerender_web_contents_delegate.cc",
+ "preloading/prerender/prerender_web_contents_delegate.h",
"privacy/privacy_metrics_service.cc",
"privacy/privacy_metrics_service.h",
"privacy/privacy_metrics_service_factory.cc",
@@ -1694,14 +1709,8 @@ static_library("browser") {
"ssl/https_first_mode_settings_tracker.h",
"ssl/https_only_mode_controller_client.cc",
"ssl/https_only_mode_controller_client.h",
- "ssl/https_only_mode_navigation_throttle.cc",
- "ssl/https_only_mode_navigation_throttle.h",
"ssl/https_only_mode_tab_helper.cc",
"ssl/https_only_mode_tab_helper.h",
- "ssl/https_only_mode_upgrade_interceptor.cc",
- "ssl/https_only_mode_upgrade_interceptor.h",
- "ssl/https_only_mode_upgrade_url_loader.cc",
- "ssl/https_only_mode_upgrade_url_loader.h",
"ssl/https_upgrades_interceptor.cc",
"ssl/https_upgrades_interceptor.h",
"ssl/https_upgrades_navigation_throttle.cc",
@@ -1782,8 +1791,6 @@ static_library("browser") {
"sync/sync_service_factory.h",
"sync/sync_startup_tracker.cc",
"sync/sync_startup_tracker.h",
- "sync/trusted_vault_encryption_keys_tab_helper.cc",
- "sync/trusted_vault_encryption_keys_tab_helper.h",
"sync/user_event_service_factory.cc",
"sync/user_event_service_factory.h",
"tab_contents/navigation_metrics_recorder.cc",
@@ -1816,6 +1823,10 @@ static_library("browser") {
"translate/translate_ranker_metrics_provider.h",
"translate/translate_service.cc",
"translate/translate_service.h",
+ "trusted_vault/trusted_vault_encryption_keys_tab_helper.cc",
+ "trusted_vault/trusted_vault_encryption_keys_tab_helper.h",
+ "trusted_vault/trusted_vault_service_factory.cc",
+ "trusted_vault/trusted_vault_service_factory.h",
"undo/bookmark_undo_service_factory.cc",
"undo/bookmark_undo_service_factory.h",
"unexpire_flags.cc",
@@ -1846,7 +1857,6 @@ static_library("browser") {
"user_bypass/user_bypass_web_contents_observer.h",
"visibility_timer_tab_helper.cc",
"visibility_timer_tab_helper.h",
- "vr/ui_suppressed_element.h",
"vr/vr_tab_helper.cc",
"vr/vr_tab_helper.h",
"web_data_service_factory.cc",
@@ -1879,6 +1889,25 @@ static_library("browser") {
"webid/federated_identity_permission_context_factory.h",
]
+ if (!is_android) {
+ sources += [
+ "picture_in_picture/auto_pip_setting_helper.cc",
+ "picture_in_picture/auto_pip_setting_helper.h",
+ "picture_in_picture/auto_pip_setting_overlay_view.cc",
+ "picture_in_picture/auto_pip_setting_overlay_view.h",
+ "picture_in_picture/auto_pip_setting_view.cc",
+ "picture_in_picture/auto_pip_setting_view.h",
+ "user_education/browser_tutorial_service.cc",
+ "user_education/browser_tutorial_service.h",
+ "user_education/user_education_configuration_provider.cc",
+ "user_education/user_education_configuration_provider.h",
+ "user_education/user_education_service.cc",
+ "user_education/user_education_service.h",
+ "user_education/user_education_service_factory.cc",
+ "user_education/user_education_service_factory.h",
+ ]
+ }
+
configs += [
"//build/config/compiler:wexit_time_destructors",
"//build/config:precompiled_headers",
@@ -1928,6 +1957,8 @@ static_library("browser") {
"//chrome/services/file_util/public/mojom",
"//components/account_id",
"//components/autofill/core/browser",
+ "//components/cross_device/logging",
+ "//components/enterprise/buildflags",
"//components/enterprise/common:files_scan_data",
"//components/nacl/common:buildflags",
"//components/optimization_guide:machine_learning_tflite_buildflags",
@@ -1939,6 +1970,7 @@ static_library("browser") {
"//components/safe_browsing:buildflags",
"//components/services/screen_ai/buildflags",
"//components/services/storage/public/mojom",
+ "//components/session_proto_db:session_proto_db",
"//components/sync",
"//components/sync_device_info",
"//content/public/browser",
@@ -1955,6 +1987,7 @@ static_library("browser") {
":buildflags",
":dev_ui_browser_resources_grit",
":expired_flags_list",
+ ":get_proxy_config_proto",
":k_anonymity_storage_proto",
":lcp_critical_path_predictor_proto",
":ntp_background_proto",
@@ -1980,7 +2013,6 @@ static_library("browser") {
"//chrome/browser/bitmap_fetcher",
"//chrome/browser/breadcrumbs",
"//chrome/browser/browsing_data:constants",
- "//chrome/browser/chrome_for_testing:buildflags",
"//chrome/browser/companion/core",
"//chrome/browser/companion/text_finder",
"//chrome/browser/crash_upload_list",
@@ -2036,6 +2068,7 @@ static_library("browser") {
"//chrome/browser/ui/webui/app_service_internals:mojo_bindings",
"//chrome/browser/ui/webui/feed:mojo_bindings",
"//chrome/browser/ui/webui/internals/user_education:mojo_bindings",
+ "//chrome/browser/ui/webui/location_internals:mojo_bindings",
"//chrome/browser/ui/webui/omnibox:mojo_bindings",
"//chrome/browser/ui/webui/reset_password:mojo_bindings",
"//chrome/browser/ui/webui/segmentation_internals:mojo_bindings",
@@ -2051,7 +2084,6 @@ static_library("browser") {
"//chrome/common/notifications",
"//chrome/installer/util:with_no_strings",
"//chrome/services/speech/buildflags",
- "//chromeos/components/kiosk",
"//components/assist_ranker",
"//components/autofill/content/browser",
"//components/autofill/core/browser",
@@ -2103,6 +2135,7 @@ static_library("browser") {
"//components/custom_handlers",
"//components/device_event_log",
"//components/device_reauth",
+ "//components/device_signals/core/browser:browser",
"//components/dom_distiller/content/browser",
"//components/dom_distiller/content/common/mojom",
"//components/domain_reliability",
@@ -2147,6 +2180,7 @@ static_library("browser") {
"//components/history/metrics",
"//components/history_clusters/core",
"//components/history_clusters/history_clusters_internals/webui",
+ "//components/history_clusters/history_clusters_internals/webui:constants",
"//components/infobars/content",
"//components/infobars/core",
"//components/invalidation/impl",
@@ -2165,6 +2199,7 @@ static_library("browser") {
"//components/lookalikes/core",
"//components/lookalikes/core:proto",
"//components/lookalikes/core:safety_tips",
+ "//components/media_device_salt",
"//components/memory_pressure",
"//components/metrics:call_stack_profile_collector",
"//components/metrics:call_stack_profile_params",
@@ -2211,6 +2246,7 @@ static_library("browser") {
"//components/password_manager/content/browser",
"//components/password_manager/content/common",
"//components/password_manager/core/browser",
+ "//components/password_manager/core/browser/features:password_features",
"//components/password_manager/core/common",
"//components/payments/content:utils",
"//components/payments/core",
@@ -2218,6 +2254,7 @@ static_library("browser") {
"//components/permissions",
"//components/permissions/prediction_service",
"//components/permissions/prediction_service:prediction_service_messages_proto",
+ "//components/plus_addresses",
"//components/policy:generated",
"//components/policy/content:safe_sites_navigation_throttle",
"//components/policy/content/",
@@ -2230,6 +2267,7 @@ static_library("browser") {
"//components/prefs",
"//components/privacy_sandbox",
"//components/privacy_sandbox:privacy_sandbox_prefs",
+ "//components/privacy_sandbox:tracking_protection_prefs",
"//components/privacy_sandbox/privacy_sandbox_attestations",
"//components/profile_metrics",
"//components/proxy_config",
@@ -2246,6 +2284,7 @@ static_library("browser") {
"//components/reporting/encryption:encryption_module",
"//components/reporting/encryption:encryption_module_interface",
"//components/reporting/encryption:verification",
+ "//components/reporting/proto:configuration_file_proto",
"//components/reporting/proto:record_constants",
"//components/reporting/proto:record_proto",
"//components/reporting/proto:status_proto",
@@ -2324,7 +2363,7 @@ static_library("browser") {
"//components/spellcheck:buildflags",
"//components/sqlite_proto",
"//components/ssl_errors",
- "//components/startup_metric_utils/browser",
+ "//components/startup_metric_utils",
"//components/storage_monitor",
"//components/strings",
"//components/subresource_filter/content/browser",
@@ -2345,6 +2384,7 @@ static_library("browser") {
"//components/translate/core/browser",
"//components/translate/core/browser:translate_model_service",
"//components/translate/core/common",
+ "//components/trusted_vault",
"//components/ui_devtools",
"//components/ukm",
"//components/ukm:observers",
@@ -2354,8 +2394,8 @@ static_library("browser") {
"//components/upload_list",
"//components/url_formatter",
"//components/url_formatter/spoof_checks/top_domains:common",
- "//components/url_formatter/spoof_checks/top_domains:top500_domains",
- "//components/url_formatter/spoof_checks/top_domains:top500_domains_header",
+ "//components/url_formatter/spoof_checks/top_domains:top_bucket_domains",
+ "//components/url_formatter/spoof_checks/top_domains:top_bucket_domains_header",
"//components/url_matcher",
"//components/user_manager",
"//components/user_prefs",
@@ -2485,7 +2525,10 @@ static_library("browser") {
]
if (!is_android) {
- deps += [ "//chrome/browser/companion/visual_search:flags" ]
+ deps += [
+ "//chrome/browser/companion/visual_search:flags",
+ "//components/user_education/common",
+ ]
}
if (enable_screen_ai_service) {
@@ -2500,8 +2543,6 @@ static_library("browser") {
"accessibility/pdf_ocr_controller_factory.h",
"renderer_context_menu/pdf_ocr_menu_observer.cc",
"renderer_context_menu/pdf_ocr_menu_observer.h",
- "screen_ai/screen_ai_downloader.cc",
- "screen_ai/screen_ai_downloader.h",
]
deps += [
"//chrome/browser/screen_ai:prefs",
@@ -2512,14 +2553,33 @@ static_library("browser") {
"//components/services/screen_ai/public/mojom",
]
- if (is_chromeos_ash) {
- deps += [ "//chrome/browser/screen_ai:screen_ai_chromeos_installer" ]
+ if (is_chromeos) {
+ sources += [
+ "screen_ai/screen_ai_downloader_chromeos.cc",
+ "screen_ai/screen_ai_downloader_chromeos.h",
+ ]
} else {
sources += [
"component_updater/screen_ai_component_installer.cc",
"component_updater/screen_ai_component_installer.h",
+ "screen_ai/screen_ai_downloader_non_chromeos.cc",
+ "screen_ai/screen_ai_downloader_non_chromeos.h",
]
}
+
+ if (is_chromeos_ash) {
+ deps += [ "//chrome/browser/screen_ai:screen_ai_dlc_installer" ]
+ }
+ }
+
+ if (enable_search_engine_choice) {
+ sources += [
+ "search_engine_choice/search_engine_choice_service.cc",
+ "search_engine_choice/search_engine_choice_service.h",
+ "search_engine_choice/search_engine_choice_service_factory.cc",
+ "search_engine_choice/search_engine_choice_service_factory.h",
+ ]
+ deps += [ "//chrome/browser/ui/webui/search_engine_choice:mojo_bindings" ]
}
if (is_linux || is_chromeos) {
@@ -2564,8 +2624,17 @@ static_library("browser") {
]
}
+ if (!is_chromeos_ash) {
+ sources += [
+ "signin/wait_for_network_callback_helper_chrome.cc",
+ "signin/wait_for_network_callback_helper_chrome.h",
+ ]
+ }
+
if (is_android) {
sources += [
+ "accessibility/accessibility_prefs/android/accessibility_prefs_controller.cc",
+ "accessibility/accessibility_prefs/android/accessibility_prefs_controller.h",
"after_startup_task_utils_android.cc",
"android/android_theme_resources.h",
"android/autocomplete/tab_matcher_android.cc",
@@ -2686,6 +2755,8 @@ static_library("browser") {
"android/feedback/screenshot_task.cc",
"android/flags/bad_flags_snackbar_manager.cc",
"android/flags/bad_flags_snackbar_manager.h",
+ "android/flags/chrome_cached_flags.cc",
+ "android/flags/chrome_cached_flags.h",
"android/foreign_session_helper.cc",
"android/foreign_session_helper.h",
"android/framebust_intervention/framebust_blocked_delegate_android.cc",
@@ -2723,7 +2794,6 @@ static_library("browser") {
"android/httpclient/http_client_bridge.cc",
"android/httpclient/http_client_bridge.h",
"android/intent_handler.cc",
- "android/javascript/web_context_fetcher.cc",
"android/locale/locale_manager.cc",
"android/locale/locale_manager.h",
"android/locale/locale_template_url_loader.cc",
@@ -2770,6 +2840,17 @@ static_library("browser") {
"android/partner_browser_customizations.cc",
"android/partner_browser_customizations.h",
"android/permissions/permission_settings_bridge.cc",
+ "android/persisted_tab_data/leveldb_persisted_tab_data_storage_android.cc",
+ "android/persisted_tab_data/leveldb_persisted_tab_data_storage_android.h",
+ "android/persisted_tab_data/leveldb_persisted_tab_data_storage_android_factory.cc",
+ "android/persisted_tab_data/leveldb_persisted_tab_data_storage_android_factory.h",
+ "android/persisted_tab_data/persisted_tab_data_android.cc",
+ "android/persisted_tab_data/persisted_tab_data_android.h",
+ "android/persisted_tab_data/persisted_tab_data_config_android.cc",
+ "android/persisted_tab_data/persisted_tab_data_config_android.h",
+ "android/persisted_tab_data/persisted_tab_data_storage_android.h",
+ "android/persisted_tab_data/sensitivity_persisted_tab_data_android.cc",
+ "android/persisted_tab_data/sensitivity_persisted_tab_data_android.h",
"android/policy/policy_auditor.cc",
"android/policy/policy_auditor_bridge.cc",
"android/policy/policy_auditor_bridge.h",
@@ -2987,8 +3068,9 @@ static_library("browser") {
"feed/android/feed_reliability_logging_bridge.h",
"feed/android/feed_service_bridge.cc",
"feed/android/feed_service_bridge.h",
- "feed/android/feed_stream.cc",
- "feed/android/feed_stream.h",
+ "feed/android/feed_surface_renderer_bridge.cc",
+ "feed/android/feed_surface_renderer_bridge.h",
+ "feed/android/feed_surface_scope_dependency_provider_impl.cc",
"feed/android/jni_translation.cc",
"feed/android/jni_translation.h",
"feed/android/refresh_task_scheduler_impl.cc",
@@ -3187,6 +3269,8 @@ static_library("browser") {
"search_resumption/start_suggest_service_factory.h",
"segmentation_platform/android/contextual_page_action_controller_android.cc",
"segmentation_platform/android/segmentation_platform_service_factory_android.cc",
+ "segmentation_platform/client_util/tab_data_collection_util.cc",
+ "segmentation_platform/client_util/tab_data_collection_util.h",
"segmentation_platform/default_model/chrome_start_model_android.cc",
"segmentation_platform/default_model/chrome_start_model_android.h",
"segmentation_platform/default_model/chrome_start_model_android_v2.cc",
@@ -3220,6 +3304,8 @@ static_library("browser") {
"sync/glue/synced_window_delegate_android.h",
"sync/glue/synced_window_delegates_getter_android.cc",
"sync/glue/synced_window_delegates_getter_android.h",
+ "sync/glue/web_contents_state_synced_tab_delegate.cc",
+ "sync/glue/web_contents_state_synced_tab_delegate.h",
"tab/web_contents_state.cc",
"tab/web_contents_state.h",
"trusted_vault/trusted_vault_client_android.cc",
@@ -3247,6 +3333,8 @@ static_library("browser") {
"usb/android/web_usb_chooser_android.h",
"webapps/web_app_offline_android.cc",
"webauthn/android/cable_module_android.cc",
+ "webauthn/android/cable_registration_state.cc",
+ "webauthn/android/cable_registration_state.h",
"webauthn/android/chrome_webauthn_client_android.cc",
"webauthn/android/chrome_webauthn_client_android.h",
"webauthn/android/webauthn_request_delegate_android.cc",
@@ -3260,6 +3348,7 @@ static_library("browser") {
":delta_file_proto",
":fast_checkout_funnels_proto",
":profile_token",
+ ":sensitivity_data_proto",
":usage_stats_proto",
"//cc/slim",
"//chrome/android:jni_headers",
@@ -3301,7 +3390,7 @@ static_library("browser") {
"//chrome/browser/password_manager:password_manager_buildflags",
"//chrome/browser/password_manager/android:backend_public",
"//chrome/browser/password_manager/android:jni_headers",
- "//chrome/browser/payments/android:jni_headers",
+ "//chrome/browser/password_manager/android:password_generation_utils",
"//chrome/browser/policy/android:android",
"//chrome/browser/policy/android:jni_headers",
"//chrome/browser/prefetch/android:jni_headers",
@@ -3313,7 +3402,6 @@ static_library("browser") {
"//chrome/browser/safe_browsing/android:safe_browsing_enums",
"//chrome/browser/safety_check/android",
"//chrome/browser/search_resumption:jni_headers",
- "//chrome/browser/share/android:jni_headers",
"//chrome/browser/share/core/crow:crow_configuration",
"//chrome/browser/signin/services/android:jni_headers",
"//chrome/browser/sync/android:jni_headers",
@@ -3330,7 +3418,6 @@ static_library("browser") {
"//components/bookmarks/common/android",
"//components/browser_ui/accessibility/android",
"//components/browser_ui/contacts_picker/android",
- "//components/browser_ui/modaldialog/android",
"//components/browser_ui/photo_picker/android",
"//components/browser_ui/share/android",
"//components/browser_ui/site_settings/android",
@@ -3358,6 +3445,7 @@ static_library("browser") {
"//components/embedder_support/android:util",
"//components/embedder_support/android:web_contents_delegate",
"//components/endpoint_fetcher:endpoint_fetcher",
+ "//components/environment_integrity/android",
"//components/external_intents/android",
"//components/favicon/android",
"//components/favicon/core:database",
@@ -3428,6 +3516,7 @@ static_library("browser") {
"//third_party/libaddressinput:util",
"//third_party/libphonenumber",
"//third_party/smhasher:murmurhash2",
+ "//ui/accessibility",
"//url:gurl_android",
"//url:origin_android",
]
@@ -3448,8 +3537,6 @@ static_library("browser") {
sources += [
"supervised_user/android/website_parent_approval.cc",
"supervised_user/android/website_parent_approval.h",
- "supervised_user/child_accounts/child_account_feedback_reporter_android.cc",
- "supervised_user/child_accounts/child_account_feedback_reporter_android.h",
"supervised_user/child_accounts/child_account_service_android.cc",
"supervised_user/child_accounts/child_account_service_android.h",
]
@@ -3477,8 +3564,6 @@ static_library("browser") {
"accessibility/invert_bubble_prefs.h",
"accessibility/live_translate_controller_factory.cc",
"accessibility/live_translate_controller_factory.h",
- "apps/intent_helper/apps_navigation_throttle.cc",
- "apps/intent_helper/apps_navigation_throttle.h",
"apps/intent_helper/apps_navigation_types.cc",
"apps/intent_helper/apps_navigation_types.h",
"apps/intent_helper/intent_picker_auto_display_prefs.cc",
@@ -3491,8 +3576,6 @@ static_library("browser") {
"apps/intent_helper/intent_picker_helpers.h",
"apps/intent_helper/intent_picker_internal.cc",
"apps/intent_helper/intent_picker_internal.h",
- "apps/intent_helper/page_transition_util.cc",
- "apps/intent_helper/page_transition_util.h",
"autocomplete/tab_matcher_desktop.cc",
"autocomplete/tab_matcher_desktop.h",
"background/background_contents.cc",
@@ -3552,6 +3635,8 @@ static_library("browser") {
"content_settings/generated_cookie_prefs.h",
"content_settings/generated_notification_pref.cc",
"content_settings/generated_notification_pref.h",
+ "content_settings/generated_permission_prompting_behavior_pref.cc",
+ "content_settings/generated_permission_prompting_behavior_pref.h",
"content_settings/one_time_permission_provider.cc",
"content_settings/one_time_permission_provider.h",
"device_api/device_attribute_api.cc",
@@ -3648,7 +3733,6 @@ static_library("browser") {
"enterprise/connectors/analysis/analysis_settings.h",
"enterprise/connectors/analysis/content_analysis_delegate.cc",
"enterprise/connectors/analysis/content_analysis_delegate.h",
- "enterprise/connectors/analysis/content_analysis_delegate_base.cc",
"enterprise/connectors/analysis/content_analysis_delegate_base.h",
"enterprise/connectors/analysis/content_analysis_dialog.cc",
"enterprise/connectors/analysis/content_analysis_dialog.h",
@@ -3791,6 +3875,12 @@ static_library("browser") {
"lifetime/application_lifetime_desktop.h",
"lifetime/browser_close_manager.cc",
"lifetime/browser_close_manager.h",
+ "manta/manta_service.cc",
+ "manta/manta_service.h",
+ "manta/manta_service_factory.cc",
+ "manta/manta_service_factory.h",
+ "manta/snapper_provider.cc",
+ "manta/snapper_provider.h",
"media/capture_access_handler_base.cc",
"media/capture_access_handler_base.h",
"media/unified_autoplay_config.cc",
@@ -3918,6 +4008,8 @@ static_library("browser") {
"new_tab_page/modules/feed/feed_handler.h",
"new_tab_page/modules/history_clusters/cart/cart_processor.cc",
"new_tab_page/modules/history_clusters/cart/cart_processor.h",
+ "new_tab_page/modules/history_clusters/discount/discount_processor.cc",
+ "new_tab_page/modules/history_clusters/discount/discount_processor.h",
"new_tab_page/modules/history_clusters/history_clusters_module_service.cc",
"new_tab_page/modules/history_clusters/history_clusters_module_service.h",
"new_tab_page/modules/history_clusters/history_clusters_module_service_factory.cc",
@@ -3954,6 +4046,8 @@ static_library("browser") {
"new_tab_page/modules/safe_browsing/safe_browsing_handler.h",
"new_tab_page/modules/safe_browsing/safe_browsing_prefs.cc",
"new_tab_page/modules/safe_browsing/safe_browsing_prefs.h",
+ "new_tab_page/modules/v2/history_clusters/history_clusters_page_handler_v2.cc",
+ "new_tab_page/modules/v2/history_clusters/history_clusters_page_handler_v2.h",
"new_tab_page/new_tab_page_util.cc",
"new_tab_page/new_tab_page_util.h",
"new_tab_page/one_google_bar/one_google_bar_data.cc",
@@ -3982,8 +4076,6 @@ static_library("browser") {
"obsolete_system/obsolete_system.h",
"page_info/about_this_site_side_panel_throttle.cc",
"page_info/about_this_site_side_panel_throttle.h",
- "page_info/about_this_site_tab_helper.cc",
- "page_info/about_this_site_tab_helper.h",
"password_manager/generated_password_leak_detection_pref.cc",
"password_manager/generated_password_leak_detection_pref.h",
"password_manager/web_app_profile_switcher.cc",
@@ -4010,14 +4102,14 @@ static_library("browser") {
"performance_manager/policies/page_discarding_helper.h",
"performance_manager/policies/urgent_page_discarding_policy.cc",
"performance_manager/policies/urgent_page_discarding_policy.h",
+ "performance_manager/public/user_tuning/battery_saver_mode_manager.h",
"performance_manager/public/user_tuning/user_performance_tuning_manager.h",
+ "performance_manager/user_tuning/battery_saver_mode_manager.cc",
"performance_manager/user_tuning/profile_discard_opt_out_list_helper.cc",
"performance_manager/user_tuning/profile_discard_opt_out_list_helper.h",
"performance_manager/user_tuning/user_performance_tuning_manager.cc",
"performance_manager/user_tuning/user_performance_tuning_notifier.cc",
"performance_manager/user_tuning/user_performance_tuning_notifier.h",
- "permissions/attestation_permission_request.cc",
- "permissions/attestation_permission_request.h",
"permissions/one_time_permissions_tracker.cc",
"permissions/one_time_permissions_tracker.h",
"permissions/one_time_permissions_tracker_factory.cc",
@@ -4025,6 +4117,8 @@ static_library("browser") {
"permissions/one_time_permissions_tracker_helper.cc",
"permissions/one_time_permissions_tracker_helper.h",
"permissions/one_time_permissions_tracker_observer.h",
+ "picture_in_picture/auto_picture_in_picture_tab_helper.cc",
+ "picture_in_picture/auto_picture_in_picture_tab_helper.h",
"policy/browsing_history_policy_handler.cc",
"policy/browsing_history_policy_handler.h",
"policy/developer_tools_policy_handler.cc",
@@ -4037,8 +4131,6 @@ static_library("browser") {
"policy/webhid_device_policy_handler.h",
"preloading/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.cc",
"preloading/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper.h",
- "preloading/preloading_features.cc",
- "preloading/preloading_features.h",
"private_network_access/chrome_private_network_device_chooser.cc",
"private_network_access/chrome_private_network_device_chooser.h",
"private_network_access/chrome_private_network_device_chooser_desktop.cc",
@@ -4240,6 +4332,8 @@ static_library("browser") {
"support_tool/policy_data_collector.h",
"support_tool/screenshot_data_collector.cc",
"support_tool/screenshot_data_collector.h",
+ "support_tool/signin_data_collector.cc",
+ "support_tool/signin_data_collector.h",
"support_tool/support_packet_metadata.cc",
"support_tool/support_packet_metadata.h",
"support_tool/support_tool_handler.cc",
@@ -4370,6 +4464,12 @@ static_library("browser") {
"upgrade_detector/upgrade_detector.cc",
"upgrade_detector/upgrade_detector.h",
"upgrade_detector/upgrade_observer.h",
+ "usb/usb_connection_tracker.cc",
+ "usb/usb_connection_tracker.h",
+ "usb/usb_connection_tracker_factory.cc",
+ "usb/usb_connection_tracker_factory.h",
+ "usb/usb_system_tray_icon.cc",
+ "usb/usb_system_tray_icon.h",
"usb/web_usb_chooser_desktop.cc",
"usb/web_usb_chooser_desktop.h",
"usb/web_usb_detector.cc",
@@ -4408,13 +4508,12 @@ static_library("browser") {
"//chrome/app/theme:chrome_unscaled_resources_grit",
"//chrome/app/vector_icons",
"//chrome/browser/apps/app_service",
+ "//chrome/browser/apps/link_capturing",
"//chrome/browser/cart:mojo_bindings",
"//chrome/browser/companion/visual_search",
"//chrome/browser/enterprise/connectors/analysis:features",
"//chrome/browser/enterprise/connectors/device_trust:prefs",
- "//chrome/browser/enterprise/data_controls",
"//chrome/browser/enterprise/signals:utils",
- "//chrome/browser/first_party_sets",
"//chrome/browser/image_editor:image_editor_component_util",
"//chrome/browser/media/router/discovery/access_code:access_code_sink_service",
"//chrome/browser/new_tab_page/chrome_colors:generate_chrome_colors_info",
@@ -4423,9 +4522,11 @@ static_library("browser") {
"//chrome/browser/new_tab_page/modules/feed:mojo_bindings",
"//chrome/browser/new_tab_page/modules/history_clusters:mojo_bindings",
"//chrome/browser/new_tab_page/modules/history_clusters/cart:mojo_bindings",
+ "//chrome/browser/new_tab_page/modules/history_clusters/discount:mojo_bindings",
"//chrome/browser/new_tab_page/modules/photos:mojo_bindings",
"//chrome/browser/new_tab_page/modules/recipes:mojo_bindings",
"//chrome/browser/new_tab_page/modules/safe_browsing:mojo_bindings",
+ "//chrome/browser/new_tab_page/modules/v2/history_clusters:mojo_bindings",
"//chrome/browser/policy:path_parser",
"//chrome/browser/profile_resetter:profile_reset_report_proto",
"//chrome/browser/resource_coordinator:intervention_policy_database_proto",
@@ -4455,11 +4556,13 @@ static_library("browser") {
"//components/app_constants",
"//components/commerce/core:cart_db_content_proto",
"//components/commerce/core:coupon_db_content_proto",
+ "//components/commerce/core:discounts_db_content_proto",
"//components/commerce/core:proto",
"//components/commerce/core:public",
"//components/commerce/core/mojom:mojo_bindings",
"//components/constrained_window",
"//components/endpoint_fetcher:endpoint_fetcher",
+ "//components/enterprise/buildflags",
"//components/feedback",
"//components/feedback/content:factory",
"//components/feedback/redaction_tool",
@@ -4515,6 +4618,8 @@ static_library("browser") {
"device_notifications/device_status_icon_renderer.h",
"hid/hid_status_icon.cc",
"hid/hid_status_icon.h",
+ "usb/usb_status_icon.cc",
+ "usb/usb_status_icon.h",
]
}
if (!is_chromeos) {
@@ -4539,6 +4644,8 @@ static_library("browser") {
"enterprise/remote_commands/cbcm_remote_commands_factory.h",
"enterprise/remote_commands/clear_browsing_data_job.cc",
"enterprise/remote_commands/clear_browsing_data_job.h",
+ "enterprise/remote_commands/job_profile_picker.cc",
+ "enterprise/remote_commands/job_profile_picker.h",
"first_run/upgrade_util.cc",
"first_run/upgrade_util.h",
"lifetime/switch_utils.cc",
@@ -4670,41 +4777,6 @@ static_library("browser") {
if (is_chromeos_ash) {
assert(enable_system_notifications)
sources += [
- "apps/app_deduplication_service/app_deduplication_cache.cc",
- "apps/app_deduplication_service/app_deduplication_cache.h",
- "apps/app_deduplication_service/app_deduplication_mapper.cc",
- "apps/app_deduplication_service/app_deduplication_mapper.h",
- "apps/app_deduplication_service/app_deduplication_server_connector.cc",
- "apps/app_deduplication_service/app_deduplication_server_connector.h",
- "apps/app_deduplication_service/app_deduplication_service.cc",
- "apps/app_deduplication_service/app_deduplication_service.h",
- "apps/app_deduplication_service/app_deduplication_service_factory.cc",
- "apps/app_deduplication_service/app_deduplication_service_factory.h",
- "apps/app_deduplication_service/duplicate_group.cc",
- "apps/app_deduplication_service/duplicate_group.h",
- "apps/app_deduplication_service/entry_types.cc",
- "apps/app_deduplication_service/entry_types.h",
- "apps/app_discovery_service/app_discovery_service.cc",
- "apps/app_discovery_service/app_discovery_service.h",
- "apps/app_discovery_service/app_discovery_service_factory.cc",
- "apps/app_discovery_service/app_discovery_service_factory.h",
- "apps/app_discovery_service/app_discovery_util.h",
- "apps/app_discovery_service/app_fetcher_manager.cc",
- "apps/app_discovery_service/app_fetcher_manager.h",
- "apps/app_discovery_service/game_extras.cc",
- "apps/app_discovery_service/game_extras.h",
- "apps/app_discovery_service/game_fetcher.cc",
- "apps/app_discovery_service/game_fetcher.h",
- "apps/app_discovery_service/play_extras.cc",
- "apps/app_discovery_service/play_extras.h",
- "apps/app_discovery_service/recommended_arc_app_fetcher.cc",
- "apps/app_discovery_service/recommended_arc_app_fetcher.h",
- "apps/app_discovery_service/result.cc",
- "apps/app_discovery_service/result.h",
- "apps/app_discovery_service/test_fetcher.cc",
- "apps/app_discovery_service/test_fetcher.h",
- "apps/app_provisioning_service/app_provisioning_data_manager.cc",
- "apps/app_provisioning_service/app_provisioning_data_manager.h",
"apps/digital_goods/digital_goods_ash.cc",
"apps/digital_goods/digital_goods_ash.h",
"apps/digital_goods/digital_goods_factory_impl.cc",
@@ -4731,6 +4803,8 @@ static_library("browser") {
"enterprise/connectors/analysis/file_transfer_analysis_delegate.h",
"enterprise/connectors/analysis/source_destination_matcher_ash.cc",
"enterprise/connectors/analysis/source_destination_matcher_ash.h",
+ "enterprise/connectors/device_trust/ash/ash_attestation_policy_observer.cc",
+ "enterprise/connectors/device_trust/ash/ash_attestation_policy_observer.h",
"enterprise/connectors/device_trust/signals/ash/ash_signals_filterer.cc",
"enterprise/connectors/device_trust/signals/ash/ash_signals_filterer.h",
"enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator.cc",
@@ -4739,6 +4813,8 @@ static_library("browser") {
"enterprise/reporting/android_app_info_generator.h",
"feedback/system_logs/log_sources/lacros_log_files_log_source.cc",
"feedback/system_logs/log_sources/lacros_log_files_log_source.h",
+ "file_system_access/cloud_identifier/cloud_identifier_util_ash.cc",
+ "file_system_access/cloud_identifier/cloud_identifier_util_ash.h",
"icon_loader_chromeos.cc",
"lifetime/application_lifetime_chromeos.cc",
"lifetime/application_lifetime_chromeos.h",
@@ -4805,6 +4881,8 @@ static_library("browser") {
"metrics/structured/ash_structured_metrics_recorder.h",
"metrics/structured/cros_events_processor.cc",
"metrics/structured/cros_events_processor.h",
+ "metrics/structured/key_data_provider_ash.cc",
+ "metrics/structured/key_data_provider_ash.h",
"metrics/structured/metadata_processor_ash.cc",
"metrics/structured/metadata_processor_ash.h",
"metrics/structured/structured_metrics_key_events_observer.cc",
@@ -4865,12 +4943,14 @@ static_library("browser") {
"nearby_sharing/nearby_share_feature_status.h",
"nearby_sharing/nearby_share_feature_usage_metrics.cc",
"nearby_sharing/nearby_share_feature_usage_metrics.h",
- "nearby_sharing/nearby_share_metrics_logger.cc",
- "nearby_sharing/nearby_share_metrics_logger.h",
+ "nearby_sharing/nearby_share_metrics.cc",
+ "nearby_sharing/nearby_share_metrics.h",
"nearby_sharing/nearby_share_profile_info_provider_impl.cc",
"nearby_sharing/nearby_share_profile_info_provider_impl.h",
"nearby_sharing/nearby_share_settings.cc",
"nearby_sharing/nearby_share_settings.h",
+ "nearby_sharing/nearby_share_transfer_profiler.cc",
+ "nearby_sharing/nearby_share_transfer_profiler.h",
"nearby_sharing/nearby_sharing_service.h",
"nearby_sharing/nearby_sharing_service_factory.cc",
"nearby_sharing/nearby_sharing_service_factory.h",
@@ -4970,6 +5050,8 @@ static_library("browser") {
"sharesheet/sharesheet_types.h",
"sharesheet/sharesheet_ui_delegate.h",
"shell_integration_chromeos.cc",
+ "signin/wait_for_network_callback_helper_ash.cc",
+ "signin/wait_for_network_callback_helper_ash.h",
"speech/cros_speech_recognition_service.cc",
"speech/cros_speech_recognition_service.h",
"speech/cros_speech_recognition_service_factory.cc",
@@ -5094,9 +5176,10 @@ static_library("browser") {
"//ash/webui/system_extensions_internals_ui/mojom",
"//build:chromeos_buildflags",
"//chrome/app/theme:chrome_unscaled_resources_grit",
- "//chrome/browser/apps/app_deduplication_service/proto",
+ "//chrome/browser/apps/app_deduplication_service",
+ "//chrome/browser/apps/app_discovery_service",
"//chrome/browser/apps/app_preload_service",
- "//chrome/browser/apps/app_provisioning_service/proto",
+ "//chrome/browser/apps/app_provisioning_service",
"//chrome/browser/ash",
"//chrome/browser/ash/crosapi",
"//chrome/browser/ash/power/ml/smart_dim",
@@ -5126,6 +5209,7 @@ static_library("browser") {
"//chrome/browser/ui/quick_answers",
"//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings",
"//chrome/browser/ui/webui/ash/audio:mojo_bindings",
+ "//chrome/browser/ui/webui/ash/borealis_installer:mojo_bindings",
"//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings",
"//chrome/browser/ui/webui/ash/crostini_installer:mojo_bindings",
"//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings",
@@ -5134,12 +5218,12 @@ static_library("browser") {
"//chrome/browser/ui/webui/ash/manage_mirrorsync:mojo_bindings",
"//chrome/browser/ui/webui/ash/office_fallback:mojo_bindings",
"//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
+ "//chrome/browser/ui/webui/ash/settings/search/mojom",
"//chrome/browser/ui/webui/ash/vm:mojo_bindings",
"//chrome/browser/ui/webui/nearby_share:mojom",
"//chrome/browser/ui/webui/settings/ash/files_page/mojom",
"//chrome/browser/ui/webui/settings/ash/input_device_settings:mojom",
"//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom",
- "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings",
"//chrome/services/sharing/public/cpp",
"//chrome/services/sharing/public/proto",
"//chrome/services/speech:lib",
@@ -5167,6 +5251,7 @@ static_library("browser") {
"//chromeos/ash/components/local_search_service",
"//chromeos/ash/components/local_search_service/public/cpp",
"//chromeos/ash/components/local_search_service/public/mojom",
+ "//chromeos/ash/components/login/hibernate:hibernate_manager",
"//chromeos/ash/components/login/login_state",
"//chromeos/ash/components/login/session",
"//chromeos/ash/components/memory",
@@ -5218,7 +5303,6 @@ static_library("browser") {
"//chromeos/strings",
"//chromeos/ui/base",
"//chromeos/ui/vector_icons",
- "//chromeos/version",
"//components/account_manager_core",
"//components/app_constants",
"//components/app_restore",
@@ -5349,6 +5433,8 @@ static_library("browser") {
"lacros/app_mode/device_local_account_extension_installer_lacros.h",
"lacros/app_mode/kiosk_session_service_lacros.cc",
"lacros/app_mode/kiosk_session_service_lacros.h",
+ "lacros/app_mode/web_kiosk_installer_lacros.cc",
+ "lacros/app_mode/web_kiosk_installer_lacros.h",
"lacros/arc/arc_icon_cache.cc",
"lacros/arc/arc_icon_cache.h",
"lacros/arc/arc_intent_helper_mojo_lacros.cc",
@@ -5403,6 +5489,8 @@ static_library("browser") {
"lacros/geolocation/system_geolocation_source_lacros.h",
"lacros/identity_manager_lacros.cc",
"lacros/identity_manager_lacros.h",
+ "lacros/lacros_apps_publisher.cc",
+ "lacros/lacros_apps_publisher.h",
"lacros/lacros_extension_apps_controller.cc",
"lacros/lacros_extension_apps_controller.h",
"lacros/lacros_extension_apps_publisher.cc",
@@ -5505,6 +5593,8 @@ static_library("browser") {
"ui/lacros/float_controller_lacros.h",
"ui/lacros/immersive_context_lacros.cc",
"ui/lacros/immersive_context_lacros.h",
+ "ui/lacros/screen_capture_notification_ui_lacros.cc",
+ "ui/lacros/screen_capture_notification_ui_lacros.h",
"ui/lacros/snap_controller_lacros.cc",
"ui/lacros/snap_controller_lacros.h",
"ui/lacros/window_properties.cc",
@@ -5519,6 +5609,7 @@ static_library("browser") {
"//chrome/browser/chromeos/extensions/wm",
"//chrome/browser/chromeos/launcher_search:search_util",
"//chrome/browser/chromeos/office_web_app",
+ "//chrome/browser/lacros/cros_apps/api/diagnostics",
"//chrome/common/chromeos/extensions",
"//chromeos/crosapi/cpp",
"//chromeos/crosapi/cpp:crosapi_constants",
@@ -5549,10 +5640,10 @@ static_library("browser") {
if (is_chromeos) {
sources += [
+ "apps/intent_helper/chromeos_disabled_apps_throttle.cc",
+ "apps/intent_helper/chromeos_disabled_apps_throttle.h",
"apps/intent_helper/chromeos_intent_picker_helpers.cc",
"apps/intent_helper/chromeos_intent_picker_helpers.h",
- "apps/intent_helper/common_apps_navigation_throttle.cc",
- "apps/intent_helper/common_apps_navigation_throttle.h",
"apps/intent_helper/metrics/intent_handling_metrics.cc",
"apps/intent_helper/metrics/intent_handling_metrics.h",
"apps/intent_helper/supported_links_infobar_delegate.cc",
@@ -5589,6 +5680,8 @@ static_library("browser") {
"download/notification/download_item_notification.h",
"download/notification/download_notification_manager.cc",
"download/notification/download_notification_manager.h",
+ "file_system_access/cloud_identifier/cloud_identifier_util_cros.cc",
+ "file_system_access/cloud_identifier/cloud_identifier_util_cros.h",
"hid/hid_pinned_notification.cc",
"hid/hid_pinned_notification.h",
"media/platform_verification_chromeos.cc",
@@ -5612,22 +5705,25 @@ static_library("browser") {
"renderer_context_menu/quick_answers_menu_observer.h",
"speech/tts_crosapi_util.cc",
"speech/tts_crosapi_util.h",
+ "usb/usb_pinned_notification.cc",
+ "usb/usb_pinned_notification.h",
"webshare/chromeos/sharesheet_client.cc",
"webshare/chromeos/sharesheet_client.h",
]
deps += [
"//ash/webui/projector_app/public/cpp",
+ "//ash/webui/settings/public/constants",
"//chrome/browser/chromeos",
"//chrome/browser/chromeos/drivefs",
"//chrome/browser/chromeos/extensions/telemetry",
"//chrome/browser/policy:onc",
"//chrome/browser/ui/quick_answers",
- "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
"//chrome/browser/webshare:storage",
"//chrome/common/chromeos/extensions",
"//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_browser",
"//chromeos/components/certificate_provider:certificate_provider",
"//chromeos/components/disks:prefs",
+ "//chromeos/components/kiosk",
"//chromeos/components/onc:onc",
"//chromeos/components/quick_answers:quick_answers",
"//chromeos/components/quick_answers/public/cpp:cpp",
@@ -5643,6 +5739,7 @@ static_library("browser") {
"//chromeos/dbus/power",
"//chromeos/ui/clipboard_history",
"//chromeos/ui/wm",
+ "//chromeos/version",
"//components/app_constants",
"//components/arc/common",
"//components/arc/common:arc_intent_helper_constants",
@@ -5670,6 +5767,7 @@ static_library("browser") {
}
if (is_mac) {
sources += [
+ "webauthn/chrome_authenticator_request_delegate_mac.mm",
"webauthn/local_credential_management_mac.cc",
"webauthn/local_credential_management_mac.h",
]
@@ -5843,7 +5941,6 @@ static_library("browser") {
public_deps += [
":titlebar_config",
"//build:branding_buildflags",
- "//chrome/browser/chrome_for_testing:buildflags",
"//chrome/services/util_win/public/mojom",
"//ui/views",
"//ui/views/controls/webview",
@@ -5953,10 +6050,6 @@ static_library("browser") {
}
}
- if (is_apple) {
- configs += [ "//build/config/compiler:enable_arc" ]
- }
-
if (is_mac) {
sources += [
"accessibility/caption_settings_dialog.h",
@@ -6018,6 +6111,8 @@ static_library("browser") {
"media/webrtc/system_media_capture_permissions_mac.mm",
"media/webrtc/system_media_capture_permissions_stats_mac.h",
"media/webrtc/system_media_capture_permissions_stats_mac.mm",
+ "media/webrtc/thumbnail_capturer_mac.h",
+ "media/webrtc/thumbnail_capturer_mac.mm",
"media/webrtc/window_icon_util_mac.mm",
"media_galleries/mac/mtp_device_delegate_impl_mac.h",
"media_galleries/mac/mtp_device_delegate_impl_mac.mm",
@@ -6057,7 +6152,6 @@ static_library("browser") {
"//chrome/app_shim",
"//chrome/browser/apps/app_shim",
"//chrome/browser/renderer_host:history_swiper",
- "//chrome/browser/ui:ui_arc",
"//chrome/browser/updater:browser_updater_client",
"//chrome/browser/updater:scheduler",
"//chrome/browser/webshare:storage",
@@ -6074,14 +6168,12 @@ static_library("browser") {
"//third_party/crashpad/crashpad/client",
"//third_party/google_toolbox_for_mac",
]
- allow_circular_includes_from += [
- "//chrome/browser/apps/app_shim",
- "//chrome/browser/ui:ui_arc",
- ]
+ allow_circular_includes_from += [ "//chrome/browser/apps/app_shim" ]
frameworks = [
"Accelerate.framework",
"AudioUnit.framework",
"AVFoundation.framework",
+ "AuthenticationServices.framework",
"DiskArbitration.framework",
"IOKit.framework",
"ImageCaptureCore.framework",
@@ -6090,11 +6182,11 @@ static_library("browser") {
"QuartzCore.framework",
"SafariServices.framework",
"SecurityInterface.framework",
+ "UserNotifications.framework",
]
weak_frameworks = [
- "AuthenticationServices.framework", # macOS 10.15
+ "ScreenCaptureKit.framework", # Available in macOS 12.3, stable in 13.2.
"UniformTypeIdentifiers.framework", # macOS 11
- "UserNotifications.framework", # macOS 10.14
]
public_deps += [ "//chrome/browser/mac:keystone_glue" ]
}
@@ -6127,6 +6219,10 @@ static_library("browser") {
}
}
+ if (enterprise_data_controls) {
+ deps += [ "//chrome/browser/enterprise/data_controls" ]
+ }
+
if (is_linux || is_win || is_mac || is_chromeos_ash) {
sources += [
"enterprise/connectors/device_trust/consent_policy_observer.cc",
@@ -6179,15 +6275,12 @@ static_library("browser") {
"enterprise/idle/action.h",
"enterprise/idle/action_runner.cc",
"enterprise/idle/action_runner.h",
- "enterprise/idle/idle_features.cc",
- "enterprise/idle/idle_features.h",
"enterprise/idle/idle_service.cc",
"enterprise/idle/idle_service.h",
"enterprise/idle/idle_service_factory.cc",
"enterprise/idle/idle_service_factory.h",
- "enterprise/idle/idle_timeout_policy_handler.cc",
- "enterprise/idle/idle_timeout_policy_handler.h",
]
+ deps += [ "//components/enterprise/idle" ]
}
if (is_linux || is_win || is_mac) {
@@ -6878,14 +6971,25 @@ static_library("browser") {
"signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.h",
"signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.cc",
"signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.h",
+ "signin/bound_session_credentials/bound_session_params_storage.cc",
+ "signin/bound_session_credentials/bound_session_params_storage.h",
+ "signin/bound_session_credentials/bound_session_params_util.cc",
+ "signin/bound_session_credentials/bound_session_params_util.h",
"signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.cc",
"signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h",
"signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc",
"signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h",
+ "signin/bound_session_credentials/bound_session_registration_fetcher.h",
+ "signin/bound_session_credentials/bound_session_registration_fetcher_impl.cc",
+ "signin/bound_session_credentials/bound_session_registration_fetcher_impl.h",
+ "signin/bound_session_credentials/bound_session_registration_fetcher_param.cc",
+ "signin/bound_session_credentials/bound_session_registration_fetcher_param.h",
"signin/bound_session_credentials/bound_session_request_throttled_listener_browser_impl.cc",
"signin/bound_session_credentials/bound_session_request_throttled_listener_browser_impl.h",
"signin/bound_session_credentials/registration_token_helper.cc",
"signin/bound_session_credentials/registration_token_helper.h",
+ "signin/bound_session_credentials/session_binding_helper.cc",
+ "signin/bound_session_credentials/session_binding_helper.h",
"signin/bound_session_credentials/unexportable_key_service_factory.cc",
"signin/bound_session_credentials/unexportable_key_service_factory.h",
]
@@ -7154,6 +7258,7 @@ static_library("browser") {
]
deps += [
"//components/cdm/common",
+ "//media/cdm:cdm_paths",
"//third_party/widevine/cdm:headers",
]
}
@@ -7484,10 +7589,7 @@ static_library("browser") {
"sessions/tab_loader_delegate.cc",
"sessions/tab_loader_delegate.h",
]
- deps += [
- "//chromeos/components/kiosk",
- "//components/tab_groups",
- ]
+ deps += [ "//components/tab_groups" ]
}
if (enable_spellcheck) {
@@ -7539,10 +7641,6 @@ static_library("browser") {
if (enable_supervised_users) {
sources += [
- "content_settings/content_settings_supervised_provider.cc",
- "content_settings/content_settings_supervised_provider.h",
- "supervised_user/child_accounts/child_account_service.cc",
- "supervised_user/child_accounts/child_account_service.h",
"supervised_user/child_accounts/child_account_service_factory.cc",
"supervised_user/child_accounts/child_account_service_factory.h",
"supervised_user/child_accounts/list_family_members_service_factory.cc",
@@ -7553,8 +7651,6 @@ static_library("browser") {
"supervised_user/chrome_supervised_user_web_content_handler_base.h",
"supervised_user/kids_chrome_management/kids_chrome_management_client_factory.cc",
"supervised_user/kids_chrome_management/kids_chrome_management_client_factory.h",
- "supervised_user/kids_chrome_management/kids_management_service.cc",
- "supervised_user/kids_chrome_management/kids_management_service.h",
"supervised_user/kids_chrome_management/kids_profile_manager.cc",
"supervised_user/kids_chrome_management/kids_profile_manager.h",
"supervised_user/supervised_user_browser_utils.cc",
@@ -7599,7 +7695,6 @@ static_library("browser") {
"//components/supervised_user/core/browser:fetcher",
"//components/supervised_user/core/browser:list_family_members_service",
"//components/supervised_user/core/browser:supervised_user_preferences",
- "//components/supervised_user/core/browser/proto",
"//components/supervised_user/core/common",
]
}
@@ -7767,6 +7862,10 @@ if (is_android) {
sources = [ "fast_checkout/fast_checkout_funnels.proto" ]
}
+ proto_library("sensitivity_data_proto") {
+ sources = [ "android/persisted_tab_data/sensitivity_data.proto" ]
+ }
+
proto_library("profile_token") {
sources = [ "android/proto/profile_token.proto" ]
}
@@ -7855,13 +7954,17 @@ proto_library("lcp_critical_path_predictor_proto") {
]
}
+proto_library("get_proxy_config_proto") {
+ sources = [ "ip_protection/get_proxy_config.proto" ]
+}
+
proto_library("permissions_proto") {
sources = [ "permissions/crowd_deny.proto" ]
}
if (enable_bound_session_credentials) {
proto_library("bound_session_credentials_proto") {
- sources = [ "signin/bound_session_credentials/bound_session_registration_params.proto" ]
+ sources = [ "signin/bound_session_credentials/bound_session_params.proto" ]
}
}
@@ -7913,7 +8016,6 @@ grit("resources") {
"//chrome/browser/resources/chromeos/account_manager:css_wrapper_files",
"//chrome/browser/resources/chromeos/account_manager:html_wrapper_files",
"//chrome/browser/resources/chromeos/account_manager/components:html_wrapper_files",
- "//chrome/browser/resources/chromeos/add_supervision:web_components",
"//chrome/browser/resources/chromeos/arc_account_picker:web_components",
"//chrome/browser/resources/chromeos/crostini_installer:html_wrapper_files",
"//chrome/browser/resources/chromeos/crostini_upgrader:html_wrapper_files",
@@ -7927,7 +8029,6 @@ grit("resources") {
"//chrome/browser/resources/chromeos/set_time_dialog:html_wrapper_files",
"//chrome/browser/resources/chromeos/smb_shares:web_components",
"//chrome/browser/resources/chromeos/vm:web_components",
- "//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings_js",
"//chrome/browser/ui/webui/ash/crostini_installer:mojo_bindings_js",
"//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings_js",
"//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_js",
@@ -8167,7 +8268,7 @@ static_library("test_support") {
]
deps += [
"//chrome/browser/apps/app_service",
- "//components/services/app_service/public/cpp:preferred_apps",
+ "//components/services/app_service",
]
}
@@ -8315,15 +8416,8 @@ if (!is_android) {
deps = [
":browser",
":browser_process",
- "//chrome:browser_tests_pak",
"//chrome/browser/profiles:profile",
- "//chrome/common:mojo_bindings",
"//chrome/test/data/webui:web_ui_test_bindings",
- "//components/metrics:test_support",
- "//components/password_manager/core/browser:test_support",
- "//components/signin/public/identity_manager",
- "//components/sync:test_support",
- "//components/translate/content/common",
"//content/test:test_support",
]
@@ -8361,7 +8455,6 @@ if (is_chromeos_ash) {
":browser",
"//base",
"//components/exo/wayland:ui_controls_protocol_stub",
- "//components/exo/wayland:weston_test_stub",
"//third_party/protobuf:protobuf_lite",
]
}
diff --git a/chromium/chrome/browser/accessibility/DEPS b/chromium/chrome/browser/accessibility/DEPS
index 749bbd463c6..8a1d211a9a0 100644
--- a/chromium/chrome/browser/accessibility/DEPS
+++ b/chromium/chrome/browser/accessibility/DEPS
@@ -5,3 +5,9 @@ include_rules = [
"+testing/gmock",
"+testing/gtest",
]
+
+specific_include_rules = {
+ 'accessibility_extension_api_chromeos.cc': [
+ "+services/accessibility/public/mojom/assistive_technology_type.mojom.h",
+ ]
+} \ No newline at end of file
diff --git a/chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.cc b/chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.cc
index 492c24e6cdf..1da4f05e94a 100644
--- a/chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.cc
+++ b/chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.cc
@@ -14,6 +14,7 @@
#include "ash/public/cpp/accessibility_focus_ring_info.h"
#include "ash/public/cpp/event_rewriter_controller.h"
#include "ash/public/cpp/window_tree_host_lookup.h"
+#include "ash/webui/settings/public/constants/routes_util.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/json/json_writer.h"
@@ -30,7 +31,6 @@
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes_util.h"
#include "chrome/common/extensions/api/accessibility_private.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/webui_url_constants.h"
@@ -45,9 +45,11 @@
#include "extensions/browser/extension_system.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/manifest_handlers/background_info.h"
+#include "services/accessibility/public/mojom/assistive_technology_type.mojom.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/window_tree_host.h"
+#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
@@ -57,6 +59,7 @@
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/events/ozone/layout/keyboard_layout_engine.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/strings/grit/ui_strings.h"
#include "ui/wm/core/coordinate_conversion.h"
namespace {
@@ -64,6 +67,17 @@ namespace {
namespace accessibility_private = ::extensions::api::accessibility_private;
using ::ash::AccessibilityManager;
+ash::AccessibilityToastType ConvertToastType(
+ accessibility_private::ToastType type) {
+ switch (type) {
+ case accessibility_private::ToastType::
+ TOAST_TYPE_DICTATIONNOFOCUSEDTEXTFIELD:
+ return ash::AccessibilityToastType::kDictationNoFocusedTextField;
+ case accessibility_private::ToastType::TOAST_TYPE_NONE:
+ NOTREACHED_NORETURN();
+ }
+}
+
ash::DictationBubbleHintType ConvertDictationHintType(
accessibility_private::DictationBubbleHintType hint_type) {
switch (hint_type) {
@@ -263,14 +277,14 @@ AccessibilityPrivateIsFeatureEnabledFunction::Run() {
IsExperimentalAccessibilityDictationContextCheckingEnabled();
break;
case accessibility_private::AccessibilityFeature::
- ACCESSIBILITY_FEATURE_CHROMEVOXTABSDEPRECATION:
- enabled = ::features::IsAccessibilityDeprecateChromeVoxTabsEnabled();
- break;
- case accessibility_private::AccessibilityFeature::
ACCESSIBILITY_FEATURE_CHROMEVOXSETTINGSMIGRATION:
enabled = ::features::IsAccessibilityChromeVoxPageMigrationEnabled();
break;
case accessibility_private::AccessibilityFeature::
+ ACCESSIBILITY_FEATURE_GAMEFACEINTEGRATION:
+ enabled = ::features::IsAccessibilityGameFaceIntegrationEnabled();
+ break;
+ case accessibility_private::AccessibilityFeature::
ACCESSIBILITY_FEATURE_NONE:
return RespondNow(Error("Unrecognized feature"));
}
@@ -507,6 +521,37 @@ AccessibilityPrivateSetFocusRingsFunction::Run() {
auto* accessibility_manager = AccessibilityManager::Get();
+ ax::mojom::AssistiveTechnologyType at_type;
+ switch (params->at_type) {
+ case extensions::api::accessibility_private::ASSISTIVE_TECHNOLOGY_TYPE_NONE:
+ at_type = ax::mojom::AssistiveTechnologyType::kUnknown;
+ break;
+ case extensions::api::accessibility_private::
+ ASSISTIVE_TECHNOLOGY_TYPE_CHROMEVOX:
+ at_type = ax::mojom::AssistiveTechnologyType::kChromeVox;
+ break;
+ case extensions::api::accessibility_private::
+ ASSISTIVE_TECHNOLOGY_TYPE_SELECTTOSPEAK:
+ at_type = ax::mojom::AssistiveTechnologyType::kSelectToSpeak;
+ break;
+ case extensions::api::accessibility_private::
+ ASSISTIVE_TECHNOLOGY_TYPE_SWITCHACCESS:
+ at_type = ax::mojom::AssistiveTechnologyType::kSwitchAccess;
+ break;
+ case extensions::api::accessibility_private::
+ ASSISTIVE_TECHNOLOGY_TYPE_AUTOCLICK:
+ at_type = ax::mojom::AssistiveTechnologyType::kAutoClick;
+ break;
+ case extensions::api::accessibility_private::
+ ASSISTIVE_TECHNOLOGY_TYPE_MAGNIFIER:
+ at_type = ax::mojom::AssistiveTechnologyType::kMagnifier;
+ break;
+ case extensions::api::accessibility_private::
+ ASSISTIVE_TECHNOLOGY_TYPE_DICTATION:
+ at_type = ax::mojom::AssistiveTechnologyType::kDictation;
+ break;
+ }
+
for (const accessibility_private::FocusRingInfo& focus_ring_info :
params->focus_rings) {
auto focus_ring = std::make_unique<ash::AccessibilityFocusRingInfo>();
@@ -520,7 +565,7 @@ AccessibilityPrivateSetFocusRingsFunction::Run() {
}
const std::string id = accessibility_manager->GetFocusRingId(
- extension_id(), focus_ring_info.id ? *(focus_ring_info.id) : "");
+ at_type, focus_ring_info.id ? *(focus_ring_info.id) : "");
if (!content::ParseHexColorString(focus_ring_info.color,
&(focus_ring->color))) {
@@ -748,6 +793,15 @@ AccessibilityPrivateSetVirtualKeyboardVisibleFunction::Run() {
return RespondNow(NoArguments());
}
+ExtensionFunction::ResponseAction AccessibilityPrivateShowToastFunction::Run() {
+ absl::optional<accessibility_private::ShowToast::Params> params(
+ accessibility_private::ShowToast::Params::Create(args()));
+ EXTENSION_FUNCTION_VALIDATE(params);
+ ash::AccessibilityController::Get()->ShowToast(
+ ConvertToastType(params->type));
+ return RespondNow(NoArguments());
+}
+
ExtensionFunction::ResponseAction
AccessibilityPrivateShowConfirmationDialogFunction::Run() {
absl::optional<accessibility_private::ShowConfirmationDialog::Params> params =
@@ -756,8 +810,11 @@ AccessibilityPrivateShowConfirmationDialogFunction::Run() {
std::u16string title = base::UTF8ToUTF16(params->title);
std::u16string description = base::UTF8ToUTF16(params->description);
+ std::u16string cancel_name =
+ params->cancel_name ? base::UTF8ToUTF16(params->cancel_name.value())
+ : l10n_util::GetStringUTF16(IDS_APP_CANCEL);
ash::AccessibilityController::Get()->ShowConfirmationDialog(
- title, description,
+ title, description, cancel_name,
base::BindOnce(
&AccessibilityPrivateShowConfirmationDialogFunction::OnDialogResult,
this, /* confirmed */ true),
@@ -842,10 +899,10 @@ AccessibilityPrivateUpdateDictationBubbleFunction::Run() {
if (properties.hints) {
std::vector<ash::DictationBubbleHintType> converted_hints;
for (size_t i = 0; i < (*properties.hints).size(); ++i) {
- converted_hints.push_back(
+ converted_hints.emplace_back(
ConvertDictationHintType((*properties.hints)[i]));
}
- hints = converted_hints;
+ hints = std::move(converted_hints);
}
if (hints.has_value() && hints.value().size() > 5)
@@ -929,7 +986,6 @@ AccessibilityPrivateUpdateSwitchAccessBubbleFunction::Run() {
ExtensionFunction::ResponseAction
AccessibilityPrivateIsLacrosPrimaryFunction::Run() {
- const bool is_lacros_primary =
- crosapi::lacros_startup_state::IsLacrosPrimaryEnabled();
- return RespondNow(WithArguments(is_lacros_primary));
+ return RespondNow(
+ WithArguments(crosapi::lacros_startup_state::IsLacrosEnabled()));
}
diff --git a/chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.h b/chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.h
index 39311db4354..be965639138 100644
--- a/chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.h
+++ b/chromium/chrome/browser/accessibility/accessibility_extension_api_chromeos.h
@@ -240,6 +240,14 @@ class AccessibilityPrivateSetVirtualKeyboardVisibleFunction
ACCESSIBILITY_PRIVATE_SETVIRTUALKEYBOARDVISIBLE)
};
+// API function that displays an accessibility-related toast.
+class AccessibilityPrivateShowToastFunction : public ExtensionFunction {
+ ~AccessibilityPrivateShowToastFunction() override = default;
+ ResponseAction Run() override;
+ DECLARE_EXTENSION_FUNCTION("accessibilityPrivate.showToast",
+ ACCESSIBILITY_PRIVATE_SHOWTOAST)
+};
+
// API function that shows a confirmation dialog, with callbacks for
// confirm/cancel.
class AccessibilityPrivateShowConfirmationDialogFunction
diff --git a/chromium/chrome/browser/accessibility/accessibility_prefs/OWNERS b/chromium/chrome/browser/accessibility/accessibility_prefs/OWNERS
new file mode 100644
index 00000000000..23fe4e46c35
--- /dev/null
+++ b/chromium/chrome/browser/accessibility/accessibility_prefs/OWNERS
@@ -0,0 +1,3 @@
+file://ui/accessibility/OWNERS
+aldietz@google.com
+mschillaci@google.com \ No newline at end of file
diff --git a/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_android_policy_browsertest.cc b/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_android_policy_browsertest.cc
new file mode 100644
index 00000000000..95714abb032
--- /dev/null
+++ b/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_android_policy_browsertest.cc
@@ -0,0 +1,43 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/policy/policy_test_utils.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_accessibility_state.h"
+#include "content/public/test/browser_test.h"
+#include "ui/accessibility/accessibility_prefs.h"
+
+namespace policy {
+class AccessibilityAndroidPolicyTest : public PolicyTest {};
+
+IN_PROC_BROWSER_TEST_F(AccessibilityAndroidPolicyTest,
+ AccessibilityPerformanceFilteringNotSet) {
+ EXPECT_TRUE(g_browser_process->local_state()->GetBoolean(
+ prefs::kAccessibilityPerformanceFilteringAllowed));
+
+ content::BrowserAccessibilityState* accessibility_state =
+ content::BrowserAccessibilityState::GetInstance();
+ EXPECT_TRUE(accessibility_state->IsPerformanceFilteringAllowed());
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityAndroidPolicyTest,
+ AccessibilityPerformanceFilteringAllowed) {
+ PolicyMap policies;
+ policies.Set(key::kAccessibilityPerformanceFilteringAllowed,
+ POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+ POLICY_SOURCE_CLOUD, base::Value(false), nullptr);
+ UpdateProviderPolicy(policies);
+
+ EXPECT_FALSE(g_browser_process->local_state()->GetBoolean(
+ prefs::kAccessibilityPerformanceFilteringAllowed));
+
+ content::BrowserAccessibilityState* accessibility_state =
+ content::BrowserAccessibilityState::GetInstance();
+ EXPECT_FALSE(accessibility_state->IsPerformanceFilteringAllowed());
+}
+
+} // namespace policy
diff --git a/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.cc b/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.cc
new file mode 100644
index 00000000000..ea274034353
--- /dev/null
+++ b/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.cc
@@ -0,0 +1,46 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.h"
+
+#include "base/functional/bind.h"
+#include "chrome/browser/browser_process.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "content/public/browser/browser_accessibility_state.h"
+#include "ui/accessibility/accessibility_prefs.h"
+
+namespace accessibility {
+
+// static
+void AccessibilityPrefsController::RegisterLocalStatePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(
+ prefs::kAccessibilityPerformanceFilteringAllowed, true);
+}
+
+AccessibilityPrefsController::AccessibilityPrefsController(
+ PrefService* local_state_prefs)
+ : local_state_prefs_(local_state_prefs) {
+ pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
+ pref_change_registrar_->Init(local_state_prefs_);
+
+ pref_change_registrar_->Add(
+ prefs::kAccessibilityPerformanceFilteringAllowed,
+ base::BindRepeating(&AccessibilityPrefsController::
+ OnAccessibilityPerformanceFilteringAllowedChanged,
+ base::Unretained(this)));
+}
+
+AccessibilityPrefsController::~AccessibilityPrefsController() = default;
+
+void AccessibilityPrefsController::
+ OnAccessibilityPerformanceFilteringAllowedChanged() {
+ bool new_state = local_state_prefs_->GetBoolean(
+ prefs::kAccessibilityPerformanceFilteringAllowed);
+ content::BrowserAccessibilityState::GetInstance()
+ ->SetPerformanceFilteringAllowed(new_state);
+}
+
+} // namespace accessibility
diff --git a/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.h b/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.h
new file mode 100644
index 00000000000..ff95d43e2b6
--- /dev/null
+++ b/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.h
@@ -0,0 +1,34 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ACCESSIBILITY_ACCESSIBILITY_PREFS_ANDROID_ACCESSIBILITY_PREFS_CONTROLLER_H_
+#define CHROME_BROWSER_ACCESSIBILITY_ACCESSIBILITY_PREFS_ANDROID_ACCESSIBILITY_PREFS_CONTROLLER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/no_destructor.h"
+#include "components/prefs/pref_service.h"
+
+class PrefChangeRegistrar;
+class PrefRegistrySimple;
+
+namespace accessibility {
+
+// AccessibilityPrefsController is for managing accessibility related prefs for
+// the browser.
+class AccessibilityPrefsController {
+ public:
+ static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+ explicit AccessibilityPrefsController(PrefService* local_state_prefs);
+ ~AccessibilityPrefsController();
+
+ private:
+ void OnAccessibilityPerformanceFilteringAllowedChanged();
+
+ raw_ptr<PrefService> local_state_prefs_;
+ std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
+};
+
+} // namespace accessibility
+
+#endif // CHROME_BROWSER_ACCESSIBILITY_ACCESSIBILITY_PREFS_ANDROID_ACCESSIBILITY_PREFS_CONTROLLER_H_
diff --git a/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller_browsertest.cc b/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller_browsertest.cc
new file mode 100644
index 00000000000..c30dd65746f
--- /dev/null
+++ b/chromium/chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller_browsertest.cc
@@ -0,0 +1,43 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/test/base/android/android_browser_test.h"
+#include "content/public/test/browser_test.h"
+#include "ui/accessibility/accessibility_prefs.h"
+
+class AccessibilityPrefsControllerTest : public AndroidBrowserTest {
+ public:
+ AccessibilityPrefsControllerTest() = default;
+ ~AccessibilityPrefsControllerTest() override = default;
+ AccessibilityPrefsControllerTest(const AccessibilityPrefsControllerTest&) =
+ delete;
+ AccessibilityPrefsControllerTest& operator=(
+ const AccessibilityPrefsControllerTest&) = delete;
+
+ bool GetAccessibilityPerformanceFilteringAllowed() {
+ return g_browser_process->local_state()->GetBoolean(
+ prefs::kAccessibilityPerformanceFilteringAllowed);
+ }
+
+ void SetAccessibilityPerformanceFilteringAllowed(bool allowed) {
+ g_browser_process->local_state()->SetBoolean(
+ prefs::kAccessibilityPerformanceFilteringAllowed, allowed);
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(AccessibilityPrefsControllerTest,
+ LocalStatePrefsNotRegistered) {
+ EXPECT_TRUE(GetAccessibilityPerformanceFilteringAllowed());
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityPrefsControllerTest,
+ LocalStatePrefsRegistered) {
+ EXPECT_TRUE(GetAccessibilityPerformanceFilteringAllowed());
+ SetAccessibilityPerformanceFilteringAllowed(false);
+ EXPECT_FALSE(GetAccessibilityPerformanceFilteringAllowed());
+ SetAccessibilityPerformanceFilteringAllowed(true);
+ EXPECT_TRUE(GetAccessibilityPerformanceFilteringAllowed());
+}
diff --git a/chromium/chrome/browser/accessibility/ax_screen_ai_annotator.cc b/chromium/chrome/browser/accessibility/ax_screen_ai_annotator.cc
index e1897365859..569eced313a 100644
--- a/chromium/chrome/browser/accessibility/ax_screen_ai_annotator.cc
+++ b/chromium/chrome/browser/accessibility/ax_screen_ai_annotator.cc
@@ -58,11 +58,14 @@ AXScreenAIAnnotator::AXScreenAIAnnotator(
AXScreenAIAnnotator::~AXScreenAIAnnotator() = default;
void AXScreenAIAnnotator::StateChanged(ScreenAIInstallState::State state) {
- if (state != ScreenAIInstallState::State::kReady)
+ if (state != ScreenAIInstallState::State::kReady &&
+ state != ScreenAIInstallState::State::kDownloaded) {
return;
+ }
- DCHECK(!screen_ai_service_client_.is_bound());
- BindToScreenAIService(browser_context_);
+ if (!screen_ai_service_client_.is_bound()) {
+ BindToScreenAIService(browser_context_);
+ }
}
void AXScreenAIAnnotator::BindToScreenAIService(
@@ -73,15 +76,16 @@ void AXScreenAIAnnotator::BindToScreenAIService(
ScreenAIServiceRouter* service_router =
ScreenAIServiceRouterFactory::GetForBrowserContext(browser_context);
- service_router->BindScreenAIAnnotator(std::move(screen_ai_receiver));
+ // Client interface should be ready to receive annotation results before a
+ // request is sent to the service, therefore it should be created first.
service_router->BindScreenAIAnnotatorClient(
screen_ai_service_client_.BindNewPipeAndPassRemote());
+ service_router->BindScreenAIAnnotator(std::move(screen_ai_receiver));
}
-void AXScreenAIAnnotator::AnnotateScreenshot(Browser* browser) {
+void AXScreenAIAnnotator::AnnotateScreenshot(
+ content::WebContents* web_contents) {
// Request screenshot from content area of the main frame.
- content::WebContents* web_contents =
- browser->tab_strip_model()->GetActiveWebContents();
if (!web_contents)
return;
gfx::NativeView native_view = web_contents->GetContentNativeView();
@@ -114,7 +118,6 @@ void AXScreenAIAnnotator::OnScreenshotReceived(
const ui::AXTreeID& ax_tree_id,
const base::TimeTicks& start_time,
gfx::Image snapshot) {
- DCHECK(screen_ai_annotator_.is_bound());
base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time;
if (snapshot.IsEmpty()) {
VLOG(1) << "AxScreenAIAnnotator could not grab snapshot.";
@@ -125,8 +128,35 @@ void AXScreenAIAnnotator::OnScreenshotReceived(
base::UmaHistogramTimes(
"Accessibility.ScreenAI.AnnotateScreenshotTime.Success", elapsed_time);
+
+ // If screenshot is ready before service is initialized, the service call is
+ // delayed for 3 seconds.
+ // TODO(crbug.com/1443349): This solution is only for prototyping and should
+ // be updated so that the requests are queued before initialization
+ // completes.
+ if (!screen_ai_annotator_.is_bound() ||
+ !screen_ai_service_client_.is_bound()) {
+ base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&AXScreenAIAnnotator::ExtractSemanticLayout,
+ weak_ptr_factory_.GetWeakPtr(), ax_tree_id,
+ snapshot.AsBitmap()),
+ base::Seconds(3));
+ } else {
+ ExtractSemanticLayout(ax_tree_id, snapshot.AsBitmap());
+ }
+}
+
+void AXScreenAIAnnotator::ExtractSemanticLayout(const ui::AXTreeID& ax_tree_id,
+ const SkBitmap bitmap) {
+ if (!screen_ai_annotator_.is_bound() ||
+ !screen_ai_service_client_.is_bound()) {
+ VLOG(0) << "Service is not ready yet.";
+ return;
+ }
+
screen_ai_annotator_->ExtractSemanticLayout(
- snapshot.AsBitmap(), ax_tree_id,
+ bitmap, ax_tree_id,
base::BindOnce(&AXScreenAIAnnotator::OnSemanticLayoutExtractionPerformed,
weak_ptr_factory_.GetWeakPtr(), ax_tree_id));
}
diff --git a/chromium/chrome/browser/accessibility/ax_screen_ai_annotator.h b/chromium/chrome/browser/accessibility/ax_screen_ai_annotator.h
index e9c5d2b8c78..cd7771155a1 100644
--- a/chromium/chrome/browser/accessibility/ax_screen_ai_annotator.h
+++ b/chromium/chrome/browser/accessibility/ax_screen_ai_annotator.h
@@ -17,10 +17,9 @@
#include "mojo/public/cpp/bindings/remote.h"
#include "ui/accessibility/ax_tree_id.h"
-class Browser;
-
namespace content {
class BrowserContext;
+class WebContents;
}
namespace gfx {
@@ -47,7 +46,7 @@ class AXScreenAIAnnotator : public KeyedService,
// Takes a screenshot and sends it to `OnScreenshotReceived` through an async
// call.
- void AnnotateScreenshot(Browser* browser);
+ void AnnotateScreenshot(content::WebContents* web_contents);
// ScreenAIInstallState::Observer:
void StateChanged(ScreenAIInstallState::State state) override;
@@ -56,7 +55,8 @@ class AXScreenAIAnnotator : public KeyedService,
// Binds `screen_ai_annotator_` to the Screen AI service.
virtual void BindToScreenAIService(content::BrowserContext* browser_context);
- // Receives an screenshot and sends it to ScreenAI library for processing.
+ // Receives a screenshot and passes it to `ExtractSemanticLayout` for
+ // processing.
// `ax_tree_id` represents the accessibility tree that is associated with the
// snapshot at the time of triggering the request. `start_time` represents
// the time when the screenshot is requested.
@@ -64,6 +64,9 @@ class AXScreenAIAnnotator : public KeyedService,
const base::TimeTicks& start_time,
gfx::Image snapshot);
+ void ExtractSemanticLayout(const ui::AXTreeID& ax_tree_id,
+ const SkBitmap bitmap);
+
// Informs this instance that the Screen AI Service has finished creating the
// semantic layout. `parent_tree_id` is the ID of the accessibility tree
// associated with the screenshot that was sent to the Screen AI Service, and
diff --git a/chromium/chrome/browser/accessibility/hierarchysnapshotter/android/BUILD.gn b/chromium/chrome/browser/accessibility/hierarchysnapshotter/android/BUILD.gn
index aeee85642f7..4888398d7e5 100644
--- a/chromium/chrome/browser/accessibility/hierarchysnapshotter/android/BUILD.gn
+++ b/chromium/chrome/browser/accessibility/hierarchysnapshotter/android/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2022 The Chromium Authors. All rights reserved.
+# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/chromium/chrome/browser/accessibility/image_annotation_browsertest.cc b/chromium/chrome/browser/accessibility/image_annotation_browsertest.cc
index 7d1d1961dfa..821e68a82a3 100644
--- a/chromium/chrome/browser/accessibility/image_annotation_browsertest.cc
+++ b/chromium/chrome/browser/accessibility/image_annotation_browsertest.cc
@@ -538,8 +538,14 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest,
"Appears to say: red.png Annotation. Appears to be: red.png 'fr' Label");
}
+// TODO(crbug.com/1476383): Fix flakiness on ChromeOS MSan
+#if BUILDFLAG(IS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DoesntAnnotateInternalPages DISABLED_DoesntAnnotateInternalPages
+#else
+#define MAYBE_DoesntAnnotateInternalPages DoesntAnnotateInternalPages
+#endif
IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest,
- DoesntAnnotateInternalPages) {
+ MAYBE_DoesntAnnotateInternalPages) {
FakeAnnotator::SetReturnLabelResults(true);
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL("chrome://version")));
diff --git a/chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.cc b/chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.cc
index b14041ea148..54dad55ec47 100644
--- a/chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.cc
+++ b/chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.cc
@@ -56,9 +56,10 @@ bool LiveCaptionControllerFactory::ServiceIsCreatedWithBrowserContext() const {
return true;
}
-KeyedService* LiveCaptionControllerFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+LiveCaptionControllerFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
- return new LiveCaptionController(
+ return std::make_unique<LiveCaptionController>(
Profile::FromBrowserContext(context)->GetPrefs(),
g_browser_process->local_state(),
g_browser_process->GetApplicationLocale(), context);
diff --git a/chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.h b/chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.h
index 09f1b618022..d5a24c6c57f 100644
--- a/chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.h
+++ b/chromium/chrome/browser/accessibility/live_caption/live_caption_controller_factory.h
@@ -31,7 +31,7 @@ class LiveCaptionControllerFactory : public ProfileKeyedServiceFactory {
// BrowserContextKeyedServiceFactory:
bool ServiceIsCreatedWithBrowserContext() const override;
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc b/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
index d62e19f62da..e72c13f25d4 100644
--- a/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
+++ b/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
@@ -6,11 +6,16 @@
#include <memory>
#include <string>
+#include <unordered_map>
#include <utility>
+#include <vector>
#include "base/feature_list.h"
#include "base/functional/callback_forward.h"
#include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/accessibility/caption_bubble_context_browser.h"
#include "chrome/browser/accessibility/live_caption/live_caption_controller_factory.h"
@@ -26,8 +31,95 @@
#include "media/base/media_switches.h"
#include "media/mojo/mojom/speech_recognition_result.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "third_party/icu/source/common/unicode/brkiter.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/common/unicode/uscript.h"
+#include "third_party/re2/src/re2/re2.h"
#include "ui/base/l10n/l10n_util.h"
+namespace {
+
+// Split the transcription into sentences. Spaces are included in the preceding
+// sentence.
+std::vector<std::string> SplitSentences(const std::string& text,
+ const std::string& locale) {
+ std::vector<std::string> sentences;
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Use icu::BreakIterator instead of base::i18n::BreakIterator to avoid flakey
+ // mid-string sentence breaks.
+ icu::BreakIterator* iter =
+ icu::BreakIterator::createSentenceInstance(locale.c_str(), status);
+
+ DCHECK(U_SUCCESS(status))
+ << "ICU could not open a break iterator: " << u_errorName(status) << " ("
+ << status << ")";
+
+ // Set the text to be analyzed.
+ icu::UnicodeString unicode_text = icu::UnicodeString::fromUTF8(text);
+ iter->setText(unicode_text);
+
+ // Iterate over the sentences.
+ int32_t start = iter->first();
+ int32_t end = iter->next();
+ while (end != icu::BreakIterator::DONE) {
+ icu::UnicodeString sentence;
+ unicode_text.extractBetween(start, end, sentence);
+ std::string sentence_string;
+ sentence.toUTF8String(sentence_string);
+ sentences.emplace_back(sentence_string);
+ start = end;
+ end = iter->next();
+ }
+
+ delete iter;
+
+ return sentences;
+}
+
+bool ContainsTrailingSpace(const std::string& str) {
+ return !str.empty() && base::IsAsciiWhitespace(str.back());
+}
+
+std::string RemoveTrailingSpace(const std::string& str) {
+ if (ContainsTrailingSpace(str)) {
+ return str.substr(0, str.length() - 1);
+ }
+
+ return str;
+}
+
+std::string RemovePunctuationToLower(std::string str) {
+ re2::RE2::GlobalReplace(&str, "[[:punct:]]", "");
+
+ return base::ToLowerASCII(str);
+}
+
+std::string GetTranslationCacheKey(const std::string& source_language,
+ const std::string& target_language,
+ const std::string& transcription) {
+ return base::StrCat({source_language, target_language, "|",
+ RemovePunctuationToLower(transcription)});
+}
+
+bool IsIdeographicLocale(const std::string& locale) {
+ // Retrieve the script codes used by the given language from ICU. When the
+ // given language consists of two or more scripts, we just use the first
+ // script. The size of returned script codes is always < 8. Therefore, we use
+ // an array of size 8 so we can include all script codes without insufficient
+ // buffer errors.
+ UErrorCode error = U_ZERO_ERROR;
+ UScriptCode script_code[8];
+ int scripts = uscript_getCode(locale.c_str(), script_code,
+ std::size(script_code), &error);
+
+ return U_SUCCESS(error) && scripts >= 1 &&
+ (script_code[0] == USCRIPT_HAN || script_code[0] == USCRIPT_HIRAGANA ||
+ script_code[0] == USCRIPT_YI || script_code[0] == USCRIPT_KATAKANA);
+}
+
+} // namespace
+
namespace captions {
// static
@@ -80,18 +172,59 @@ void LiveCaptionSpeechRecognitionHost::OnSpeechRecognitionRecognitionEvent(
return;
}
+ std::string target_language =
+ prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode);
if (base::FeatureList::IsEnabled(media::kLiveTranslate) &&
prefs_->GetBoolean(prefs::kLiveTranslateEnabled) &&
- l10n_util::GetLanguage(
- prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode)) !=
+ l10n_util::GetLanguage(target_language) !=
l10n_util::GetLanguage(source_language_)) {
- characters_translated_ += result.transcription.size();
- GetLiveTranslateController()->GetTranslation(
- result, source_language_,
- prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode),
- base::BindOnce(&LiveCaptionSpeechRecognitionHost::OnTranslationCallback,
- weak_factory_.GetWeakPtr()));
- std::move(reply).Run(!stop_transcriptions_);
+ std::vector<std::string> sentences =
+ SplitSentences(result.transcription, source_language_);
+
+ std::string cached_translation;
+ std::string string_to_translate;
+ bool cached_translation_found = true;
+ for (const std::string& sentence : sentences) {
+ if (cached_translation_found) {
+ std::string trailing_space =
+ ContainsTrailingSpace(sentence)
+ ? sentence.substr(sentence.length() - 1, sentence.length())
+ : std::string();
+ auto translation_cache_key = GetTranslationCacheKey(
+ source_language_, target_language,
+ trailing_space.empty() ? sentence : RemoveTrailingSpace(sentence));
+ auto iter = translation_cache_.find(translation_cache_key);
+ if (iter != translation_cache_.end()) {
+ cached_translation += iter->second;
+ if (!trailing_space.empty()) {
+ cached_translation += trailing_space;
+ }
+
+ continue;
+ }
+ cached_translation_found = false;
+ }
+
+ string_to_translate = base::StrCat({string_to_translate, sentence});
+ }
+
+ if (!string_to_translate.empty()) {
+ characters_translated_ += string_to_translate.size();
+ GetLiveTranslateController()->GetTranslation(
+ string_to_translate, source_language_, target_language,
+ base::BindOnce(
+ &LiveCaptionSpeechRecognitionHost::OnTranslationCallback,
+ weak_factory_.GetWeakPtr(), cached_translation,
+ string_to_translate, source_language_, target_language,
+ result.is_final));
+ std::move(reply).Run(!stop_transcriptions_);
+ } else {
+ // Dispatch the transcription immediately if the entire transcription was
+ // cached.
+ std::move(reply).Run(live_caption_controller->DispatchTranscription(
+ context_.get(),
+ media::SpeechRecognitionResult(cached_translation, result.is_final)));
+ }
} else {
std::move(reply).Run(
live_caption_controller->DispatchTranscription(context_.get(), result));
@@ -139,10 +272,53 @@ void LiveCaptionSpeechRecognitionHost::MediaEffectivelyFullscreenChanged(
#endif
void LiveCaptionSpeechRecognitionHost::OnTranslationCallback(
- media::SpeechRecognitionResult result) {
+ const std::string& cached_translation,
+ const std::string& original_transcription,
+ const std::string& source_language,
+ const std::string& target_language,
+ bool is_final,
+ const std::string& result) {
+ std::string formatted_result = result;
+ // Don't cache the translation if the source language is an ideographic
+ // language but the target language is not to avoid translate
+ // sentence by sentence because the Cloud Translation API does not properly
+ // translate ideographic punctuation marks.
+ if (!IsIdeographicLocale(source_language) ||
+ IsIdeographicLocale(target_language)) {
+ auto original_sentences =
+ SplitSentences(original_transcription, source_language);
+ auto translated_sentences = SplitSentences(result, target_language);
+ if (is_final) {
+ translation_cache_.clear();
+ } else {
+ if (original_sentences.size() > 1 &&
+ original_sentences.size() == translated_sentences.size()) {
+ for (size_t i = 0; i < original_sentences.size() - 1; i++) {
+ // Sentences are always cached without the trailing space.
+ std::string sentence = RemoveTrailingSpace(original_sentences[i]);
+ translation_cache_.insert(
+ {GetTranslationCacheKey(source_language, target_language,
+ sentence),
+ RemoveTrailingSpace(translated_sentences[i])});
+ }
+ }
+ }
+ } else {
+ // Append a space after final results when translating from an ideographic
+ // to non-ideographic locale. The Speech On-Device API (SODA) automatically
+ // prepends a space to recognition events after a final event, but only for
+ // non-ideographic locales.
+ // TODO(crbug.com/1426899): Consider moving this to the LiveTranslateController.
+ if (is_final) {
+ formatted_result += " ";
+ }
+ }
+
LiveCaptionController* live_caption_controller = GetLiveCaptionController();
- stop_transcriptions_ =
- !live_caption_controller->DispatchTranscription(context_.get(), result);
+ stop_transcriptions_ = !live_caption_controller->DispatchTranscription(
+ context_.get(),
+ media::SpeechRecognitionResult(
+ base::StrCat({cached_translation, formatted_result}), is_final));
}
content::WebContents* LiveCaptionSpeechRecognitionHost::GetWebContents() {
diff --git a/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h b/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
index db15c32c71b..fd2b9721898 100644
--- a/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
+++ b/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
@@ -6,6 +6,8 @@
#define CHROME_BROWSER_ACCESSIBILITY_LIVE_CAPTION_LIVE_CAPTION_SPEECH_RECOGNITION_HOST_H_
#include <memory>
+#include <string>
+#include <unordered_map>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -73,7 +75,12 @@ class LiveCaptionSpeechRecognitionHost
mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizerClient>
pending_receiver);
~LiveCaptionSpeechRecognitionHost() override;
- void OnTranslationCallback(media::SpeechRecognitionResult result);
+ void OnTranslationCallback(const std::string& cached_translation,
+ const std::string& original_transcription,
+ const std::string& source_language,
+ const std::string& target_language,
+ bool is_final,
+ const std::string& result);
// Returns the WebContents if it exists. If it does not exist, sets the
// RenderFrameHost reference to nullptr and returns nullptr.
@@ -93,6 +100,14 @@ class LiveCaptionSpeechRecognitionHost
// should stop.
bool stop_transcriptions_ = false;
+ // Used to cache translations to avoid retranslating the same string. The key
+ // is the source and target language codes followed by a `|` separator
+ // character and the original text. The value is the translated text. This
+ // cache is cleared upon receiving a final recognition event. The size of this
+ // cache depends on the frequency of partial and final recognition events, but
+ // is typically under ~10.
+ std::unordered_map<std::string, std::string> translation_cache_;
+
// The source language code of the audio stream.
std::string source_language_;
diff --git a/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc b/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc
index 69f52c59253..54b15bf04a8 100644
--- a/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc
+++ b/chromium/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc
@@ -4,17 +4,23 @@
#include "chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h"
+#include <string>
+#include <vector>
+
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/accessibility/live_caption/live_caption_controller_factory.h"
#include "chrome/browser/accessibility/live_caption/live_caption_test_util.h"
+#include "chrome/browser/accessibility/live_translate_controller_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/live_caption/caption_bubble_controller.h"
#include "components/live_caption/live_caption_controller.h"
+#include "components/live_caption/live_translate_controller.h"
#include "components/live_caption/pref_names.h"
+#include "components/prefs/pref_service.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/test/browser_test.h"
@@ -55,6 +61,29 @@ class FullscreenEventsWaiter : public content::WebContentsObserver {
namespace captions {
+class MockLiveTranslateController : public LiveTranslateController {
+ public:
+ MockLiveTranslateController(PrefService* profile_prefs,
+ content::BrowserContext* browser_context)
+ : LiveTranslateController(profile_prefs, browser_context) {}
+
+ void GetTranslation(const std::string& result,
+ std::string source_language,
+ std::string target_language,
+ OnTranslateEventCallback callback) override {
+ translation_requests_.push_back(result);
+ std::move(callback).Run(result);
+ }
+
+ // Returns a collection of strings passed into `GetTranslation()`.
+ std::vector<std::string> GetTranslationRequests() {
+ return translation_requests_;
+ }
+
+ private:
+ std::vector<std::string> translation_requests_;
+};
+
class LiveCaptionSpeechRecognitionHostTest : public LiveCaptionBrowserTest {
public:
LiveCaptionSpeechRecognitionHostTest() = default;
@@ -64,6 +93,12 @@ class LiveCaptionSpeechRecognitionHostTest : public LiveCaptionBrowserTest {
LiveCaptionSpeechRecognitionHostTest& operator=(
const LiveCaptionSpeechRecognitionHostTest&) = delete;
+ std::unique_ptr<KeyedService> SetLiveTranslateController(
+ content::BrowserContext* context) {
+ return std::make_unique<testing::NiceMock<MockLiveTranslateController>>(
+ browser()->profile()->GetPrefs(), browser()->profile());
+ }
+
// LiveCaptionBrowserTest:
void SetUp() override {
// This is required for the fullscreen video tests.
@@ -73,6 +108,11 @@ class LiveCaptionSpeechRecognitionHostTest : public LiveCaptionBrowserTest {
}
void SetUpOnMainThread() override {
+ LiveTranslateControllerFactory::GetInstance()->SetTestingFactory(
+ browser()->profile(),
+ base::BindRepeating(
+ &LiveCaptionSpeechRecognitionHostTest::SetLiveTranslateController,
+ base::Unretained(this)));
LiveCaptionBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
}
@@ -89,9 +129,10 @@ class LiveCaptionSpeechRecognitionHostTest : public LiveCaptionBrowserTest {
void OnSpeechRecognitionRecognitionEvent(content::RenderFrameHost* frame_host,
std::string text,
- bool expected_success) {
+ bool expected_success,
+ bool is_final = false) {
remotes_[frame_host]->OnSpeechRecognitionRecognitionEvent(
- media::SpeechRecognitionResult(text, /*is_final=*/false),
+ media::SpeechRecognitionResult(text, is_final),
base::BindOnce(&LiveCaptionSpeechRecognitionHostTest::
DispatchTranscriptionCallback,
base::Unretained(this), expected_success));
@@ -125,6 +166,13 @@ class LiveCaptionSpeechRecognitionHostTest : public LiveCaptionBrowserTest {
#endif
}
+ std::vector<std::string> GetTranslationRequests() {
+ return static_cast<MockLiveTranslateController*>(
+ LiveTranslateControllerFactory::GetForProfile(
+ browser()->profile()))
+ ->GetTranslationRequests();
+ }
+
private:
void DispatchTranscriptionCallback(bool expected_success, bool success) {
EXPECT_EQ(expected_success, success);
@@ -262,4 +310,172 @@ IN_PROC_BROWSER_TEST_F(LiveCaptionSpeechRecognitionHostTest, LiveTranslate) {
ExpectIsWidgetVisible(true);
}
+IN_PROC_BROWSER_TEST_F(LiveCaptionSpeechRecognitionHostTest, TranslationCache) {
+ content::RenderFrameHost* frame_host = browser()
+ ->tab_strip_model()
+ ->GetActiveWebContents()
+ ->GetPrimaryMainFrame();
+ CreateLiveCaptionSpeechRecognitionHost(frame_host);
+
+ SetLiveCaptionEnabled(true);
+ SetLiveTranslateEnabled(true);
+
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host,
+ "Pomeranians come in 23 different color combinations. Some Pomeranians",
+ /* expected_success= */ true, /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ExpectIsWidgetVisible(true);
+
+ ASSERT_EQ(1u, GetTranslationRequests().size());
+ ASSERT_EQ(
+ "Pomeranians come in 23 different color combinations. Some Pomeranians",
+ GetTranslationRequests().back());
+
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host,
+ "Pomeranians come in 23 different color combinations. Some Pomeranians",
+ /* expected_success= */ true, /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(2u, GetTranslationRequests().size());
+ ASSERT_EQ("Some Pomeranians", GetTranslationRequests().back());
+
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host,
+ "Pomeranians come in 23 different color combinations. Some Pomeranians "
+ "are even tricolored! Are",
+ /* expected_success= */ true, /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(3u, GetTranslationRequests().size());
+ ASSERT_EQ("Some Pomeranians are even tricolored! Are",
+ GetTranslationRequests().back());
+
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host,
+ "Pomeranians come in 23 different color combinations. Some Pomeranians "
+ "are even tricolored! Are they the cutest dog breed in the",
+ /* expected_success= */ true, /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(4u, GetTranslationRequests().size());
+ ASSERT_EQ("Are they the cutest dog breed in the",
+ GetTranslationRequests().back());
+
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host,
+ "Pomeranians come in 23 different color combinations. Some Pomeranians "
+ "are even tricolored! Are they the cutest dog breed in the world? "
+ "Absolutely.",
+ /* expected_success= */ true, /* is_final= */ true);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(5u, GetTranslationRequests().size());
+ ASSERT_EQ("Are they the cutest dog breed in the world? Absolutely.",
+ GetTranslationRequests().back());
+
+ // The previous final event clears the translation cache.
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host, "Pomeranians come in 23 different color combinations.",
+ /* expected_success= */ true, /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(6u, GetTranslationRequests().size());
+ ASSERT_EQ("Pomeranians come in 23 different color combinations.",
+ GetTranslationRequests().back());
+
+ // Ensure that cached strings aren't retrieved out of order.
+ OnSpeechRecognitionRecognitionEvent(frame_host, "First sentence. Second",
+ /* expected_success= */ true,
+ /* is_final= */ false);
+ OnSpeechRecognitionRecognitionEvent(frame_host, "Third sentence. Fourth",
+ /* expected_success= */ true,
+ /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(8u, GetTranslationRequests().size());
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host, "First sentence. Second sentence. Third sentence.",
+ /* expected_success= */ true, /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(9u, GetTranslationRequests().size());
+ ASSERT_EQ("Second sentence. Third sentence.",
+ GetTranslationRequests().back());
+
+ // Ensure that partial sentences aren't cached.
+ OnSpeechRecognitionRecognitionEvent(frame_host, "Fourth sentence",
+ /* expected_success= */ true,
+ /* is_final= */ false);
+ OnSpeechRecognitionRecognitionEvent(frame_host, "Fourth sentence",
+ /* expected_success= */ true,
+ /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(11u, GetTranslationRequests().size());
+
+ // Ensure that punctuation marks aren't cached.
+ OnSpeechRecognitionRecognitionEvent(frame_host, "Possums can play dead! Wow",
+ /* expected_success= */ true,
+ /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(12u, GetTranslationRequests().size());
+ ASSERT_EQ("Possums can play dead! Wow", GetTranslationRequests().back());
+
+ OnSpeechRecognitionRecognitionEvent(frame_host, "Possums can play dead? Wow",
+ /* expected_success= */ true,
+ /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(13u, GetTranslationRequests().size());
+ ASSERT_EQ("Wow", GetTranslationRequests().back());
+
+ // Ensure that phrases are cached without capitalization.
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host, "Possums can play DEAD? Amazing",
+ /* expected_success= */ true, /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(14u, GetTranslationRequests().size());
+ ASSERT_EQ("Amazing", GetTranslationRequests().back());
+
+ // Ensure that strings are cached without whitespace.
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host, "Flying squirrels can glide up to 300 feet. Wow",
+ /* expected_success= */ true, /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(15u, GetTranslationRequests().size());
+ ASSERT_EQ("Flying squirrels can glide up to 300 feet. Wow",
+ GetTranslationRequests().back());
+ OnSpeechRecognitionRecognitionEvent(
+ frame_host, "Flying squirrels can glide up to 300 feet.\nThat's so far",
+ /* expected_success= */ true, /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(16u, GetTranslationRequests().size());
+ ASSERT_EQ("That's so far", GetTranslationRequests().back());
+}
+
+IN_PROC_BROWSER_TEST_F(LiveCaptionSpeechRecognitionHostTest,
+ IdeographicTranslationCache) {
+ content::RenderFrameHost* frame_host = browser()
+ ->tab_strip_model()
+ ->GetActiveWebContents()
+ ->GetPrimaryMainFrame();
+ SetLiveCaptionEnabled(true);
+ SetLiveTranslateEnabled(true);
+
+ // Ensure that ideographic to non-ideographic translations are not cached.
+ browser()->profile()->GetPrefs()->SetString(prefs::kLiveCaptionLanguageCode,
+ "ja-JP");
+ CreateLiveCaptionSpeechRecognitionHost(frame_host);
+
+ OnSpeechRecognitionRecognitionEvent(frame_host,
+ "Tanuki are canids, similar to dogs but "
+ "with larger ears and tails. So cool",
+ /* expected_success= */ true,
+ /* is_final= */ false);
+ OnSpeechRecognitionRecognitionEvent(frame_host,
+ "Tanuki are canids, similar to dogs but "
+ "with larger ears and tails. So cool",
+ /* expected_success= */ true,
+ /* is_final= */ false);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(2u, GetTranslationRequests().size());
+ ASSERT_EQ(
+ "Tanuki are canids, similar to dogs but with larger ears and tails. So "
+ "cool",
+ GetTranslationRequests().back());
+}
+
} // namespace captions
diff --git a/chromium/chrome/browser/accessibility/live_caption/live_caption_test_util.cc b/chromium/chrome/browser/accessibility/live_caption/live_caption_test_util.cc
index 49cde9c64c5..0b76c74bedc 100644
--- a/chromium/chrome/browser/accessibility/live_caption/live_caption_test_util.cc
+++ b/chromium/chrome/browser/accessibility/live_caption/live_caption_test_util.cc
@@ -83,6 +83,10 @@ void LiveCaptionBrowserTest::SetLiveCaptionEnabledOnProfile(bool enabled,
void LiveCaptionBrowserTest::SetLiveTranslateEnabled(bool enabled) {
browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveTranslateEnabled,
enabled);
+ browser()->profile()->GetPrefs()->SetString(prefs::kLiveCaptionLanguageCode,
+ "en-US");
+ browser()->profile()->GetPrefs()->SetString(
+ prefs::kLiveTranslateTargetLanguageCode, "fr-FR");
}
} // namespace captions
diff --git a/chromium/chrome/browser/accessibility/live_translate_controller_factory.cc b/chromium/chrome/browser/accessibility/live_translate_controller_factory.cc
index b50cb893ecd..305cb302edb 100644
--- a/chromium/chrome/browser/accessibility/live_translate_controller_factory.cc
+++ b/chromium/chrome/browser/accessibility/live_translate_controller_factory.cc
@@ -49,9 +49,10 @@ bool LiveTranslateControllerFactory::ServiceIsCreatedWithBrowserContext()
return true;
}
-KeyedService* LiveTranslateControllerFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+LiveTranslateControllerFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* browser_context) const {
- return new LiveTranslateController(
+ return std::make_unique<LiveTranslateController>(
Profile::FromBrowserContext(browser_context)->GetPrefs(),
browser_context);
}
diff --git a/chromium/chrome/browser/accessibility/live_translate_controller_factory.h b/chromium/chrome/browser/accessibility/live_translate_controller_factory.h
index 20ceb5741df..1872aaef7dd 100644
--- a/chromium/chrome/browser/accessibility/live_translate_controller_factory.h
+++ b/chromium/chrome/browser/accessibility/live_translate_controller_factory.h
@@ -33,7 +33,7 @@ class LiveTranslateControllerFactory : public ProfileKeyedServiceFactory {
// BrowserContextKeyedServiceFactory:
bool ServiceIsCreatedWithBrowserContext() const override;
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* browser_context) const override;
};
diff --git a/chromium/chrome/browser/accessibility/pdf_ocr_controller.cc b/chromium/chrome/browser/accessibility/pdf_ocr_controller.cc
index 09f8af73cd5..5b3a68b55ef 100644
--- a/chromium/chrome/browser/accessibility/pdf_ocr_controller.cc
+++ b/chromium/chrome/browser/accessibility/pdf_ocr_controller.cc
@@ -6,6 +6,9 @@
#include "base/check_is_test.h"
#include "base/check_op.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/metrics_hashes.h"
+#include "base/strings/string_split.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/screen_ai/screen_ai_service_router.h"
@@ -15,6 +18,8 @@
#include "chrome/common/pdf_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/language/core/common/language_util.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
@@ -23,6 +28,11 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/accessibility/view_accessibility.h"
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/crosapi/mojom/prefs.mojom.h"
+#include "chromeos/lacros/lacros_service.h"
+#endif
+
namespace {
constexpr char kHtmlMimeType[] = "text/html";
@@ -86,6 +96,19 @@ void AnnounceToScreenReader(const int message_id) {
l10n_util::GetStringUTF16(message_id));
}
+void RecordAcceptLanguages(const std::string& accept_languages) {
+ for (std::string language :
+ base::SplitString(accept_languages, ",", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ // Convert to a Chrome language code synonym. This language synonym is then
+ // converted into a `LocaleCodeISO639` enum value for a UMA histogram.
+ language::ToChromeLanguageSynonym(&language);
+ // TODO(crbug.com/1443345): Add a browser test to validate this UMA metric.
+ base::UmaHistogramSparse("Accessibility.PdfOcr.UserAcceptLanguage",
+ base::HashMetricName(language));
+ }
+}
+
} // namespace
namespace screen_ai {
@@ -133,6 +156,9 @@ void PdfOcrController::RunPdfOcrOnlyOnce(content::WebContents* web_contents) {
ui::AXMode ax_mode = web_contents->GetAccessibilityMode();
ax_mode.set_mode(ui::AXMode::kPDFOcr, true);
web_contents->SetAccessibilityMode(ax_mode);
+
+ RecordAcceptLanguages(
+ profile_->GetPrefs()->GetString(language::prefs::kAcceptLanguages));
}
bool PdfOcrController::IsEnabled() const {
@@ -146,7 +172,25 @@ void PdfOcrController::OnPdfOcrAlwaysActiveChanged() {
profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityPdfOcrAlwaysActive);
VLOG(2) << "PDF OCR Always Active changed: " << is_always_active;
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ // This preference should be kept in sync with Ash.
+ auto* lacros_service = chromeos::LacrosService::Get();
+ if (!lacros_service ||
+ !lacros_service->IsAvailable<crosapi::mojom::Prefs>()) {
+ VLOG(0) << "Cannot sync the preference with Ash.";
+ } else {
+ lacros_service->GetRemote<crosapi::mojom::Prefs>()->SetPref(
+ crosapi::mojom::PrefPath::kAccessibilityPdfOcrAlwaysActive,
+ profile_->GetPrefs()
+ ->GetValue(prefs::kAccessibilityPdfOcrAlwaysActive)
+ .Clone(),
+ base::OnceClosure());
+ }
+#endif
+
if (is_always_active) {
+ RecordAcceptLanguages(
+ profile_->GetPrefs()->GetString(language::prefs::kAcceptLanguages));
if (MaybeScheduleRequest(/*web_contents_for_only_once_request=*/nullptr)) {
// The request will be handled when the library is ready or discarded if
// it fails to load.
diff --git a/chromium/chrome/browser/accessibility/pdf_ocr_controller.h b/chromium/chrome/browser/accessibility/pdf_ocr_controller.h
index 0d6d4b9c7e8..4b46cafdedb 100644
--- a/chromium/chrome/browser/accessibility/pdf_ocr_controller.h
+++ b/chromium/chrome/browser/accessibility/pdf_ocr_controller.h
@@ -27,6 +27,7 @@ class PdfOcrControllerFactory;
// mode of WebContents when it changes, provided its feature flag is enabled.
class PdfOcrController : public KeyedService, ScreenAIInstallState::Observer {
public:
+ explicit PdfOcrController(Profile* profile);
PdfOcrController(const PdfOcrController&) = delete;
PdfOcrController& operator=(const PdfOcrController&) = delete;
~PdfOcrController() override;
@@ -50,8 +51,6 @@ class PdfOcrController : public KeyedService, ScreenAIInstallState::Observer {
private:
friend class PdfOcrControllerFactory;
- explicit PdfOcrController(Profile* profile);
-
void OnPdfOcrAlwaysActiveChanged();
// Sends Pdf Ocr Always Active state to all relevant WebContents.
diff --git a/chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.cc b/chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.cc
index 5da3a81df72..d2231b554ca 100644
--- a/chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.cc
+++ b/chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.cc
@@ -32,9 +32,11 @@ PdfOcrControllerFactory::PdfOcrControllerFactory()
PdfOcrControllerFactory::~PdfOcrControllerFactory() = default;
-KeyedService* PdfOcrControllerFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+PdfOcrControllerFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
- return new PdfOcrController(Profile::FromBrowserContext(context));
+ return std::make_unique<PdfOcrController>(
+ Profile::FromBrowserContext(context));
}
} // namespace screen_ai
diff --git a/chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.h b/chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.h
index 8c230d2fc01..7693d4efde8 100644
--- a/chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.h
+++ b/chromium/chrome/browser/accessibility/pdf_ocr_controller_factory.h
@@ -31,7 +31,7 @@ class PdfOcrControllerFactory : public ProfileKeyedServiceFactory {
~PdfOcrControllerFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};
diff --git a/chromium/chrome/browser/accessibility/screen_ai_service_browsertest.cc b/chromium/chrome/browser/accessibility/screen_ai_service_browsertest.cc
index e362265c1a4..1fcc72bde6c 100644
--- a/chromium/chrome/browser/accessibility/screen_ai_service_browsertest.cc
+++ b/chromium/chrome/browser/accessibility/screen_ai_service_browsertest.cc
@@ -64,7 +64,9 @@ IN_PROC_BROWSER_TEST_F(ScreenAIServiceTest, DISABLED_ScreenshotTest) {
run_loop.Quit();
});
- browser()->RunScreenAIAnnotator();
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ annotator->AnnotateScreenshot(web_contents);
run_loop.Run();
// TODO(crbug.com/1443345): Add a test that mocks
diff --git a/chromium/chrome/browser/accessibility/service/accessibility_service_router.cc b/chromium/chrome/browser/accessibility/service/accessibility_service_router.cc
index 8bf3b6cfa10..9ed7d592e84 100644
--- a/chromium/chrome/browser/accessibility/service/accessibility_service_router.cc
+++ b/chromium/chrome/browser/accessibility/service/accessibility_service_router.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -36,6 +36,17 @@ void AccessibilityServiceRouter::BindAssistiveTechnologyController(
}
}
+void AccessibilityServiceRouter::ConnectDevToolsAgent(
+ ::mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent,
+ mojom::AssistiveTechnologyType type) {
+#if BUILDFLAG(ENABLE_ACCESSIBILITY_SERVICE)
+ LaunchIfNotRunning();
+ // Check to make sure the service was actually launched.
+ CHECK(accessibility_service_.is_bound());
+ accessibility_service_->ConnectDevToolsAgent(std::move(agent), type);
+#endif
+}
+
void AccessibilityServiceRouter::LaunchIfNotRunning() {
if (accessibility_service_.is_bound())
return;
diff --git a/chromium/chrome/browser/accessibility/service/accessibility_service_router.h b/chromium/chrome/browser/accessibility/service/accessibility_service_router.h
index 7b11fdd43a8..2965868ffc4 100644
--- a/chromium/chrome/browser/accessibility/service/accessibility_service_router.h
+++ b/chromium/chrome/browser/accessibility/service/accessibility_service_router.h
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -30,6 +30,10 @@ class AccessibilityServiceRouter : public KeyedService {
at_controller_receiver,
const std::vector<mojom::AssistiveTechnologyType>& enabled_features);
+ virtual void ConnectDevToolsAgent(
+ ::mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent,
+ mojom::AssistiveTechnologyType type);
+
private:
void LaunchIfNotRunning();
diff --git a/chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.cc b/chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.cc
index 7f0891e44c9..ebc2d0c758f 100644
--- a/chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.cc
+++ b/chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.cc
@@ -1,9 +1,10 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/accessibility/service/accessibility_service_router_factory.h"
#include "chrome/browser/accessibility/service/accessibility_service_router.h"
+#include "chrome/browser/profiles/profile_selections.h"
#include "content/public/browser/browser_context.h"
namespace ax {
@@ -27,18 +28,25 @@ AccessibilityServiceRouterFactory::AccessibilityServiceRouterFactory()
: ProfileKeyedServiceFactory(
"AccessibilityService",
ProfileSelections::Builder()
+ // TODO(b/262637071): Ensure A11yService can run on all profiles,
+ // there should be no UI surface that doesn't allow the
+ // A11yService, as it is required to run A11y features like
+ // ChromeVox.
.WithRegular(ProfileSelection::kOriginalOnly)
// TODO(crbug.com/1418376): Check if this service is needed in
// Guest mode.
.WithGuest(ProfileSelection::kOriginalOnly)
+ .WithSystem(ProfileSelection::kOriginalOnly)
+ .WithAshInternals(ProfileSelection::kOriginalOnly)
.Build()) {}
AccessibilityServiceRouterFactory::~AccessibilityServiceRouterFactory() =
default;
-KeyedService* AccessibilityServiceRouterFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+AccessibilityServiceRouterFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
- return new AccessibilityServiceRouter();
+ return std::make_unique<AccessibilityServiceRouter>();
}
// static
diff --git a/chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.h b/chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.h
index a7eb87a8cfc..a908ec15a3e 100644
--- a/chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.h
+++ b/chromium/chrome/browser/accessibility/service/accessibility_service_router_factory.h
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -36,7 +36,7 @@ class AccessibilityServiceRouterFactory : public ProfileKeyedServiceFactory {
~AccessibilityServiceRouterFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};
diff --git a/chromium/chrome/browser/android/browserservices/intents/BUILD.gn b/chromium/chrome/browser/android/browserservices/intents/BUILD.gn
index 82704a30c94..9c5e6c52145 100644
--- a/chromium/chrome/browser/android/browserservices/intents/BUILD.gn
+++ b/chromium/chrome/browser/android/browserservices/intents/BUILD.gn
@@ -22,6 +22,7 @@ android_library("java") {
deps = [
"//base:base_java",
"//chrome/android/webapk/libs/common:common_java",
+ "//chrome/browser/android/intents:java",
"//chrome/browser/flags:java",
"//components/webapps/browser/android:java",
"//content/public/android:content_main_dex_java",
@@ -47,6 +48,7 @@ robolectric_library("junit") {
"//chrome/android/webapk/libs/common:common_java",
"//chrome/android/webapk/libs/common:splash_java",
"//chrome/android/webapk/test:junit_test_support",
+ "//chrome/browser/android/intents:java",
"//chrome/test/android:chrome_java_unit_test_support",
"//components/webapk/android/libs/common:java",
"//components/webapps/browser/android:java",
@@ -54,6 +56,7 @@ robolectric_library("junit") {
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_browser_browser_java",
"//third_party/blink/public/mojom:mojom_platform_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//ui/android:ui_no_recycler_view_java",
diff --git a/chromium/chrome/browser/android/browserservices/verification/BUILD.gn b/chromium/chrome/browser/android/browserservices/verification/BUILD.gn
index 52ef0957a75..04e9f6055f8 100644
--- a/chromium/chrome/browser/android/browserservices/verification/BUILD.gn
+++ b/chromium/chrome/browser/android/browserservices/verification/BUILD.gn
@@ -64,11 +64,13 @@ robolectric_binary("verification_junit_tests") {
":junit_test_support",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/profiles/android:java",
"//components/content_relationship_verification:java",
"//components/content_relationship_verification/android:java",
"//components/content_relationship_verification/android:junit_test_support",
"//components/embedder_support/android:util_java",
+ "//components/externalauth/android:java",
"//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//third_party/androidx:androidx_browser_browser_java",
diff --git a/chromium/chrome/browser/android/customtabs/branding/BUILD.gn b/chromium/chrome/browser/android/customtabs/branding/BUILD.gn
index 2fa6e4323b1..c00ff353ca0 100644
--- a/chromium/chrome/browser/android/customtabs/branding/BUILD.gn
+++ b/chromium/chrome/browser/android/customtabs/branding/BUILD.gn
@@ -40,6 +40,7 @@ robolectric_library("junit") {
sources = [
"java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingCheckerUnitTest.java",
"java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingControllerUnitTest.java",
+ "java/src/org/chromium/chrome/browser/customtabs/features/branding/SharedPreferencesBrandingTimeStorageUnitTest.java",
]
deps = [
@@ -51,6 +52,7 @@ robolectric_library("junit") {
"//chrome/android:chrome_app_java_resources",
"//chrome/browser/flags:java",
"//chrome/test/android:chrome_java_unit_test_support",
+ "//components/crash/android:java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/hamcrest:hamcrest_core_java",
diff --git a/chromium/chrome/browser/android/httpclient/BUILD.gn b/chromium/chrome/browser/android/httpclient/BUILD.gn
index d7efdb51ec6..a3e33902602 100644
--- a/chromium/chrome/browser/android/httpclient/BUILD.gn
+++ b/chromium/chrome/browser/android/httpclient/BUILD.gn
@@ -94,6 +94,7 @@ java_library("junit_tests") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/profiles/android:java",
"//net/android:net_java",
"//third_party/junit:junit",
diff --git a/chromium/chrome/browser/android/intents/BUILD.gn b/chromium/chrome/browser/android/intents/BUILD.gn
new file mode 100644
index 00000000000..2ce73d2e358
--- /dev/null
+++ b/chromium/chrome/browser/android/intents/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+ sources =
+ [ "java/src/org/chromium/chrome/browser/intents/BrowserIntentUtils.java" ]
+ deps = [ "//base:base_java" ]
+}
+
+android_library("unit_device_javatests") {
+ testonly = true
+ sources = [ "java/src/org/chromium/chrome/browser/intents/BrowserIntentUtilsUnitTest.java" ]
+ deps = [
+ ":java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_test_core_java",
+ "//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
+ "//third_party/hamcrest:hamcrest_library_java",
+ "//third_party/junit",
+ ]
+}
diff --git a/chromium/chrome/browser/android/vr/BUILD.gn b/chromium/chrome/browser/android/vr/BUILD.gn
index c81a1146755..0d94403b3e3 100644
--- a/chromium/chrome/browser/android/vr/BUILD.gn
+++ b/chromium/chrome/browser/android/vr/BUILD.gn
@@ -60,7 +60,7 @@ static_library("vr_android") {
":ui_factory",
"//base",
"//cc",
- "//chrome/android/features/vr:base_jni_headers",
+ "//chrome/android:chrome_jni_headers",
"//chrome/android/features/vr:split_jni_headers",
"//chrome/browser:browser_process",
"//chrome/browser/profiles:profile",
diff --git a/chromium/chrome/browser/android/webapk/BUILD.gn b/chromium/chrome/browser/android/webapk/BUILD.gn
index 36b563eec72..9ed85ab9bb1 100644
--- a/chromium/chrome/browser/android/webapk/BUILD.gn
+++ b/chromium/chrome/browser/android/webapk/BUILD.gn
@@ -6,6 +6,8 @@ import("//chrome/android/webapk/shell_apk/request_update_for_version.gni")
source_set("webapk_sources") {
sources = [
+ "webapk_database.cc",
+ "webapk_database.h",
"webapk_features.cc",
"webapk_features.h",
"webapk_handler_delegate.cc",
@@ -16,6 +18,9 @@ source_set("webapk_sources") {
"webapk_metrics.h",
"webapk_post_share_target_navigator.cc",
"webapk_post_share_target_navigator.h",
+ "webapk_registrar.h",
+ "webapk_registry_update.cc",
+ "webapk_registry_update.h",
"webapk_ukm_recorder.cc",
"webapk_ukm_recorder.h",
"webapk_update_data_fetcher.cc",
@@ -23,8 +28,10 @@ source_set("webapk_sources") {
]
deps = [
"//chrome/android:jni_headers",
+ "//chrome/browser/android/webapk/proto",
"//chrome/browser/profiles:profile",
"//chrome/browser/web_share_target",
+ "//components/sync",
"//components/webapps/browser",
"//content/public/browser",
"//services/metrics/public/cpp:ukm_builders",
diff --git a/chromium/chrome/browser/android/webapk/proto/BUILD.gn b/chromium/chrome/browser/android/webapk/proto/BUILD.gn
new file mode 100644
index 00000000000..94239056b4f
--- /dev/null
+++ b/chromium/chrome/browser/android/webapk/proto/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("proto") {
+ proto_in_dir = "//"
+ sources = [ "webapk_database.proto" ]
+ link_deps = [ "//components/sync/protocol" ]
+}
diff --git a/chromium/chrome/browser/apps/almanac_api_client/BUILD.gn b/chromium/chrome/browser/apps/almanac_api_client/BUILD.gn
index 94b831c3196..9c6bf1665a9 100644
--- a/chromium/chrome/browser/apps/almanac_api_client/BUILD.gn
+++ b/chromium/chrome/browser/apps/almanac_api_client/BUILD.gn
@@ -27,13 +27,19 @@ source_set("almanac_api_client") {
"//components/language/core/browser",
"//components/prefs",
"//components/version_info",
+ "//google_apis",
+ "//services/network/public/cpp",
+ "//services/network/public/mojom",
]
}
source_set("unit_tests") {
testonly = true
- sources = [ "device_info_manager_unittest.cc" ]
+ sources = [
+ "almanac_api_util_unittest.cc",
+ "device_info_manager_unittest.cc",
+ ]
deps = [
":almanac_api_client",
@@ -42,7 +48,7 @@ source_set("unit_tests") {
"//chrome/common:channel_info",
"//chrome/test:test_support",
"//components/language/core/browser",
- "//components/prefs:prefs",
- "//testing/gtest:gtest",
+ "//components/prefs",
+ "//testing/gtest",
]
}
diff --git a/chromium/chrome/browser/apps/app_deduplication_service/BUILD.gn b/chromium/chrome/browser/apps/app_deduplication_service/BUILD.gn
new file mode 100644
index 00000000000..d5f5b9e2717
--- /dev/null
+++ b/chromium/chrome/browser/apps/app_deduplication_service/BUILD.gn
@@ -0,0 +1,73 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+# App Deduplication Service is Ash-only.
+assert(is_chromeos_ash)
+
+source_set("app_deduplication_service") {
+ sources = [
+ "app_deduplication_cache.cc",
+ "app_deduplication_cache.h",
+ "app_deduplication_mapper.cc",
+ "app_deduplication_mapper.h",
+ "app_deduplication_server_connector.cc",
+ "app_deduplication_server_connector.h",
+ "app_deduplication_service.cc",
+ "app_deduplication_service.h",
+ "app_deduplication_service_factory.cc",
+ "app_deduplication_service_factory.h",
+ "duplicate_group.cc",
+ "duplicate_group.h",
+ "entry_types.cc",
+ "entry_types.h",
+ ]
+
+ public_deps = [ "//chrome/browser/apps/app_deduplication_service/proto" ]
+
+ deps = [
+ "//base",
+ "//chrome/browser/apps/almanac_api_client",
+ "//chrome/browser/apps/app_provisioning_service",
+ "//chrome/browser/apps/app_service",
+ "//chrome/browser/profiles",
+ "//chrome/browser/profiles:profile",
+ "//chrome/common:constants",
+ "//components/keyed_service/content",
+ "//components/pref_registry",
+ "//components/prefs",
+ "//components/services/app_service",
+ "//google_apis:google_apis",
+ "//services/network/public/cpp",
+ "//url",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "app_deduplication_cache_unittest.cc",
+ "app_deduplication_mapper_unittest.cc",
+ "app_deduplication_server_connector_unittest.cc",
+ "app_deduplication_service_unittest.cc",
+ ]
+
+ deps = [
+ ":app_deduplication_service",
+ "//base",
+ "//base/test:test_support",
+ "//chrome/browser/apps/almanac_api_client",
+ "//chrome/browser/apps/app_provisioning_service",
+ "//chrome/browser/apps/app_service",
+ "//chrome/common:constants",
+ "//chrome/test:test_support",
+ "//components/version_info:channel",
+ "//content/test:test_support",
+ "//net",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
+ ]
+}
diff --git a/chromium/chrome/browser/apps/app_discovery_service/BUILD.gn b/chromium/chrome/browser/apps/app_discovery_service/BUILD.gn
new file mode 100644
index 00000000000..388bd27be8e
--- /dev/null
+++ b/chromium/chrome/browser/apps/app_discovery_service/BUILD.gn
@@ -0,0 +1,85 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+# App Discovery Service is Ash-only.
+assert(is_chromeos_ash)
+
+source_set("app_discovery_service") {
+ sources = [
+ "almanac_fetcher.cc",
+ "almanac_fetcher.h",
+ "app_discovery_service.cc",
+ "app_discovery_service.h",
+ "app_discovery_service_factory.cc",
+ "app_discovery_service_factory.h",
+ "app_discovery_util.h",
+ "app_fetcher_manager.cc",
+ "app_fetcher_manager.h",
+ "game_extras.cc",
+ "game_extras.h",
+ "game_fetcher.cc",
+ "game_fetcher.h",
+ "launcher_app_almanac_connector.cc",
+ "launcher_app_almanac_connector.h",
+ "play_extras.cc",
+ "play_extras.h",
+ "recommended_arc_app_fetcher.cc",
+ "recommended_arc_app_fetcher.h",
+ "result.cc",
+ "result.h",
+ "test_fetcher.cc",
+ "test_fetcher.h",
+ ]
+
+ deps = [
+ "//base",
+ "//chrome/browser/apps/almanac_api_client",
+ "//chrome/browser/apps/app_discovery_service/almanac_api:proto",
+ "//chrome/browser/apps/app_discovery_service/recommended_arc_apps",
+ "//chrome/browser/apps/app_provisioning_service",
+ "//chrome/browser/profiles:profile",
+ "//components/country_codes",
+ "//components/keyed_service/core",
+ "//components/language/core/browser",
+ "//components/prefs",
+ "//content/public/browser",
+ "//services/data_decoder/public/cpp",
+ "//services/network/public/cpp",
+ "//ui/base",
+ "//ui/gfx",
+ "//url",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "almanac_fetcher_unittest.cc",
+ "app_discovery_service_unittest.cc",
+ "game_fetcher_unittest.cc",
+ "launcher_app_almanac_connector_unittest.cc",
+ "recommended_arc_app_fetcher_unittest.cc",
+ ]
+
+ deps = [
+ ":app_discovery_service",
+ "//base",
+ "//base/test:proto_test_support",
+ "//base/test:test_support",
+ "//chrome/browser/apps/app_discovery_service/almanac_api:launcher_app_descriptor",
+ "//chrome/browser/apps/app_discovery_service/almanac_api:proto",
+ "//chrome/browser/apps/app_discovery_service/recommended_arc_apps:unit_tests",
+ "//chrome/browser/apps/app_provisioning_service/proto",
+ "//chrome/browser/profiles:profile",
+ "//chrome/common:constants",
+ "//chrome/test:test_support",
+ "//content/test:test_support",
+ "//testing/gtest",
+ ]
+
+ data = [ "$root_gen_dir/chrome/browser/apps/app_discovery_service/almanac_api/launcher_app.descriptor" ]
+}
diff --git a/chromium/chrome/browser/apps/app_discovery_service/almanac_api/BUILD.gn b/chromium/chrome/browser/apps/app_discovery_service/almanac_api/BUILD.gn
new file mode 100644
index 00000000000..77433ebfe95
--- /dev/null
+++ b/chromium/chrome/browser/apps/app_discovery_service/almanac_api/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("proto") {
+ proto_in_dir = "//"
+ sources = [ "launcher_app.proto" ]
+
+ link_deps = [ "//chrome/browser/apps/almanac_api_client/proto" ]
+}
+
+# Note that unlike the .cc and .h pb files the descriptor is generated in
+# "$root_gen_dir/launcher_app.descriptor" unless we specify proto_out_dir
+# because of the way we set proto_in_dir.
+# Check third_party/protobuf/proto_library.gni for more details.
+proto_library("launcher_app_descriptor") {
+ proto_in_dir = "//"
+ proto_out_dir = "chrome/browser/apps/app_discovery_service/almanac_api"
+ sources = [ "launcher_app.proto" ]
+ generate_cc = false
+ generate_python = false
+ generate_descriptor = "launcher_app.descriptor"
+ proto_deps = [ "//chrome/browser/apps/almanac_api_client/proto" ]
+}
diff --git a/chromium/chrome/browser/apps/app_discovery_service/recommended_arc_apps/BUILD.gn b/chromium/chrome/browser/apps/app_discovery_service/recommended_arc_apps/BUILD.gn
new file mode 100644
index 00000000000..9f863d4db4e
--- /dev/null
+++ b/chromium/chrome/browser/apps/app_discovery_service/recommended_arc_apps/BUILD.gn
@@ -0,0 +1,84 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+source_set("recommended_arc_apps") {
+ sources = [
+ "fake_recommend_apps_fetcher.cc",
+ "fake_recommend_apps_fetcher.h",
+ "recommend_apps_fetcher.cc",
+ "recommend_apps_fetcher.h",
+ "recommend_apps_fetcher_delegate.h",
+ "recommend_apps_fetcher_impl.cc",
+ "recommend_apps_fetcher_impl.h",
+ ]
+
+ deps = [
+ ":device_configuration_proto",
+ "//ash",
+ "//ash/components/arc",
+ "//ash/constants",
+ "//base",
+ "//chrome/browser/profiles:profile",
+ "//chromeos/crosapi/mojom",
+ "//content/public/browser",
+ "//extensions/browser",
+ "//extensions/common/api",
+ "//gpu/config",
+ "//services/data_decoder/public/cpp",
+ "//services/network/public/cpp",
+ "//third_party/zlib/google:compression_utils",
+ "//ui/display",
+ "//ui/events/devices",
+ ]
+}
+
+proto_library("device_configuration_proto") {
+ sources = [ "device_configuration.proto" ]
+ generate_python = false
+}
+
+source_set("test_support") {
+ testonly = true
+
+ sources = [
+ "fake_recommend_apps_fetcher_delegate.cc",
+ "fake_recommend_apps_fetcher_delegate.h",
+ "scoped_test_recommend_apps_fetcher_factory.cc",
+ "scoped_test_recommend_apps_fetcher_factory.h",
+ ]
+
+ deps = [
+ ":recommended_arc_apps",
+ "//base",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [ "recommend_apps_fetcher_impl_unittest.cc" ]
+
+ deps = [
+ ":recommended_arc_apps",
+ ":test_support",
+ "//ash/components/arc",
+ "//base",
+ "//chrome/test:test_support",
+ "//chromeos/crosapi/mojom:mojom",
+ "//components/user_manager",
+ "//content/test:test_support",
+ "//gpu/config",
+ "//mojo/public/cpp/bindings",
+ "//services/data_decoder/public/cpp:test_support",
+ "//services/network:test_support",
+ "//testing/gtest",
+ "//third_party/zlib/google:compression_utils",
+ "//ui/display",
+ "//ui/display:test_support",
+ "//ui/display/util",
+ "//ui/events/devices:test_support",
+ ]
+}
diff --git a/chromium/chrome/browser/apps/app_preload_service/BUILD.gn b/chromium/chrome/browser/apps/app_preload_service/BUILD.gn
index 7fcf491372d..bfbe494e2d3 100644
--- a/chromium/chrome/browser/apps/app_preload_service/BUILD.gn
+++ b/chromium/chrome/browser/apps/app_preload_service/BUILD.gn
@@ -36,8 +36,7 @@ source_set("app_preload_service") {
"//components/keyed_service/core",
"//components/pref_registry",
"//components/prefs",
- "//components/services/app_service/public/cpp:app_types",
- "//components/services/app_service/public/cpp:types",
+ "//components/services/app_service",
"//components/user_manager",
"//components/webapps/browser:constants",
"//google_apis",
@@ -66,7 +65,7 @@ source_set("unit_tests") {
"//chrome/browser/web_applications:web_applications_test_support",
"//chrome/test:test_support",
"//components/prefs",
- "//components/services/app_service/public/cpp:app_update",
+ "//components/services/app_service",
"//components/user_manager",
"//components/version_info",
"//content/test:test_support",
@@ -93,6 +92,7 @@ source_set("browser_tests") {
"//base",
"//chrome/browser/apps/app_preload_service/proto",
"//chrome/browser/apps/app_service",
+ "//chrome/browser/apps/app_service:app_registry_cache_waiter",
"//chrome/browser/ash/crosapi",
"//chrome/browser/ui",
"//chrome/browser/web_applications",
@@ -100,8 +100,7 @@ source_set("browser_tests") {
"//chrome/common:chrome_features",
"//chrome/test:test_support",
"//chrome/test:test_support_ui",
- "//components/services/app_service/public/cpp:app_types",
- "//components/services/app_service/public/cpp:app_update",
+ "//components/services/app_service",
"//components/user_manager",
"//components/webapps/browser:constants",
"//content/test:test_support",
diff --git a/chromium/chrome/browser/apps/app_provisioning_service/BUILD.gn b/chromium/chrome/browser/apps/app_provisioning_service/BUILD.gn
new file mode 100644
index 00000000000..8cc1b6ffbca
--- /dev/null
+++ b/chromium/chrome/browser/apps/app_provisioning_service/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+# App Provisioning Service is Ash-only.
+assert(is_chromeos_ash)
+
+source_set("app_provisioning_service") {
+ sources = [
+ "app_provisioning_data_manager.cc",
+ "app_provisioning_data_manager.h",
+ ]
+
+ public_deps = [ "//chrome/browser/apps/app_provisioning_service/proto" ]
+
+ deps = [ "//base" ]
+}
diff --git a/chromium/chrome/browser/apps/app_service/BUILD.gn b/chromium/chrome/browser/apps/app_service/BUILD.gn
index a7fd0910564..4a4cf59b73a 100644
--- a/chromium/chrome/browser/apps/app_service/BUILD.gn
+++ b/chromium/chrome/browser/apps/app_service/BUILD.gn
@@ -18,6 +18,7 @@ source_set("app_service") {
"app_icon/app_icon_util.h",
"app_icon/dip_px_util.cc",
"app_icon/dip_px_util.h",
+ "app_icon/icon_effects.h",
"app_icon/icon_key_util.cc",
"app_icon/icon_key_util.h",
"app_service_proxy.h",
@@ -36,6 +37,8 @@ source_set("app_service") {
"metrics/app_service_metrics.h",
"package_id.cc",
"package_id.h",
+ "package_id_util.cc",
+ "package_id_util.h",
"paused_apps.cc",
"paused_apps.h",
"policy_util.cc",
@@ -87,8 +90,6 @@ source_set("app_service") {
sources += [
"app_notifications.cc",
"app_notifications.h",
- "app_web_contents_data.cc",
- "app_web_contents_data.h",
"browser_app_instance.cc",
"browser_app_instance.h",
"browser_app_instance_map.h",
@@ -126,8 +127,6 @@ source_set("app_service") {
"app_icon/app_icon_writer.h",
"app_icon/arc_activity_adaptive_icon_impl.cc",
"app_icon/arc_activity_adaptive_icon_impl.h",
- "app_icon/arc_icon_once_loader.cc",
- "app_icon/arc_icon_once_loader.h",
"app_service_proxy_ash.cc",
"app_service_proxy_ash.h",
"app_shortcut_item.cc",
@@ -238,6 +237,7 @@ source_set("app_service") {
"//chrome/browser/image_decoder",
"//chrome/browser/metrics/structured:features",
"//chrome/browser/resources",
+ "//chromeos/ash/components/browser_context_helper",
"//chromeos/ash/components/login/login_state",
"//chromeos/components/kiosk:kiosk",
"//components/app_restore",
@@ -248,7 +248,8 @@ source_set("app_service") {
"//components/image_fetcher/core",
"//components/metrics/structured:structured_events",
"//components/resources:components_resources",
- "//components/services/app_service/public/cpp/shortcut",
+ "//components/services/app_service/public/cpp:instance_update",
+ "//components/services/app_service/public/cpp:macros",
"//components/ukm",
"//components/webapps/browser",
"//services/metrics/public/cpp:ukm_builders",
@@ -312,8 +313,7 @@ source_set("constants") {
deps = [
"//base",
- "//components/services/app_service/public/cpp:app_types",
- "//components/services/app_service/public/cpp:intents",
+ "//components/services/app_service",
"//ui/base:types",
"//ui/display/types",
"//ui/gfx/geometry",
@@ -435,7 +435,12 @@ source_set("unit_tests") {
}
if (is_chromeos_lacros) {
- sources += [ "app_service_proxy_lacros_unittest.cc" ]
+ sources += [
+ "app_service_proxy_lacros_unittest.cc",
+ "metrics/website_metrics_service_lacros_unittest.cc",
+ ]
+ } else {
+ sources += [ "publishers/app_publisher_unittest.cc" ]
}
}
@@ -477,3 +482,20 @@ source_set("test_support") {
]
}
}
+
+source_set("app_registry_cache_waiter") {
+ testonly = true
+
+ sources = [
+ "app_registry_cache_waiter.cc",
+ "app_registry_cache_waiter.h",
+ ]
+
+ public_deps = [ "//components/services/app_service" ]
+
+ deps = [
+ ":app_service",
+ "//build:chromeos_buildflags",
+ "//url",
+ ]
+}
diff --git a/chromium/chrome/browser/apps/app_shim/BUILD.gn b/chromium/chrome/browser/apps/app_shim/BUILD.gn
index 9469130f9e0..9ea9942fcd9 100644
--- a/chromium/chrome/browser/apps/app_shim/BUILD.gn
+++ b/chromium/chrome/browser/apps/app_shim/BUILD.gn
@@ -22,10 +22,7 @@ source_set("app_shim") {
"web_app_shim_manager_delegate_mac.h",
]
- configs += [
- "//build/config/compiler:enable_arc",
- "//build/config/compiler:wexit_time_destructors",
- ]
+ configs += [ "//build/config/compiler:wexit_time_destructors" ]
deps = [
"//apps",
diff --git a/chromium/chrome/browser/apps/link_capturing/BUILD.gn b/chromium/chrome/browser/apps/link_capturing/BUILD.gn
new file mode 100644
index 00000000000..de191a8babb
--- /dev/null
+++ b/chromium/chrome/browser/apps/link_capturing/BUILD.gn
@@ -0,0 +1,52 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/browser/buildflags.gni")
+
+source_set("link_capturing") {
+ sources = [
+ "link_capturing_navigation_throttle.cc",
+ "link_capturing_navigation_throttle.h",
+ ]
+
+ deps = [
+ "//base",
+ "//chrome/browser/profiles:profile",
+ "//chrome/browser/web_applications",
+ "//components/keep_alive_registry",
+ "//components/page_load_metrics/browser",
+ "//content/public/browser",
+ "//extensions/common",
+ "//third_party/abseil-cpp:absl",
+ "//url",
+ ]
+
+ if (is_chromeos) {
+ sources += [
+ "chromeos_link_capturing_delegate.cc",
+ "chromeos_link_capturing_delegate.h",
+ ]
+ deps += [
+ "//ash/webui/projector_app/public/cpp",
+ "//chrome/browser/apps/app_service",
+ ]
+ } else {
+ sources += [
+ "web_app_link_capturing_delegate.cc",
+ "web_app_link_capturing_delegate.h",
+ ]
+ }
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "link_capturing_navigation_throttle_unittest.cc" ]
+
+ deps = [
+ ":link_capturing",
+ "//testing/gtest",
+ "//ui/base:base",
+ "//url",
+ ]
+}
diff --git a/chromium/chrome/browser/apps/platform_apps/BUILD.gn b/chromium/chrome/browser/apps/platform_apps/BUILD.gn
index 791926d3f4c..1262277b9a7 100644
--- a/chromium/chrome/browser/apps/platform_apps/BUILD.gn
+++ b/chromium/chrome/browser/apps/platform_apps/BUILD.gn
@@ -66,7 +66,7 @@ source_set("platform_apps") {
"//components/navigation_interception",
"//components/no_state_prefetch/browser",
"//components/pref_registry",
- "//components/services/app_service/public/cpp:app_types",
+ "//components/services/app_service",
"//content/public/browser",
"//content/public/common",
"//extensions/browser",
diff --git a/chromium/chrome/browser/ash/BUILD.gn b/chromium/chrome/browser/ash/BUILD.gn
index d0dedab5b0f..464e889240f 100644
--- a/chromium/chrome/browser/ash/BUILD.gn
+++ b/chromium/chrome/browser/ash/BUILD.gn
@@ -56,10 +56,14 @@ source_set("ash") {
"accessibility/select_to_speak_event_handler_delegate_impl.h",
"accessibility/service/accessibility_service_client.cc",
"accessibility/service/accessibility_service_client.h",
+ "accessibility/service/accessibility_service_devtools_delegate.cc",
+ "accessibility/service/accessibility_service_devtools_delegate.h",
"accessibility/service/automation_client_impl.cc",
"accessibility/service/automation_client_impl.h",
"accessibility/service/tts_client_impl.cc",
"accessibility/service/tts_client_impl.h",
+ "accessibility/service/user_interface_impl.cc",
+ "accessibility/service/user_interface_impl.h",
"account_manager/account_apps_availability.cc",
"account_manager/account_apps_availability.h",
"account_manager/account_apps_availability_factory.cc",
@@ -99,10 +103,12 @@ source_set("ash") {
"android_sms/connection_manager.h",
"android_sms/fcm_connection_establisher.cc",
"android_sms/fcm_connection_establisher.h",
+ "app_list/search/local_image_search/local_image_search_service.cc",
+ "app_list/search/local_image_search/local_image_search_service.h",
+ "app_list/search/local_image_search/local_image_search_service_factory.cc",
+ "app_list/search/local_image_search/local_image_search_service_factory.h",
"app_mode/app_launch_utils.cc",
"app_mode/app_launch_utils.h",
- "app_mode/app_session_ash.cc",
- "app_mode/app_session_ash.h",
"app_mode/arc/arc_kiosk_app_data.cc",
"app_mode/arc/arc_kiosk_app_data.h",
"app_mode/arc/arc_kiosk_app_launcher.cc",
@@ -113,6 +119,7 @@ source_set("ash") {
"app_mode/arc/arc_kiosk_app_service.h",
"app_mode/arc/arc_kiosk_app_service_factory.cc",
"app_mode/arc/arc_kiosk_app_service_factory.h",
+ "app_mode/cancellable_job.h",
"app_mode/certificate_manager_dialog.cc",
"app_mode/certificate_manager_dialog.h",
"app_mode/kiosk_app_data.cc",
@@ -147,6 +154,8 @@ source_set("ash") {
"app_mode/kiosk_mode_idle_app_name_notification.h",
"app_mode/kiosk_profile_loader.cc",
"app_mode/kiosk_profile_loader.h",
+ "app_mode/kiosk_system_session.cc",
+ "app_mode/kiosk_system_session.h",
"app_mode/lacros_launcher.cc",
"app_mode/lacros_launcher.h",
"app_mode/metrics/low_disk_metrics_service.cc",
@@ -157,6 +166,8 @@ source_set("ash") {
"app_mode/metrics/periodic_metrics_service.h",
"app_mode/pref_names.cc",
"app_mode/pref_names.h",
+ "app_mode/retry_runner.cc",
+ "app_mode/retry_runner.h",
"app_mode/startup_app_launcher.cc",
"app_mode/startup_app_launcher.h",
"app_mode/web_app/web_kiosk_app_data.cc",
@@ -217,6 +228,10 @@ source_set("ash") {
"arc/accessibility/arc_accessibility_tree_tracker.h",
"arc/accessibility/arc_accessibility_util.cc",
"arc/accessibility/arc_accessibility_util.h",
+ "arc/accessibility/arc_serialization_delegate.cc",
+ "arc/accessibility/arc_serialization_delegate.h",
+ "arc/accessibility/geometry_util.cc",
+ "arc/accessibility/geometry_util.h",
"arc/adbd/arc_adbd_monitor_bridge.cc",
"arc/adbd/arc_adbd_monitor_bridge.h",
"arc/app_shortcuts/arc_app_shortcuts_menu_builder.cc",
@@ -383,10 +398,18 @@ source_set("ash") {
"arc/input_overlay/touch_injector_observer.h",
"arc/input_overlay/ui/action_label.cc",
"arc/input_overlay/ui/action_label.h",
+ "arc/input_overlay/ui/action_type_button.cc",
+ "arc/input_overlay/ui/action_type_button.h",
+ "arc/input_overlay/ui/action_type_button_group.cc",
+ "arc/input_overlay/ui/action_type_button_group.h",
"arc/input_overlay/ui/action_view.cc",
"arc/input_overlay/ui/action_view.h",
"arc/input_overlay/ui/action_view_list_item.cc",
"arc/input_overlay/ui/action_view_list_item.h",
+ "arc/input_overlay/ui/arrow_container.cc",
+ "arc/input_overlay/ui/arrow_container.h",
+ "arc/input_overlay/ui/button_label_list.cc",
+ "arc/input_overlay/ui/button_label_list.h",
"arc/input_overlay/ui/button_options_menu.cc",
"arc/input_overlay/ui/button_options_menu.h",
"arc/input_overlay/ui/edit_finish_view.cc",
@@ -441,12 +464,10 @@ source_set("ash") {
"arc/intent_helper/arc_intent_helper_mojo_ash.h",
"arc/intent_helper/arc_settings_service.cc",
"arc/intent_helper/arc_settings_service.h",
- "arc/intent_helper/chrome_arc_settings_app_delegate.cc",
- "arc/intent_helper/chrome_arc_settings_app_delegate.h",
+ "arc/intent_helper/chrome_arc_intent_helper_delegate.cc",
+ "arc/intent_helper/chrome_arc_intent_helper_delegate.h",
"arc/intent_helper/custom_tab_session_impl.cc",
"arc/intent_helper/custom_tab_session_impl.h",
- "arc/intent_helper/factory_reset_delegate.cc",
- "arc/intent_helper/factory_reset_delegate.h",
"arc/keymaster/arc_keymaster_bridge.cc",
"arc/keymaster/arc_keymaster_bridge.h",
"arc/keymaster/cert_store_bridge.cc",
@@ -660,6 +681,8 @@ source_set("ash") {
"bluetooth/hats_bluetooth_revamp_trigger_impl.h",
"boot_times_recorder.cc",
"boot_times_recorder.h",
+ "boot_times_recorder_tab_helper.cc",
+ "boot_times_recorder_tab_helper.h",
"borealis/borealis_app_launcher.cc",
"borealis/borealis_app_launcher.h",
"borealis/borealis_app_launcher_impl.cc",
@@ -692,8 +715,6 @@ source_set("ash") {
"borealis/borealis_installer_impl.h",
"borealis/borealis_launch_options.cc",
"borealis/borealis_launch_options.h",
- "borealis/borealis_launch_watcher.cc",
- "borealis/borealis_launch_watcher.h",
"borealis/borealis_metrics.cc",
"borealis/borealis_metrics.h",
"borealis/borealis_power_controller.cc",
@@ -730,14 +751,14 @@ source_set("ash") {
"browser_context_keyed_service_factories.h",
"bruschetta/bruschetta_download.cc",
"bruschetta/bruschetta_download.h",
- "bruschetta/bruschetta_download_client.cc",
- "bruschetta/bruschetta_download_client.h",
"bruschetta/bruschetta_features.cc",
"bruschetta/bruschetta_features.h",
"bruschetta/bruschetta_installer.cc",
"bruschetta/bruschetta_installer.h",
"bruschetta/bruschetta_installer_impl.cc",
"bruschetta/bruschetta_installer_impl.h",
+ "bruschetta/bruschetta_installer_policy_handler.cc",
+ "bruschetta/bruschetta_installer_policy_handler.h",
"bruschetta/bruschetta_launcher.cc",
"bruschetta/bruschetta_launcher.h",
"bruschetta/bruschetta_mount_provider.cc",
@@ -1021,6 +1042,10 @@ source_set("ash") {
"drive/fileapi/drivefs_async_file_util.h",
"drive/fileapi/drivefs_file_system_backend_delegate.cc",
"drive/fileapi/drivefs_file_system_backend_delegate.h",
+ "early_prefs/early_prefs_export_service.cc",
+ "early_prefs/early_prefs_export_service.h",
+ "early_prefs/early_prefs_export_service_factory.cc",
+ "early_prefs/early_prefs_export_service_factory.h",
"eche_app/app_id.h",
"eche_app/eche_app_manager_factory.cc",
"eche_app/eche_app_manager_factory.h",
@@ -1080,6 +1105,8 @@ source_set("ash") {
"file_manager/app_service_file_tasks.h",
"file_manager/arc_file_tasks.cc",
"file_manager/arc_file_tasks.h",
+ "file_manager/cloud_upload_prefs_watcher.cc",
+ "file_manager/cloud_upload_prefs_watcher.h",
"file_manager/copy_or_move_io_task.cc",
"file_manager/copy_or_move_io_task.h",
"file_manager/copy_or_move_io_task_impl.cc",
@@ -1270,6 +1297,8 @@ source_set("ash") {
"fileapi/external_file_url_loader_factory.h",
"fileapi/external_file_url_util.cc",
"fileapi/external_file_url_util.h",
+ "fileapi/fallback_copy_in_foreign_file.cc",
+ "fileapi/fallback_copy_in_foreign_file.h",
"fileapi/file_access_permissions.cc",
"fileapi/file_access_permissions.h",
"fileapi/file_change_service.cc",
@@ -1381,6 +1410,7 @@ source_set("ash") {
"guest_os/public/guest_os_wayland_server.h",
"guest_os/public/installer_delegate_factory.cc",
"guest_os/public/installer_delegate_factory.h",
+ "guest_os/public/types.cc",
"guest_os/public/types.h",
"guest_os/virtual_machines/virtual_machines_util.cc",
"guest_os/virtual_machines/virtual_machines_util.h",
@@ -1427,6 +1457,21 @@ source_set("ash") {
"input_method/diacritics_checker.h",
"input_method/diacritics_insensitive_string_comparator.cc",
"input_method/diacritics_insensitive_string_comparator.h",
+ "input_method/editor_consent_enums.cc",
+ "input_method/editor_consent_enums.h",
+ "input_method/editor_consent_store.cc",
+ "input_method/editor_consent_store.h",
+ "input_method/editor_event_sink.h",
+ "input_method/editor_instance_impl.cc",
+ "input_method/editor_instance_impl.h",
+ "input_method/editor_mediator.cc",
+ "input_method/editor_mediator.h",
+ "input_method/editor_panel_manager.cc",
+ "input_method/editor_panel_manager.h",
+ "input_method/editor_switch.cc",
+ "input_method/editor_switch.h",
+ "input_method/editor_text_actuator.cc",
+ "input_method/editor_text_actuator.h",
"input_method/emoji_suggester.cc",
"input_method/emoji_suggester.h",
"input_method/field_trial.cc",
@@ -1534,8 +1579,6 @@ source_set("ash") {
"lock_screen_apps/app_manager.h",
"lock_screen_apps/app_manager_impl.cc",
"lock_screen_apps/app_manager_impl.h",
- "lock_screen_apps/app_window_metrics_tracker.cc",
- "lock_screen_apps/app_window_metrics_tracker.h",
"lock_screen_apps/first_app_run_toast_manager.cc",
"lock_screen_apps/first_app_run_toast_manager.h",
"lock_screen_apps/focus_cycler_delegate.h",
@@ -1642,7 +1685,6 @@ source_set("ash") {
"login/login_auth_recorder.h",
"login/login_client_cert_usage_observer.cc",
"login/login_client_cert_usage_observer.h",
- "login/login_pref_names.cc",
"login/login_pref_names.h",
"login/login_screen_extensions_storage_cleaner.cc",
"login/login_screen_extensions_storage_cleaner.h",
@@ -1655,10 +1697,18 @@ source_set("ash") {
"login/onboarding_user_activity_counter.h",
"login/oobe_configuration.cc",
"login/oobe_configuration.h",
+ "login/oobe_metrics_helper.cc",
+ "login/oobe_metrics_helper.h",
"login/oobe_screen.cc",
"login/oobe_screen.h",
+ "login/osauth/auth_policy_enforcer.cc",
+ "login/osauth/auth_policy_enforcer.h",
"login/osauth/chrome_auth_parts.cc",
"login/osauth/chrome_auth_parts.h",
+ "login/osauth/profile_prefs_auth_policy_connector.cc",
+ "login/osauth/profile_prefs_auth_policy_connector.h",
+ "login/osauth/profile_prefs_auth_policy_connector_factory.cc",
+ "login/osauth/profile_prefs_auth_policy_connector_factory.h",
"login/profile_auth_data.cc",
"login/profile_auth_data.h",
"login/quick_unlock/auth_token.cc",
@@ -1685,6 +1735,8 @@ source_set("ash") {
"login/quick_unlock/quick_unlock_storage.h",
"login/quick_unlock/quick_unlock_utils.cc",
"login/quick_unlock/quick_unlock_utils.h",
+ "login/quickstart_controller.cc",
+ "login/quickstart_controller.h",
"login/reauth_stats.cc",
"login/reauth_stats.h",
"login/reporting/lock_unlock_reporter.cc",
@@ -1719,6 +1771,8 @@ source_set("ash") {
"login/saml/saml_profile_prefs.h",
"login/screen_manager.cc",
"login/screen_manager.h",
+ "login/screens/add_child_screen.cc",
+ "login/screens/add_child_screen.h",
"login/screens/app_downloading_screen.cc",
"login/screens/app_downloading_screen.h",
"login/screens/arc_vm_data_migration_screen.cc",
@@ -1735,6 +1789,8 @@ source_set("ash") {
"login/screens/chromevox_hint/chromevox_hint_detector.h",
"login/screens/consolidated_consent_screen.cc",
"login/screens/consolidated_consent_screen.h",
+ "login/screens/consumer_update_screen.cc",
+ "login/screens/consumer_update_screen.h",
"login/screens/core_oobe.cc",
"login/screens/core_oobe.h",
"login/screens/cryptohome_recovery_screen.cc",
@@ -1790,6 +1846,8 @@ source_set("ash") {
"login/screens/lacros_data_backward_migration_screen.h",
"login/screens/lacros_data_migration_screen.cc",
"login/screens/lacros_data_migration_screen.h",
+ "login/screens/local_password_setup_screen.cc",
+ "login/screens/local_password_setup_screen.h",
"login/screens/local_state_error_screen.cc",
"login/screens/local_state_error_screen.h",
"login/screens/locale_switch_notification.cc",
@@ -1816,6 +1874,8 @@ source_set("ash") {
"login/screens/packaged_license_screen.h",
"login/screens/parental_handoff_screen.cc",
"login/screens/parental_handoff_screen.h",
+ "login/screens/password_selection_screen.cc",
+ "login/screens/password_selection_screen.h",
"login/screens/pin_setup_screen.cc",
"login/screens/pin_setup_screen.h",
@@ -1824,13 +1884,6 @@ source_set("ash") {
"login/screens/placeholder_screen.h",
"login/screens/quick_start_screen.cc",
"login/screens/quick_start_screen.h",
- "login/screens/recommend_apps/fake_recommend_apps_fetcher.cc",
- "login/screens/recommend_apps/fake_recommend_apps_fetcher.h",
- "login/screens/recommend_apps/recommend_apps_fetcher.cc",
- "login/screens/recommend_apps/recommend_apps_fetcher.h",
- "login/screens/recommend_apps/recommend_apps_fetcher_delegate.h",
- "login/screens/recommend_apps/recommend_apps_fetcher_impl.cc",
- "login/screens/recommend_apps/recommend_apps_fetcher_impl.h",
"login/screens/recommend_apps_screen.cc",
"login/screens/recommend_apps_screen.h",
"login/screens/recovery_eligibility_screen.cc",
@@ -1988,7 +2041,6 @@ source_set("ash") {
"login/users/default_user_image/default_user_images.h",
"login/users/multi_profile_user_controller.cc",
"login/users/multi_profile_user_controller.h",
- "login/users/multi_profile_user_controller_delegate.h",
"login/users/scoped_test_user_manager.cc",
"login/users/scoped_test_user_manager.h",
"login/users/test_users.cc",
@@ -2020,6 +2072,8 @@ source_set("ash") {
"multidevice_setup/multidevice_setup_service_factory.h",
"multidevice_setup/oobe_completion_tracker_factory.cc",
"multidevice_setup/oobe_completion_tracker_factory.h",
+ "nearby/fake_quick_start_connectivity_service.cc",
+ "nearby/fake_quick_start_connectivity_service.h",
"nearby/nearby_dependencies_provider.cc",
"nearby/nearby_dependencies_provider.h",
"nearby/nearby_dependencies_provider_factory.cc",
@@ -2028,14 +2082,18 @@ source_set("ash") {
"nearby/nearby_process_manager_factory.h",
"nearby/nearby_process_manager_impl.cc",
"nearby/nearby_process_manager_impl.h",
+ "nearby/presence/credential_storage/nearby_presence_credential_storage.cc",
+ "nearby/presence/credential_storage/nearby_presence_credential_storage.h",
"nearby/presence/nearby_presence_service_factory.cc",
"nearby/presence/nearby_presence_service_factory.h",
- "nearby/quick_start_connectivity_service.cc",
- "nearby/quick_start_connectivity_service.h",
"nearby/quick_start_connectivity_service_factory.cc",
"nearby/quick_start_connectivity_service_factory.h",
+ "nearby/quick_start_connectivity_service_impl.cc",
+ "nearby/quick_start_connectivity_service_impl.h",
"net/apn_migrator.cc",
"net/apn_migrator.h",
+ "net/ash_proxy_monitor.cc",
+ "net/ash_proxy_monitor.h",
"net/bluetooth_pref_state_observer.cc",
"net/bluetooth_pref_state_observer.h",
"net/client_cert_filter.cc",
@@ -2144,10 +2202,8 @@ source_set("ash") {
"notifications/kiosk_external_update_notification.h",
"notifications/low_disk_notification.cc",
"notifications/low_disk_notification.h",
- "notifications/multi_capture_login_notification.cc",
- "notifications/multi_capture_login_notification.h",
- "notifications/multi_capture_notification.cc",
- "notifications/multi_capture_notification.h",
+ "notifications/multi_capture_notifications.cc",
+ "notifications/multi_capture_notifications.h",
"notifications/request_system_proxy_credentials_view.cc",
"notifications/request_system_proxy_credentials_view.h",
"notifications/screen_capture_notification_ui_ash.cc",
@@ -2156,6 +2212,10 @@ source_set("ash") {
"notifications/system_proxy_notification.h",
"notifications/tpm_auto_update_notification.cc",
"notifications/tpm_auto_update_notification.h",
+ "notifications/update_notification.cc",
+ "notifications/update_notification.h",
+ "notifications/update_notification_showing_controller.cc",
+ "notifications/update_notification_showing_controller.h",
"notifications/update_required_notification.cc",
"notifications/update_required_notification.h",
"os_feedback/chrome_os_feedback_delegate.cc",
@@ -2309,6 +2369,8 @@ source_set("ash") {
"policy/dlp/dialogs/files_policy_warn_dialog.h",
"policy/dlp/dlp_content_manager_ash.cc",
"policy/dlp/dlp_content_manager_ash.h",
+ "policy/dlp/dlp_extract_io_task_observer.cc",
+ "policy/dlp/dlp_extract_io_task_observer.h",
"policy/dlp/dlp_files_controller_ash.cc",
"policy/dlp/dlp_files_controller_ash.h",
"policy/dlp/dlp_files_event_storage.cc",
@@ -2319,6 +2381,8 @@ source_set("ash") {
"policy/dlp/files_policy_notification_manager.h",
"policy/dlp/files_policy_notification_manager_factory.cc",
"policy/dlp/files_policy_notification_manager_factory.h",
+ "policy/dlp/files_policy_string_util.cc",
+ "policy/dlp/files_policy_string_util.h",
"policy/enrollment/account_status_check_fetcher.cc",
"policy/enrollment/account_status_check_fetcher.h",
"policy/enrollment/auto_enrollment_client.h",
@@ -2482,6 +2546,8 @@ source_set("ash") {
"policy/remote_commands/fake_screenshot_delegate.h",
"policy/remote_commands/screenshot_delegate.cc",
"policy/remote_commands/screenshot_delegate.h",
+ "policy/remote_commands/start_crd_session_job_delegate.cc",
+ "policy/remote_commands/start_crd_session_job_delegate.h",
"policy/remote_commands/user_command_arc_job.cc",
"policy/remote_commands/user_command_arc_job.h",
"policy/remote_commands/user_commands_factory_ash.cc",
@@ -2542,18 +2608,20 @@ source_set("ash") {
"policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.h",
"policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_memory_sampler_handler.cc",
"policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_memory_sampler_handler.h",
+ "policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_psr_sampler_handler.cc",
+ "policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_psr_sampler_handler.h",
"policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_sampler_handler.h",
"policy/reporting/metrics_reporting/cros_reporting_settings.cc",
"policy/reporting/metrics_reporting/cros_reporting_settings.h",
"policy/reporting/metrics_reporting/device_activity/device_activity_sampler.cc",
"policy/reporting/metrics_reporting/device_activity/device_activity_sampler.h",
+ "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.cc",
+ "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h",
"policy/reporting/metrics_reporting/metric_reporting_manager.cc",
"policy/reporting/metrics_reporting/metric_reporting_manager.h",
"policy/reporting/metrics_reporting/metric_reporting_prefs.cc",
"policy/reporting/metrics_reporting/metric_reporting_prefs.h",
"policy/reporting/metrics_reporting/mojo_service_events_observer_base.h",
- "policy/reporting/metrics_reporting/network/fake_network_diagnostics_util.cc",
- "policy/reporting/metrics_reporting/network/fake_network_diagnostics_util.h",
"policy/reporting/metrics_reporting/network/https_latency_event_detector.cc",
"policy/reporting/metrics_reporting/network/https_latency_event_detector.h",
"policy/reporting/metrics_reporting/network/https_latency_sampler.cc",
@@ -2969,6 +3037,9 @@ source_set("ash") {
"sharesheet/drive_share_action.h",
"shimless_rma/chrome_shimless_rma_delegate.cc",
"shimless_rma/chrome_shimless_rma_delegate.h",
+ "shimless_rma/diagnostics_app_profile_helper.cc",
+ "shimless_rma/diagnostics_app_profile_helper.h",
+ "shimless_rma/diagnostics_app_profile_helper_constants.h",
"shortcut_mapping_pref_service.cc",
"shortcut_mapping_pref_service.h",
"smb_client/discovery/host_locator.h",
@@ -3065,6 +3136,8 @@ source_set("ash") {
"system/timezone_util.h",
"system/user_removal_manager.cc",
"system/user_removal_manager.h",
+ "system_logs/app_service_log_source.cc",
+ "system_logs/app_service_log_source.h",
"system_logs/bluetooth_log_source.cc",
"system_logs/bluetooth_log_source.h",
"system_logs/command_line_log_source.cc",
@@ -3109,6 +3182,118 @@ source_set("ash") {
"system_logs/virtual_keyboard_log_source.h",
"system_token_cert_db_initializer.cc",
"system_token_cert_db_initializer.h",
+ "system_web_apps/apps/calculator_app/calculator_app_utils.cc",
+ "system_web_apps/apps/calculator_app/calculator_app_utils.h",
+ "system_web_apps/apps/camera_app/camera_app_survey_handler.cc",
+ "system_web_apps/apps/camera_app/camera_app_survey_handler.h",
+ "system_web_apps/apps/camera_app/camera_app_untrusted_ui_config.cc",
+ "system_web_apps/apps/camera_app/camera_app_untrusted_ui_config.h",
+ "system_web_apps/apps/camera_app/camera_system_web_app_info.cc",
+ "system_web_apps/apps/camera_app/camera_system_web_app_info.h",
+ "system_web_apps/apps/camera_app/chrome_camera_app_ui_constants.h",
+ "system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.cc",
+ "system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.h",
+ "system_web_apps/apps/chrome_demo_mode_app_delegate.cc",
+ "system_web_apps/apps/chrome_demo_mode_app_delegate.h",
+ "system_web_apps/apps/chrome_file_manager_ui_delegate.cc",
+ "system_web_apps/apps/chrome_file_manager_ui_delegate.h",
+ "system_web_apps/apps/connectivity_diagnostics_system_web_app_info.cc",
+ "system_web_apps/apps/connectivity_diagnostics_system_web_app_info.h",
+ "system_web_apps/apps/crosh_system_web_app_info.cc",
+ "system_web_apps/apps/crosh_system_web_app_info.h",
+ "system_web_apps/apps/crosh_ui.cc",
+ "system_web_apps/apps/crosh_ui.h",
+ "system_web_apps/apps/demo_mode_web_app_info.cc",
+ "system_web_apps/apps/demo_mode_web_app_info.h",
+ "system_web_apps/apps/diagnostics_system_web_app_info.cc",
+ "system_web_apps/apps/diagnostics_system_web_app_info.h",
+ "system_web_apps/apps/eche_app_info.cc",
+ "system_web_apps/apps/eche_app_info.h",
+ "system_web_apps/apps/face_ml/chrome_face_ml_user_provider.cc",
+ "system_web_apps/apps/face_ml/chrome_face_ml_user_provider.h",
+ "system_web_apps/apps/face_ml_system_web_app_info.cc",
+ "system_web_apps/apps/face_ml_system_web_app_info.h",
+ "system_web_apps/apps/file_manager_web_app_info.cc",
+ "system_web_apps/apps/file_manager_web_app_info.h",
+ "system_web_apps/apps/files_internals_debug_json_provider.h",
+ "system_web_apps/apps/files_internals_ui_delegate.cc",
+ "system_web_apps/apps/files_internals_ui_delegate.h",
+ "system_web_apps/apps/firmware_update_system_web_app_info.cc",
+ "system_web_apps/apps/firmware_update_system_web_app_info.h",
+ "system_web_apps/apps/help_app/help_app_discover_tab_notification.cc",
+ "system_web_apps/apps/help_app/help_app_discover_tab_notification.h",
+ "system_web_apps/apps/help_app/help_app_notification_controller.cc",
+ "system_web_apps/apps/help_app/help_app_notification_controller.h",
+ "system_web_apps/apps/help_app/help_app_ui_delegate.cc",
+ "system_web_apps/apps/help_app/help_app_ui_delegate.h",
+ "system_web_apps/apps/help_app/help_app_untrusted_ui_config.cc",
+ "system_web_apps/apps/help_app/help_app_untrusted_ui_config.h",
+ "system_web_apps/apps/help_app/help_app_web_app_info.cc",
+ "system_web_apps/apps/help_app/help_app_web_app_info.h",
+ "system_web_apps/apps/media_app/chrome_media_app_ui_delegate.cc",
+ "system_web_apps/apps/media_app/chrome_media_app_ui_delegate.h",
+ "system_web_apps/apps/media_app/media_app_guest_ui_config.cc",
+ "system_web_apps/apps/media_app/media_app_guest_ui_config.h",
+ "system_web_apps/apps/media_app/media_web_app_info.cc",
+ "system_web_apps/apps/media_app/media_web_app_info.h",
+ "system_web_apps/apps/os_feedback_system_web_app_info.cc",
+ "system_web_apps/apps/os_feedback_system_web_app_info.h",
+ "system_web_apps/apps/os_flags_system_web_app_info.cc",
+ "system_web_apps/apps/os_flags_system_web_app_info.h",
+ "system_web_apps/apps/os_settings_web_app_info.cc",
+ "system_web_apps/apps/os_settings_web_app_info.h",
+ "system_web_apps/apps/os_url_handler_system_web_app_info.cc",
+ "system_web_apps/apps/os_url_handler_system_web_app_info.h",
+ "system_web_apps/apps/personalization_app/ambient_video_albums.cc",
+ "system_web_apps/apps/personalization_app/ambient_video_albums.h",
+ "system_web_apps/apps/personalization_app/enterprise_policy_delegate_impl.cc",
+ "system_web_apps/apps/personalization_app/enterprise_policy_delegate_impl.h",
+ "system_web_apps/apps/personalization_app/keyboard_backlight_color_metrics_provider.cc",
+ "system_web_apps/apps/personalization_app/keyboard_backlight_color_metrics_provider.h",
+ "system_web_apps/apps/personalization_app/personalization_app_ambient_provider_impl.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_ambient_provider_impl.h",
+ "system_web_apps/apps/personalization_app/personalization_app_keyboard_backlight_provider_impl.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_keyboard_backlight_provider_impl.h",
+ "system_web_apps/apps/personalization_app/personalization_app_manager.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_manager.h",
+ "system_web_apps/apps/personalization_app/personalization_app_manager_factory.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_manager_factory.h",
+ "system_web_apps/apps/personalization_app/personalization_app_metrics.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_metrics.h",
+ "system_web_apps/apps/personalization_app/personalization_app_theme_metrics_provider.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_theme_metrics_provider.h",
+ "system_web_apps/apps/personalization_app/personalization_app_theme_provider_impl.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_theme_provider_impl.h",
+ "system_web_apps/apps/personalization_app/personalization_app_user_provider_impl.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_user_provider_impl.h",
+ "system_web_apps/apps/personalization_app/personalization_app_utils.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_utils.h",
+ "system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.h",
+ "system_web_apps/apps/personalization_app/personalization_system_app_delegate.cc",
+ "system_web_apps/apps/personalization_app/personalization_system_app_delegate.h",
+ "system_web_apps/apps/print_management_web_app_info.cc",
+ "system_web_apps/apps/print_management_web_app_info.h",
+ "system_web_apps/apps/projector_app/untrusted_projector_annotator_ui_config.cc",
+ "system_web_apps/apps/projector_app/untrusted_projector_annotator_ui_config.h",
+ "system_web_apps/apps/projector_app/untrusted_projector_ui_config.cc",
+ "system_web_apps/apps/projector_app/untrusted_projector_ui_config.h",
+ "system_web_apps/apps/projector_system_web_app_info.cc",
+ "system_web_apps/apps/projector_system_web_app_info.h",
+ "system_web_apps/apps/scanning_system_web_app_info.cc",
+ "system_web_apps/apps/scanning_system_web_app_info.h",
+ "system_web_apps/apps/shimless_rma_system_web_app_info.cc",
+ "system_web_apps/apps/shimless_rma_system_web_app_info.h",
+ "system_web_apps/apps/shortcut_customization_system_web_app_info.cc",
+ "system_web_apps/apps/shortcut_customization_system_web_app_info.h",
+ "system_web_apps/apps/system_web_app_install_utils.cc",
+ "system_web_apps/apps/system_web_app_install_utils.h",
+ "system_web_apps/apps/terminal_source.cc",
+ "system_web_apps/apps/terminal_source.h",
+ "system_web_apps/apps/terminal_system_web_app_info.cc",
+ "system_web_apps/apps/terminal_system_web_app_info.h",
+ "system_web_apps/apps/terminal_ui.cc",
+ "system_web_apps/apps/terminal_ui.h",
"tether/fake_tether_service.cc",
"tether/fake_tether_service.h",
"tether/tether_service.cc",
@@ -3147,115 +3332,6 @@ source_set("ash") {
"wallpaper_handlers/wallpaper_handlers_metric_utils.h",
"wallpaper_handlers/wallpaper_prefs.cc",
"wallpaper_handlers/wallpaper_prefs.h",
- "web_applications/calculator_app/calculator_app_utils.cc",
- "web_applications/calculator_app/calculator_app_utils.h",
- "web_applications/camera_app/camera_app_survey_handler.cc",
- "web_applications/camera_app/camera_app_survey_handler.h",
- "web_applications/camera_app/camera_app_untrusted_ui_config.cc",
- "web_applications/camera_app/camera_app_untrusted_ui_config.h",
- "web_applications/camera_app/camera_system_web_app_info.cc",
- "web_applications/camera_app/camera_system_web_app_info.h",
- "web_applications/camera_app/chrome_camera_app_ui_constants.h",
- "web_applications/camera_app/chrome_camera_app_ui_delegate.cc",
- "web_applications/camera_app/chrome_camera_app_ui_delegate.h",
- "web_applications/chrome_file_manager_ui_delegate.cc",
- "web_applications/chrome_file_manager_ui_delegate.h",
- "web_applications/connectivity_diagnostics_system_web_app_info.cc",
- "web_applications/connectivity_diagnostics_system_web_app_info.h",
- "web_applications/crosh_system_web_app_info.cc",
- "web_applications/crosh_system_web_app_info.h",
- "web_applications/crosh_ui.cc",
- "web_applications/crosh_ui.h",
- "web_applications/demo_mode_web_app_info.cc",
- "web_applications/demo_mode_web_app_info.h",
- "web_applications/diagnostics_system_web_app_info.cc",
- "web_applications/diagnostics_system_web_app_info.h",
- "web_applications/eche_app_info.cc",
- "web_applications/eche_app_info.h",
- "web_applications/face_ml/chrome_face_ml_user_provider.cc",
- "web_applications/face_ml/chrome_face_ml_user_provider.h",
- "web_applications/face_ml_system_web_app_info.cc",
- "web_applications/face_ml_system_web_app_info.h",
- "web_applications/file_manager_web_app_info.cc",
- "web_applications/file_manager_web_app_info.h",
- "web_applications/files_internals_ui_delegate.cc",
- "web_applications/files_internals_ui_delegate.h",
- "web_applications/firmware_update_system_web_app_info.cc",
- "web_applications/firmware_update_system_web_app_info.h",
- "web_applications/help_app/help_app_discover_tab_notification.cc",
- "web_applications/help_app/help_app_discover_tab_notification.h",
- "web_applications/help_app/help_app_notification_controller.cc",
- "web_applications/help_app/help_app_notification_controller.h",
- "web_applications/help_app/help_app_ui_delegate.cc",
- "web_applications/help_app/help_app_ui_delegate.h",
- "web_applications/help_app/help_app_untrusted_ui_config.cc",
- "web_applications/help_app/help_app_untrusted_ui_config.h",
- "web_applications/help_app/help_app_web_app_info.cc",
- "web_applications/help_app/help_app_web_app_info.h",
- "web_applications/media_app/chrome_media_app_ui_delegate.cc",
- "web_applications/media_app/chrome_media_app_ui_delegate.h",
- "web_applications/media_app/media_app_guest_ui_config.cc",
- "web_applications/media_app/media_app_guest_ui_config.h",
- "web_applications/media_app/media_web_app_info.cc",
- "web_applications/media_app/media_web_app_info.h",
- "web_applications/os_feedback_system_web_app_info.cc",
- "web_applications/os_feedback_system_web_app_info.h",
- "web_applications/os_flags_system_web_app_info.cc",
- "web_applications/os_flags_system_web_app_info.h",
- "web_applications/os_settings_web_app_info.cc",
- "web_applications/os_settings_web_app_info.h",
- "web_applications/os_url_handler_system_web_app_info.cc",
- "web_applications/os_url_handler_system_web_app_info.h",
- "web_applications/personalization_app/ambient_video_albums.cc",
- "web_applications/personalization_app/ambient_video_albums.h",
- "web_applications/personalization_app/enterprise_policy_delegate_impl.cc",
- "web_applications/personalization_app/enterprise_policy_delegate_impl.h",
- "web_applications/personalization_app/keyboard_backlight_color_metrics_provider.cc",
- "web_applications/personalization_app/keyboard_backlight_color_metrics_provider.h",
- "web_applications/personalization_app/personalization_app_ambient_provider_impl.cc",
- "web_applications/personalization_app/personalization_app_ambient_provider_impl.h",
- "web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.cc",
- "web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.h",
- "web_applications/personalization_app/personalization_app_manager.cc",
- "web_applications/personalization_app/personalization_app_manager.h",
- "web_applications/personalization_app/personalization_app_manager_factory.cc",
- "web_applications/personalization_app/personalization_app_manager_factory.h",
- "web_applications/personalization_app/personalization_app_metrics.cc",
- "web_applications/personalization_app/personalization_app_metrics.h",
- "web_applications/personalization_app/personalization_app_theme_metrics_provider.cc",
- "web_applications/personalization_app/personalization_app_theme_metrics_provider.h",
- "web_applications/personalization_app/personalization_app_theme_provider_impl.cc",
- "web_applications/personalization_app/personalization_app_theme_provider_impl.h",
- "web_applications/personalization_app/personalization_app_user_provider_impl.cc",
- "web_applications/personalization_app/personalization_app_user_provider_impl.h",
- "web_applications/personalization_app/personalization_app_utils.cc",
- "web_applications/personalization_app/personalization_app_utils.h",
- "web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc",
- "web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h",
- "web_applications/personalization_app/personalization_system_app_delegate.cc",
- "web_applications/personalization_app/personalization_system_app_delegate.h",
- "web_applications/print_management_web_app_info.cc",
- "web_applications/print_management_web_app_info.h",
- "web_applications/projector_app/untrusted_projector_annotator_ui_config.cc",
- "web_applications/projector_app/untrusted_projector_annotator_ui_config.h",
- "web_applications/projector_app/untrusted_projector_ui_config.cc",
- "web_applications/projector_app/untrusted_projector_ui_config.h",
- "web_applications/projector_system_web_app_info.cc",
- "web_applications/projector_system_web_app_info.h",
- "web_applications/scanning_system_web_app_info.cc",
- "web_applications/scanning_system_web_app_info.h",
- "web_applications/shimless_rma_system_web_app_info.cc",
- "web_applications/shimless_rma_system_web_app_info.h",
- "web_applications/shortcut_customization_system_web_app_info.cc",
- "web_applications/shortcut_customization_system_web_app_info.h",
- "web_applications/system_web_app_install_utils.cc",
- "web_applications/system_web_app_install_utils.h",
- "web_applications/terminal_source.cc",
- "web_applications/terminal_source.h",
- "web_applications/terminal_system_web_app_info.cc",
- "web_applications/terminal_system_web_app_info.h",
- "web_applications/terminal_ui.cc",
- "web_applications/terminal_ui.h",
"wilco_dtc_supportd/fake_wilco_dtc_supportd_client.cc",
"wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h",
"wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc",
@@ -3292,6 +3368,8 @@ source_set("ash") {
"extensions/file_manager/file_system_provider_metrics_util.h",
"extensions/file_manager/fmpi_get_volume_root_function.cc",
"extensions/file_manager/fmpi_get_volume_root_function.h",
+ "extensions/file_manager/image_loader_private_api.cc",
+ "extensions/file_manager/image_loader_private_api.h",
"extensions/file_manager/logged_extension_function.cc",
"extensions/file_manager/logged_extension_function.h",
"extensions/file_manager/private_api_dialog.cc",
@@ -3318,8 +3396,6 @@ source_set("ash") {
"extensions/file_manager/private_api_strings.h",
"extensions/file_manager/private_api_tasks.cc",
"extensions/file_manager/private_api_tasks.h",
- "extensions/file_manager/private_api_thumbnail.cc",
- "extensions/file_manager/private_api_thumbnail.h",
"extensions/file_manager/private_api_util.cc",
"extensions/file_manager/private_api_util.h",
"extensions/file_manager/scoped_suppress_drive_notifications_for_path.cc",
@@ -3367,7 +3443,6 @@ source_set("ash") {
]
public_deps = [
- ":device_configuration_proto",
":lock_unlock_event_proto",
":login_logout_event_proto",
":os_events_proto",
@@ -3409,9 +3484,11 @@ source_set("ash") {
"//ash/webui/projector_app",
"//ash/webui/scanning",
"//ash/webui/scanning/mojom",
+ "//ash/webui/settings/public/constants",
"//ash/webui/shimless_rma",
"//ash/webui/shimless_rma/backend",
"//ash/webui/shortcut_customization_ui",
+ "//ash/webui/shortcut_customization_ui/backend",
"//base",
"//base:i18n",
"//build:chromeos_buildflags",
@@ -3423,11 +3500,12 @@ source_set("ash") {
"//chrome/browser/ash/crosapi:browser_util",
"//chrome/browser/ash/crostini:crostini_installer_types_mojom",
"//chrome/browser/ash/fusebox:fusebox_proto",
- "//chrome/browser/ash/fusebox:fusebox_staging_proto",
"//chrome/browser/ash/guest_os:guest_os_diagnostics_mojom",
+ "//chrome/browser/ash/input_method/mojom:mojom",
"//chrome/browser/ash/login/oobe_quick_start:oobe_quick_start",
"//chrome/browser/ash/login/oobe_quick_start:oobe_quick_start_pref_names",
"//chrome/browser/ash/login/oobe_quick_start/connectivity:connectivity",
+ "//chrome/browser/ash/nearby:quick_start_connectivity_service",
"//chrome/browser/ash/power/ml/smart_dim",
"//chrome/browser/ash/system_web_apps/types",
"//chrome/browser/ash/video_conference",
@@ -3523,6 +3601,7 @@ source_set("ash") {
"//chromeos/ash/components/mojo_service_manager/mojom",
"//chromeos/ash/components/multidevice",
"//chromeos/ash/components/nearby/presence",
+ "//chromeos/ash/components/nearby/presence/conversions",
"//chromeos/ash/components/network",
"//chromeos/ash/components/network/portal_detector",
"//chromeos/ash/components/osauth/impl",
@@ -3532,6 +3611,7 @@ source_set("ash") {
"//chromeos/ash/components/policy",
"//chromeos/ash/components/proximity_auth",
"//chromeos/ash/components/quick_start:quick_start",
+ "//chromeos/ash/components/quick_start:test_support",
"//chromeos/ash/components/scanning",
"//chromeos/ash/components/settings",
"//chromeos/ash/components/smbfs",
@@ -3588,6 +3668,7 @@ source_set("ash") {
"//components/autofill/core/browser",
"//components/captive_portal/core",
"//components/content_settings/core/browser",
+ "//components/cross_device/logging:logging",
"//components/desks_storage",
"//components/download/content/public",
"//components/download/public/background_service:public",
@@ -3642,6 +3723,7 @@ source_set("ash") {
"//components/sync/service",
"//components/sync_preferences",
"//components/user_manager",
+ "//components/variations/service",
"//components/version_info",
"//components/viz/common",
"//components/web_modal",
@@ -3675,8 +3757,9 @@ source_set("ash") {
"//remoting/host/chromeos:enterprise_support",
"//remoting/host/chromeos:host_event_reporter_impl",
"//remoting/host/mojom",
+ "//services/accessibility/android:android_lib",
"//services/accessibility/android/public/mojom",
- "//services/accessibility/public/mojom",
+ "//services/accessibility/public/mojom:mojom",
"//services/data_decoder/public/cpp",
"//services/device/public/mojom",
"//services/device/public/mojom:usb",
@@ -3701,6 +3784,8 @@ source_set("ash") {
"//third_party/ced",
"//third_party/icu",
"//third_party/metrics_proto",
+ "//third_party/nearby:connections_credential_proto",
+ "//third_party/nearby:connections_local_credential_proto",
"//third_party/private_membership",
"//third_party/private_membership:private_membership_proto",
"//third_party/shell-encryption:shell_encryption",
@@ -3749,7 +3834,6 @@ source_set("ash") {
":add_remove_user_event_proto",
":attestation_proto",
":device_policy_remover_generated",
- ":key_permissions_proto",
"//apps",
"//ash/components/arc:arc_features",
"//ash/components/arc:arc_metrics_constants",
@@ -3796,6 +3880,7 @@ source_set("ash") {
"//ash/webui/resources:scanning_app_resources",
"//ash/webui/resources:shimless_rma_resources",
"//ash/webui/resources:shortcut_customization_app_resources",
+ "//ash/webui/settings/public/constants:mojom",
"//ash/webui/system_apps/public:system_web_app_config",
"//ash/webui/system_apps/public:system_web_app_type",
"//build:branding_buildflags",
@@ -3812,6 +3897,7 @@ source_set("ash") {
"//chrome/browser:resources",
"//chrome/browser:theme_properties",
"//chrome/browser/apps:user_type_filter",
+ "//chrome/browser/apps/app_discovery_service",
"//chrome/browser/apps/app_service",
"//chrome/browser/apps/platform_apps",
"//chrome/browser/apps/platform_apps/api",
@@ -3835,22 +3921,23 @@ source_set("ash") {
"//chrome/browser/policy:onc",
"//chrome/browser/profiles",
"//chrome/browser/resources:component_extension_resources",
+ "//chrome/browser/resources/ash/settings:resources",
"//chrome/browser/resources/chromeos:app_icon_resources",
- "//chrome/browser/resources/settings/chromeos:resources",
- "//chrome/browser/screen_ai:screen_ai_chromeos_installer",
+ "//chrome/browser/screen_ai:screen_ai_dlc_installer",
"//chrome/browser/ui/ash/system_web_apps",
"//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings_shared",
"//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings",
- "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
"//chrome/browser/webshare:storage",
"//chrome/common:channel_info",
"//chrome/common:non_code_constants",
"//chrome/common/apps/platform_apps/api",
+ "//chrome/common/chromeos/extensions",
"//chrome/common/net",
"//chrome/services/media_gallery_util/public/mojom",
"//chrome/services/qrcode_generator/public/cpp",
"//chromeos/ash/components/account_manager",
"//chromeos/ash/components/assistant:buildflags",
+ "//chromeos/ash/components/chaps_util",
"//chromeos/ash/components/dbus",
"//chromeos/ash/components/dbus:plugin_vm_service_proto",
"//chromeos/ash/components/dbus:vm_disk_management_proto",
@@ -3891,6 +3978,8 @@ source_set("ash") {
"//chromeos/ash/components/dbus/upstart",
"//chromeos/ash/components/dbus/virtual_file_provider",
"//chromeos/ash/components/device_activity",
+ "//chromeos/ash/components/early_prefs:reader",
+ "//chromeos/ash/components/early_prefs:writer",
"//chromeos/ash/components/fwupd",
"//chromeos/ash/components/local_search_service/public/cpp",
"//chromeos/ash/components/login/hibernate:hibernate_manager",
@@ -3899,12 +3988,15 @@ source_set("ash") {
"//chromeos/ash/components/multidevice/logging",
"//chromeos/ash/components/peripheral_notification",
"//chromeos/ash/components/power",
+ "//chromeos/ash/components/scalable_iph:buildflags",
+ "//chromeos/ash/components/scalable_iph:constants",
"//chromeos/ash/components/scalable_iph:iph_session",
"//chromeos/ash/components/scalable_iph:scalable_iph_delegate",
"//chromeos/ash/components/standalone_browser",
"//chromeos/ash/components/sync_wifi",
"//chromeos/ash/components/tpm",
"//chromeos/ash/components/tpm:buildflags",
+ "//chromeos/ash/resources:resources_grit",
"//chromeos/ash/services/assistant:lib",
"//chromeos/ash/services/assistant/public/cpp",
"//chromeos/ash/services/bluetooth_config:in_process_bluetooth_config",
@@ -3926,6 +4018,7 @@ source_set("ash") {
"//chromeos/components/cdm_factory_daemon/mojom",
"//chromeos/components/certificate_provider",
"//chromeos/components/disks:prefs",
+ "//chromeos/components/kcer:key_permissions_proto",
"//chromeos/components/kiosk",
"//chromeos/components/mojo_bootstrap",
"//chromeos/components/quick_answers/public/cpp:prefs",
@@ -3989,18 +4082,17 @@ source_set("ash") {
"//components/reporting/client:report_queue_factory",
"//components/reporting/proto:interface_proto",
"//components/reporting/proto:status_proto",
+ "//components/reporting/util:rate_limiter_slide_window",
"//components/reporting/util:status",
"//components/safe_browsing/core/common:safe_browsing_prefs",
- "//components/services/app_service/public/cpp:app_file_handling",
- "//components/services/app_service/public/cpp:types",
- "//components/services/app_service/public/protos",
+ "//components/services/app_service",
"//components/services/filesystem/public/mojom",
"//components/services/unzip/content",
"//components/services/unzip/public/mojom",
"//components/signin/public/base",
"//components/site_engagement/content",
"//components/spellcheck/browser",
- "//components/startup_metric_utils/browser",
+ "//components/startup_metric_utils",
"//components/strings:components_strings",
"//components/supervised_user/core/browser",
"//components/supervised_user/core/common",
@@ -4020,6 +4112,7 @@ source_set("ash") {
"//components/vector_icons",
"//components/version_info:channel",
"//components/viz/host",
+ "//components/web_package",
"//components/web_resource",
"//components/webapps/browser",
"//components/webapps/browser:constants",
@@ -4076,6 +4169,7 @@ source_set("ash") {
"//services/tracing/public/mojom",
"//third_party/blink/public/common:headers",
"//third_party/libipp",
+ "//third_party/nearby:connections_local_credential_proto",
"//third_party/protobuf:protobuf_lite",
"//third_party/re2",
"//third_party/securemessage/proto",
@@ -4156,6 +4250,7 @@ source_set("ash") {
]
deps += [
"//chromeos/ash/components/chromebox_for_meetings",
+ "//components/media_device_salt",
"//components/variations/field_trial_config",
"//extensions/buildflags",
]
@@ -4163,8 +4258,8 @@ source_set("ash") {
if (!is_official_build) {
sources += [
- "web_applications/sample_system_web_app_info.cc",
- "web_applications/sample_system_web_app_info.h",
+ "system_web_apps/apps/sample_system_web_app_info.cc",
+ "system_web_apps/apps/sample_system_web_app_info.h",
]
deps += [
"//ash/webui/resources:sample_system_web_app_resources",
@@ -4272,8 +4367,14 @@ static_library("test_support") {
"accessibility/autoclick_test_utils.h",
"accessibility/caret_bounds_changed_waiter.cc",
"accessibility/caret_bounds_changed_waiter.h",
+ "accessibility/dictation_test_utils.cc",
+ "accessibility/dictation_test_utils.h",
+ "accessibility/fullscreen_magnifier_test_helper.cc",
+ "accessibility/fullscreen_magnifier_test_helper.h",
"accessibility/html_test_utils.cc",
"accessibility/html_test_utils.h",
+ "accessibility/magnifier_animation_waiter.cc",
+ "accessibility/magnifier_animation_waiter.h",
"accessibility/select_to_speak_test_utils.cc",
"accessibility/select_to_speak_test_utils.h",
"accessibility/service/fake_accessibility_service.cc",
@@ -4326,12 +4427,10 @@ static_library("test_support") {
"app_mode/fake_kiosk_app_launcher.h",
"app_mode/test_kiosk_extension_builder.cc",
"app_mode/test_kiosk_extension_builder.h",
- "arc/accessibility/arc_accessibility_util.cc",
- "arc/accessibility/arc_accessibility_util.h",
"arc/extensions/fake_arc_support.cc",
"arc/extensions/fake_arc_support.h",
- "arc/tracing/arc_app_performance_tracing_test_helper.cc",
- "arc/tracing/arc_app_performance_tracing_test_helper.h",
+ "arc/tracing/test/arc_app_performance_tracing_test_helper.cc",
+ "arc/tracing/test/arc_app_performance_tracing_test_helper.h",
"attestation/fake_soft_bind_attestation_flow.cc",
"attestation/fake_soft_bind_attestation_flow.h",
"attestation/mock_enrollment_certificate_uploader.cc",
@@ -4348,15 +4447,13 @@ static_library("test_support") {
"borealis/borealis_service_fake.h",
"borealis/borealis_window_manager_mock.cc",
"borealis/borealis_window_manager_mock.h",
- "borealis/borealis_window_manager_test_helper.cc",
- "borealis/borealis_window_manager_test_helper.h",
"borealis/testing/apps.cc",
"borealis/testing/apps.h",
"borealis/testing/callback_factory.h",
"borealis/testing/features.cc",
"borealis/testing/features.h",
- "borealis/testing/widgets.cc",
- "borealis/testing/widgets.h",
+ "borealis/testing/windows.cc",
+ "borealis/testing/windows.h",
"bruschetta/fake_bruschetta_features.cc",
"bruschetta/fake_bruschetta_features.h",
"bruschetta/fake_bruschetta_launcher.cc",
@@ -4401,8 +4498,6 @@ static_library("test_support") {
"drive/drivefs_test_support.h",
"extensions/test_external_cache.cc",
"extensions/test_external_cache.h",
- "file_manager/fake_disk_mount_manager.cc",
- "file_manager/fake_disk_mount_manager.h",
"file_manager/file_manager_test_util.cc",
"file_manager/file_manager_test_util.h",
"file_manager/mount_test_util.cc",
@@ -4444,8 +4539,6 @@ static_library("test_support") {
"login/screens/mock_update_screen.h",
"login/screens/mock_welcome_screen.cc",
"login/screens/mock_welcome_screen.h",
- "login/screens/recommend_apps/fake_recommend_apps_fetcher_delegate.cc",
- "login/screens/recommend_apps/fake_recommend_apps_fetcher_delegate.h",
"login/session/user_session_manager_test_api.cc",
"login/session/user_session_manager_test_api.h",
"login/test/cryptohome_mixin.cc",
@@ -4549,6 +4642,8 @@ static_library("test_support") {
"policy/handlers/minimum_version_policy_test_helpers.h",
"policy/login/signin_profile_extensions_policy_test_base.cc",
"policy/login/signin_profile_extensions_policy_test_base.h",
+ "policy/remote_commands/fake_start_crd_session_job_delegate.cc",
+ "policy/remote_commands/fake_start_crd_session_job_delegate.h",
"policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.cc",
"policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_test_utils.h",
"policy/reporting/metrics_reporting/network/fake_network_diagnostics_util.cc",
@@ -4582,14 +4677,14 @@ static_library("test_support") {
"settings/scoped_cros_settings_test_helper.h",
"settings/scoped_testing_cros_settings.cc",
"settings/scoped_testing_cros_settings.h",
+ "system_web_apps/apps/personalization_app/personalization_app_browsertest_fixture.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_browsertest_fixture.h",
+ "system_web_apps/apps/personalization_app/test_personalization_app_webui_provider.cc",
+ "system_web_apps/apps/personalization_app/test_personalization_app_webui_provider.h",
"wallpaper_handlers/mock_wallpaper_handlers.cc",
"wallpaper_handlers/mock_wallpaper_handlers.h",
"wallpaper_handlers/test_wallpaper_fetcher_delegate.cc",
"wallpaper_handlers/test_wallpaper_fetcher_delegate.h",
- "web_applications/personalization_app/personalization_app_browsertest_fixture.cc",
- "web_applications/personalization_app/personalization_app_browsertest_fixture.h",
- "web_applications/personalization_app/test_personalization_app_webui_provider.cc",
- "web_applications/personalization_app/test_personalization_app_webui_provider.h",
]
allow_circular_includes_from = [
"//chrome/test:test_support",
@@ -4600,6 +4695,7 @@ static_library("test_support") {
"//ash:test_support",
"//ash/components/arc/mojom",
"//ash/public/cpp",
+ "//ash/webui/shortcut_customization_ui/backend/search:mojo_bindings",
"//base",
"//base/test:test_support",
"//build:chromeos_buildflags",
@@ -4661,7 +4757,7 @@ static_library("test_support") {
"//net",
"//net:test_support",
"//services/accessibility/android:test_support",
- "//services/accessibility/public/mojom",
+ "//services/accessibility/public/mojom:mojom",
"//storage/browser",
"//testing/gmock",
"//testing/gtest",
@@ -4700,6 +4796,7 @@ static_library("test_support") {
"//chrome/common:non_code_constants",
"//chromeos/ash/components/attestation:test_support",
"//chromeos/ash/components/browser_context_helper",
+ "//chromeos/ash/components/chaps_util",
"//chromeos/ash/components/cryptohome",
"//chromeos/ash/components/dbus:vm_launch_proto",
"//chromeos/ash/components/dbus/chunneld",
@@ -4766,10 +4863,12 @@ source_set("unit_tests") {
"../ui/webui/ash/login/l10n_util_unittest.cc",
"../ui/webui/ash/login/oobe_display_chooser_unittest.cc",
"../ui/webui/ash/login/signin_userlist_unittest.cc",
+ "../ui/webui/ash/settings/calculator/size_calculator_test_api.h",
+ "../ui/webui/ash/settings/search/search_handler_unittest.cc",
+ "../ui/webui/ash/settings/search/search_tag_registry_unittest.cc",
"../ui/webui/settings/about_handler_unittest.cc",
"../ui/webui/settings/ash/accessibility_handler_unittest.cc",
"../ui/webui/settings/ash/bluetooth_handler_unittest.cc",
- "../ui/webui/settings/ash/calculator/size_calculator_test_api.h",
"../ui/webui/settings/ash/crostini_section_unittest.cc",
"../ui/webui/settings/ash/device_keyboard_handler_unittest.cc",
"../ui/webui/settings/ash/device_name_handler_unittest.cc",
@@ -4784,6 +4883,7 @@ source_set("unit_tests") {
"../ui/webui/settings/ash/multidevice_section_unittest.cc",
"../ui/webui/settings/ash/os_apps_page/app_notification_handler_unittest.cc",
"../ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits_unittest.cc",
+ "../ui/webui/settings/ash/os_settings_features_util_unittest.cc",
"../ui/webui/settings/ash/os_settings_hats_handler_unittest.cc",
"../ui/webui/settings/ash/os_settings_manager_unittest.cc",
"../ui/webui/settings/ash/os_settings_section_unittest.cc",
@@ -4791,14 +4891,11 @@ source_set("unit_tests") {
# TODO(crbug.com/1442928): Move to under `enable_screen_ai_service`
# when PDF OCR becomes available on windows, macOS, and linux.
"../ui/webui/settings/ash/pdf_ocr_handler_unittest.cc",
+ "../ui/webui/settings/ash/per_session_settings_user_action_tracker_unittest.cc",
"../ui/webui/settings/ash/privacy_hub_handler_unittest.cc",
- "../ui/webui/settings/ash/search/per_session_settings_user_action_tracker_unittest.cc",
- "../ui/webui/settings/ash/search/search_handler_unittest.cc",
- "../ui/webui/settings/ash/search/search_tag_registry_unittest.cc",
"../ui/webui/settings/ash/search_engines_handler_unittest.cc",
"../ui/webui/settings/ash/send_search_feedback_handler_unittest.cc",
"../ui/webui/settings/ash/settings_user_action_tracker_unittest.cc",
- "../ui/webui/settings/chromeos/constants/routes_util_unittest.cc",
"accessibility/pumpkin_installer_unittest.cc",
"account_manager/account_apps_availability_unittest.cc",
"account_manager/account_manager_edu_coexistence_controller_unittest.cc",
@@ -4813,6 +4910,7 @@ source_set("unit_tests") {
"app_list/app_list_test_util.h",
"app_list/app_service/app_service_app_model_builder_unittest.cc",
"app_list/app_service/app_service_promise_app_model_builder_unittest.cc",
+ "app_list/app_service/app_service_shortcut_model_builder_unittest.cc",
"app_list/arc/arc_app_metrics_data_unittest.cc",
"app_list/arc/arc_app_metrics_util_unittest.cc",
"app_list/arc/arc_app_sync_metrics_helper_unittest.cc",
@@ -4854,11 +4952,11 @@ source_set("unit_tests") {
"app_list/search/help_app_zero_state_provider_unittest.cc",
"app_list/search/keyboard_shortcut_provider_unittest.cc",
"app_list/search/keyboard_shortcut_result_unittest.cc",
- "app_list/search/local_images/annotation_storage_unittest.cc",
- "app_list/search/local_images/image_annotation_worker_unittest.cc",
- "app_list/search/local_images/local_image_search_test_util.cc",
- "app_list/search/local_images/local_image_search_test_util.h",
- "app_list/search/local_images/sql_database_unittest.cc",
+ "app_list/search/local_image_search/annotation_storage_unittest.cc",
+ "app_list/search/local_image_search/image_annotation_worker_unittest.cc",
+ "app_list/search/local_image_search/local_image_search_test_util.cc",
+ "app_list/search/local_image_search/local_image_search_test_util.h",
+ "app_list/search/local_image_search/sql_database_unittest.cc",
"app_list/search/omnibox/omnibox_answer_result_unittest.cc",
"app_list/search/omnibox/omnibox_lacros_provider_unittest.cc",
"app_list/search/omnibox/omnibox_provider_unittest.cc",
@@ -4909,6 +5007,7 @@ source_set("unit_tests") {
"app_mode/metrics/low_disk_metrics_service_unittest.cc",
"app_mode/metrics/network_connectivity_metrics_service_unittest.cc",
"app_mode/metrics/periodic_metrics_service_unittest.cc",
+ "app_mode/retry_runner_unittest.cc",
"app_mode/startup_app_launcher_unittest.cc",
"app_mode/web_app/web_kiosk_app_launcher_unittest.cc",
"app_mode/web_app/web_kiosk_app_manager_unittest.cc",
@@ -4922,6 +5021,7 @@ source_set("unit_tests") {
"apps/apk_web_app_installer_unittest.cc",
"arc/accessibility/arc_accessibility_helper_bridge_unittest.cc",
"arc/accessibility/arc_accessibility_tree_tracker_unittest.cc",
+ "arc/accessibility/arc_serialization_delegate_unittest.cc",
"arc/adbd/arc_adbd_monitor_bridge_unittest.cc",
"arc/app_shortcuts/arc_app_shortcuts_menu_builder_unittest.cc",
"arc/app_shortcuts/arc_app_shortcuts_request_unittest.cc",
@@ -4970,11 +5070,16 @@ source_set("unit_tests") {
"arc/input_overlay/actions/input_element_unittest.cc",
"arc/input_overlay/actions/position_unittest.cc",
"arc/input_overlay/arc_input_overlay_manager_unittest.cc",
+ "arc/input_overlay/display_overlay_controller_alpha_unittest.cc",
"arc/input_overlay/display_overlay_controller_unittest.cc",
"arc/input_overlay/test/arc_test_window.cc",
"arc/input_overlay/test/arc_test_window.h",
"arc/input_overlay/test/event_capturer.cc",
"arc/input_overlay/test/event_capturer.h",
+ "arc/input_overlay/test/game_controls_test_base.cc",
+ "arc/input_overlay/test/game_controls_test_base.h",
+ "arc/input_overlay/test/overlay_view_test_base.cc",
+ "arc/input_overlay/test/overlay_view_test_base.h",
"arc/input_overlay/test/test_utils.cc",
"arc/input_overlay/test/test_utils.h",
"arc/input_overlay/test/view_test_base.cc",
@@ -5064,7 +5169,6 @@ source_set("unit_tests") {
"borealis/borealis_features_unittest.cc",
"borealis/borealis_install_url_handler_unittest.cc",
"borealis/borealis_installer_unittest.cc",
- "borealis/borealis_launch_watcher_unittest.cc",
"borealis/borealis_power_controller_unittest.cc",
"borealis/borealis_security_delegate_unittest.cc",
"borealis/borealis_shutdown_monitor_unittest.cc",
@@ -5238,6 +5342,7 @@ source_set("unit_tests") {
"file_system_provider/throttled_file_system_unittest.cc",
"fileapi/external_file_url_loader_factory_unittest.cc",
"fileapi/external_file_url_util_unittest.cc",
+ "fileapi/fallback_copy_in_foreign_file_unittest.cc",
"fileapi/file_access_permissions_unittest.cc",
"fileapi/file_change_service_unittest.cc",
"fileapi/file_system_backend_unittest.cc",
@@ -5284,6 +5389,8 @@ source_set("unit_tests") {
"input_method/autocorrect_prefs_unittest.cc",
"input_method/diacritics_checker_unittest.cc",
"input_method/diacritics_insensitive_string_comparator_unittest.cc",
+ "input_method/editor_consent_store_unittest.cc",
+ "input_method/editor_switch_unittest.cc",
"input_method/emoji_suggester_unittest.cc",
"input_method/fake_suggestion_handler.cc",
"input_method/fake_suggestion_handler.h",
@@ -5365,7 +5472,6 @@ source_set("unit_tests") {
"login/screens/arc_vm_data_migration_screen_unittest.cc",
"login/screens/chromevox_hint/chromevox_hint_detector_unittest.cc",
"login/screens/network_screen_unittest.cc",
- "login/screens/recommend_apps/recommend_apps_fetcher_impl_unittest.cc",
"login/screens/reset_screen_unittest.cc",
"login/screens/update_required_screen_unittest.cc",
"login/screens/update_screen_unittest.cc",
@@ -5383,9 +5489,11 @@ source_set("unit_tests") {
"login/users/user_manager_unittest.cc",
"login/version_updater/update_time_estimator_unittest.cc",
"login/version_updater/version_updater_unittest.cc",
+ "login/wizard_controller_unittest.cc",
"mobile/mobile_activator_unittest.cc",
"nearby/bluetooth_adapter_manager_unittest.cc",
"nearby/nearby_process_manager_impl_unittest.cc",
+ "nearby/presence/credential_storage/nearby_presence_credential_storage_unittest.cc",
"net/apn_migrator_unittest.cc",
"net/client_cert_store_ash_unittest.cc",
"net/dns_over_https/templates_uri_resolver_impl_unittest.cc",
@@ -5436,9 +5544,10 @@ source_set("unit_tests") {
"notifications/low_disk_notification_unittest.cc",
"notifications/mock_adb_sideloading_policy_change_notification.cc",
"notifications/mock_adb_sideloading_policy_change_notification.h",
- "notifications/multi_capture_login_notification_unittest.cc",
- "notifications/multi_capture_notification_unittest.cc",
+ "notifications/multi_capture_notifications_unittest.cc",
"notifications/request_system_proxy_credentials_view_unittest.cc",
+ "notifications/update_notification_showing_controller_unittest.cc",
+ "notifications/update_notification_unittest.cc",
"notifications/update_required_notification_unittest.cc",
"os_feedback/os_feedback_screenshot_manager_unittest.cc",
"ownership/owner_key_loader_unittest.cc",
@@ -5473,10 +5582,14 @@ source_set("unit_tests") {
"policy/dlp/dlp_files_controller_ash_unittest.cc",
"policy/dlp/dlp_files_event_storage_unittest.cc",
"policy/dlp/files_policy_notification_manager_unittest.cc",
- "policy/dlp/mock_dlp_files_controller_ash.cc",
- "policy/dlp/mock_dlp_files_controller_ash.h",
- "policy/dlp/mock_files_policy_notification_manager.cc",
- "policy/dlp/mock_files_policy_notification_manager.h",
+ "policy/dlp/test/dlp_files_test_with_mounts.cc",
+ "policy/dlp/test/dlp_files_test_with_mounts.h",
+ "policy/dlp/test/files_policy_notification_manager_test_utils.cc",
+ "policy/dlp/test/files_policy_notification_manager_test_utils.h",
+ "policy/dlp/test/mock_dlp_files_controller_ash.cc",
+ "policy/dlp/test/mock_dlp_files_controller_ash.h",
+ "policy/dlp/test/mock_files_policy_notification_manager.cc",
+ "policy/dlp/test/mock_files_policy_notification_manager.h",
"policy/enrollment/account_status_check_fetcher_unittest.cc",
"policy/enrollment/auto_enrollment_client_impl_unittest.cc",
"policy/enrollment/auto_enrollment_controller_unittest.cc",
@@ -5525,7 +5638,7 @@ source_set("unit_tests") {
"policy/remote_commands/device_command_run_routine_job_unittest.cc",
"policy/remote_commands/device_command_screenshot_job_unittest.cc",
"policy/remote_commands/device_command_set_volume_job_unittest.cc",
- "policy/remote_commands/device_command_start_crd_session_unittest.cc",
+ "policy/remote_commands/device_command_start_crd_session_job_unittest.cc",
"policy/remote_commands/device_command_wipe_users_job_unittest.cc",
"policy/remote_commands/fake_cros_network_config.cc",
"policy/remote_commands/fake_cros_network_config.h",
@@ -5552,6 +5665,7 @@ source_set("unit_tests") {
"policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc",
"policy/reporting/metrics_reporting/cros_reporting_settings_unittest.cc",
"policy/reporting/metrics_reporting/device_activity/device_activity_sampler_unittest.cc",
+ "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_unittest.cc",
"policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc",
"policy/reporting/metrics_reporting/mojo_service_events_observer_base_unittest.cc",
"policy/reporting/metrics_reporting/network/https_latency_event_detector_unittest.cc",
@@ -5613,7 +5727,6 @@ source_set("unit_tests") {
"power/ml/user_activity_ukm_logger_helpers_unittest.cc",
"power/ml/user_activity_ukm_logger_unittest.cc",
"power/power_data_collector_unittest.cc",
- "power/power_metrics_reporter_unittest.cc",
"power/renderer_freezer_unittest.cc",
"power/smart_charging/smart_charging_manager_unittest.cc",
"power/smart_charging/smart_charging_ukm_logger_unittest.cc",
@@ -5651,6 +5764,7 @@ source_set("unit_tests") {
"printing/print_management/printing_manager_factory_unittest.cc",
"printing/print_management/printing_manager_unittest.cc",
"printing/print_servers_manager_unittest.cc",
+ "printing/print_servers_policy_provider_unittest.cc",
"printing/print_servers_provider_unittest.cc",
"printing/printer_authenticator_unittest.cc",
"printing/printer_event_tracker_unittest.cc",
@@ -5718,6 +5832,7 @@ source_set("unit_tests") {
"system/device_disabling_manager_unittest.cc",
"system/procfs_util_unittest.cc",
"system/user_removal_manager_unittest.cc",
+ "system_logs/app_service_log_source_unittest.cc",
"system_logs/connected_input_devices_log_source_unittest.cc",
"system_logs/crosapi_system_log_source_unittest.cc",
"system_logs/debug_daemon_log_source_unittest.cc",
@@ -5728,6 +5843,19 @@ source_set("unit_tests") {
"system_logs/single_debug_daemon_log_source_unittest.cc",
"system_logs/single_log_file_log_source_unittest.cc",
"system_token_cert_db_initializer_unittest.cc",
+ "system_web_apps/apps/face_ml/chrome_face_ml_user_provider_unittest.cc",
+ "system_web_apps/apps/help_app/help_app_discover_tab_notification_unittest.cc",
+ "system_web_apps/apps/help_app/help_app_notification_controller_unittest.cc",
+ "system_web_apps/apps/help_app/help_app_ui_delegate_unittest.cc",
+ "system_web_apps/apps/os_settings_web_app_info_unittest.cc",
+ "system_web_apps/apps/personalization_app/ambient_video_albums_unittest.cc",
+ "system_web_apps/apps/personalization_app/mock_personalization_app_manager.cc",
+ "system_web_apps/apps/personalization_app/mock_personalization_app_manager.h",
+ "system_web_apps/apps/personalization_app/personalization_app_ambient_provider_impl_unittest.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_keyboard_backlight_provider_impl_unittest.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_theme_provider_impl_unittest.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_user_provider_impl_unittest.cc",
+ "system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc",
"tether/tether_service_unittest.cc",
"throttle_observer_unittest.cc",
"throttle_service_unittest.cc",
@@ -5735,18 +5863,6 @@ source_set("unit_tests") {
"usb/cros_usb_detector_unittest.cc",
"video_conference/video_conference_manager_ash_unittest.cc",
"wallpaper_handlers/wallpaper_handlers_unittest.cc",
- "web_applications/face_ml/chrome_face_ml_user_provider_unittest.cc",
- "web_applications/help_app/help_app_discover_tab_notification_unittest.cc",
- "web_applications/help_app/help_app_notification_controller_unittest.cc",
- "web_applications/os_settings_web_app_info_unittest.cc",
- "web_applications/personalization_app/ambient_video_albums_unittest.cc",
- "web_applications/personalization_app/mock_personalization_app_manager.cc",
- "web_applications/personalization_app/mock_personalization_app_manager.h",
- "web_applications/personalization_app/personalization_app_ambient_provider_impl_unittest.cc",
- "web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl_unittest.cc",
- "web_applications/personalization_app/personalization_app_theme_provider_impl_unittest.cc",
- "web_applications/personalization_app/personalization_app_user_provider_impl_unittest.cc",
- "web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc",
"wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.cc",
"wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.h",
"wilco_dtc_supportd/testing_wilco_dtc_supportd_network_context.cc",
@@ -5757,6 +5873,7 @@ source_set("unit_tests") {
"wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc",
"wilco_dtc_supportd/wilco_dtc_supportd_web_request_service_unittest.cc",
]
+
deps = [
":add_remove_user_event_proto",
":arc_test_support",
@@ -5810,6 +5927,7 @@ source_set("unit_tests") {
"//ash/webui/personalization_app/mojom",
"//ash/webui/scanning",
"//ash/webui/scanning/mojom",
+ "//ash/webui/settings/public/constants:mojom",
"//base",
"//base:i18n",
"//base/test:test_config",
@@ -5823,6 +5941,7 @@ source_set("unit_tests") {
"//chrome/browser",
"//chrome/browser:browser_process",
"//chrome/browser/apps:icon_standardizer",
+ "//chrome/browser/apps/app_discovery_service",
"//chrome/browser/apps/app_service:constants",
"//chrome/browser/apps/app_service:test_support",
"//chrome/browser/ash/app_list/search/ranking:proto",
@@ -5861,10 +5980,9 @@ source_set("unit_tests") {
"//chrome/browser/ui:ash_test_support",
"//chrome/browser/ui:test_support",
"//chrome/browser/ui/ash/holding_space:test_support",
+ "//chrome/browser/ui/webui/ash/settings/search/mojom",
"//chrome/browser/ui/webui/settings/ash/input_device_settings:mojom",
"//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom",
- "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings",
- "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
"//chrome/browser/web_applications",
"//chrome/browser/web_applications:web_applications_test_support",
"//chrome/browser/web_applications/mojom:mojom_web_apps_enum",
@@ -5916,6 +6034,7 @@ source_set("unit_tests") {
"//chromeos/ash/components/dbus/kerberos:kerberos_proto",
"//chromeos/ash/components/dbus/lorgnette_manager",
"//chromeos/ash/components/dbus/lorgnette_manager:lorgnette_proto",
+ "//chromeos/ash/components/dbus/oobe_config",
"//chromeos/ash/components/dbus/pciguard",
"//chromeos/ash/components/dbus/resourced",
"//chromeos/ash/components/dbus/seneschal",
@@ -6079,6 +6198,7 @@ source_set("unit_tests") {
"//components/keyed_service/core",
"//components/language/core/browser",
"//components/leveldb_proto",
+ "//components/leveldb_proto:test_support",
"//components/live_caption:constants",
"//components/metrics",
"//components/metrics:serialization",
@@ -6113,12 +6233,12 @@ source_set("unit_tests") {
"//components/reporting/proto:interface_proto",
"//components/reporting/proto:metric_data_proto",
"//components/reporting/proto:record_constants",
+ "//components/reporting/util:rate_limiter_interface",
"//components/reporting/util:status",
"//components/reporting/util:test_callbacks_support",
"//components/safe_browsing/core/common:safe_browsing_prefs",
"//components/search_engines",
"//components/services/app_service",
- "//components/services/app_service/public/cpp:icon_loader_test_support",
"//components/services/app_service/public/cpp:test_support",
"//components/services/app_service/public/protos",
"//components/services/filesystem/public/mojom",
@@ -6194,7 +6314,6 @@ source_set("unit_tests") {
"//remoting/host/chromeos:features",
"//remoting/host/chromeos:host_event_reporter_impl",
"//remoting/host/mojom",
- "//services/accessibility/android:android_lib",
"//services/accessibility/android:tests",
"//services/audio/public/cpp",
"//services/audio/public/cpp:test_support",
@@ -6357,49 +6476,37 @@ source_set("test_with_shadow_variables") {
}
proto_library("add_remove_user_event_proto") {
+ proto_in_dir = "//"
sources =
[ "../policy/messaging_layer/proto/synced/add_remove_user_event.proto" ]
- deps = [
- "//chrome/browser/policy/messaging_layer/proto:session_affiliated_user",
- ]
+ import_dirs = [ "//components/reporting/proto/synced" ]
+ deps = [ "//components/reporting/proto:session_affiliated_user_proto" ]
}
proto_library("attestation_proto") {
sources = [ "attestation/attestation_key_payload.proto" ]
}
-proto_library("device_configuration_proto") {
- sources = [ "login/screens/recommend_apps/device_configuration.proto" ]
- generate_python = false
-}
-
-proto_library("key_permissions_proto") {
- sources = [ "//third_party/cros_system_api/dbus/chaps/key_permissions.proto" ]
- generate_python = false
-
- proto_out_dir = "chrome/browser/ash/platform_keys/key_permissions"
-}
-
proto_library("login_logout_event_proto") {
+ proto_in_dir = "//"
sources =
[ "../policy/messaging_layer/proto/synced/login_logout_event.proto" ]
- deps = [
- "//chrome/browser/policy/messaging_layer/proto:session_affiliated_user",
- ]
+ import_dirs = [ "//components/reporting/proto/synced" ]
+ deps = [ "//components/reporting/proto:session_affiliated_user_proto" ]
}
proto_library("lock_unlock_event_proto") {
+ proto_in_dir = "//"
sources = [ "../policy/messaging_layer/proto/synced/lock_unlock_event.proto" ]
- deps = [
- "//chrome/browser/policy/messaging_layer/proto:session_affiliated_user",
- ]
+ import_dirs = [ "//components/reporting/proto/synced" ]
+ deps = [ "//components/reporting/proto:session_affiliated_user_proto" ]
}
proto_library("os_events_proto") {
+ proto_in_dir = "//"
sources = [ "../policy/messaging_layer/proto/synced/os_events.proto" ]
- deps = [
- "//chrome/browser/policy/messaging_layer/proto:session_affiliated_user",
- ]
+ import_dirs = [ "//components/reporting/proto/synced" ]
+ deps = [ "//components/reporting/proto:session_affiliated_user_proto" ]
}
proto_library("print_job_info_proto") {
@@ -6492,7 +6599,6 @@ fuzzer_test("zeroconf_printer_detector_fuzzer") {
"//base/test:test_support",
"//chrome/browser",
"//components/exo/wayland:ui_controls_protocol_stub",
- "//components/exo/wayland:weston_test_stub",
]
}
@@ -6503,7 +6609,6 @@ fuzzer_test("create_fnmatch_query_fuzzer") {
"//base",
"//chrome/browser",
"//components/exo/wayland:ui_controls_protocol",
- "//components/exo/wayland:weston_test",
]
}
@@ -6514,7 +6619,6 @@ fuzzer_test("search_by_pattern_fuzzer") {
"//base",
"//chrome/browser",
"//components/exo/wayland:ui_controls_protocol",
- "//components/exo/wayland:weston_test",
]
}
@@ -6535,7 +6639,6 @@ if (use_fuzzing_engine_with_lpm) {
"//chrome/common:constants",
"//chromeos/ash/components/install_attributes",
"//components/exo/wayland:ui_controls_protocol_stub",
- "//components/exo/wayland:weston_test_stub",
"//components/policy:generated",
"//components/policy/core/browser",
"//components/policy/core/common",
@@ -6580,7 +6683,6 @@ if (use_fuzzing_engine_with_lpm) {
"//base",
"//chrome/browser",
"//components/exo/wayland:ui_controls_protocol_stub",
- "//components/exo/wayland:weston_test_stub",
]
}
@@ -6592,8 +6694,8 @@ if (use_fuzzing_engine_with_lpm) {
"//base",
"//base:i18n",
"//chrome/browser",
+ "//chrome/browser/ash:test_support",
"//components/exo/wayland:ui_controls_protocol_stub",
- "//components/exo/wayland:weston_test_stub",
"//components/policy/proto",
]
}
diff --git a/chromium/chrome/browser/ash/crosapi/BUILD.gn b/chromium/chrome/browser/ash/crosapi/BUILD.gn
index 241002aac2e..331598cc827 100644
--- a/chromium/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chromium/chrome/browser/ash/crosapi/BUILD.gn
@@ -77,8 +77,6 @@ source_set("crosapi") {
"clipboard_history_ash.h",
"content_protection_ash.cc",
"content_protection_ash.h",
- "copy_migrator.cc",
- "copy_migrator.h",
"crosapi_ash.cc",
"crosapi_ash.h",
"crosapi_dependency_registry.cc",
@@ -98,6 +96,10 @@ source_set("crosapi") {
"device_local_account_extension_service_ash.h",
"device_oauth2_token_service_ash.cc",
"device_oauth2_token_service_ash.h",
+ "device_ownership_waiter.cc",
+ "device_ownership_waiter.h",
+ "device_ownership_waiter_impl.cc",
+ "device_ownership_waiter_impl.h",
"device_settings_ash.cc",
"device_settings_ash.h",
"dlp_ash.cc",
@@ -112,6 +114,8 @@ source_set("crosapi") {
"drive_integration_service_ash.h",
"echo_private_ash.cc",
"echo_private_ash.h",
+ "embedded_accessibility_helper_client_ash.cc",
+ "embedded_accessibility_helper_client_ash.h",
"emoji_picker_ash.cc",
"emoji_picker_ash.h",
"environment_provider.cc",
@@ -124,6 +128,8 @@ source_set("crosapi") {
"field_trial_service_ash.h",
"file_manager_ash.cc",
"file_manager_ash.h",
+ "file_system_access_cloud_identifier_provider_ash.cc",
+ "file_system_access_cloud_identifier_provider_ash.h",
"file_system_provider_service_ash.cc",
"file_system_provider_service_ash.h",
"files_app_launcher.cc",
@@ -192,6 +198,8 @@ source_set("crosapi") {
"networking_private_ash.h",
"parent_access_ash.cc",
"parent_access_ash.h",
+ "payment_app_instance_ash.cc",
+ "payment_app_instance_ash.h",
"persistent_forced_extension_keep_alive.cc",
"persistent_forced_extension_keep_alive.h",
"policy_service_ash.cc",
@@ -246,6 +254,8 @@ source_set("crosapi") {
"wallpaper_ash.h",
"web_app_service_ash.cc",
"web_app_service_ash.h",
+ "web_kiosk_service_ash.cc",
+ "web_kiosk_service_ash.h",
"web_page_info_ash.cc",
"web_page_info_ash.h",
"window_util.cc",
@@ -263,10 +273,12 @@ source_set("crosapi") {
deps = [
"//ash",
+ "//ash/components/arc",
"//ash/components/arc:arc_base_utils",
"//ash/components/arc/mojom",
"//ash/components/arc/session",
"//ash/constants",
+ "//ash/public/cpp/external_arc",
"//ash/webui/camera_app_ui",
"//ash/webui/connectivity_diagnostics",
"//ash/webui/diagnostics_ui",
@@ -274,6 +286,7 @@ source_set("crosapi") {
"//ash/webui/help_app_ui",
"//ash/webui/print_management",
"//ash/webui/scanning",
+ "//ash/webui/settings/public/constants:mojom",
"//ash/webui/system_apps/public:system_web_app_type",
"//base",
"//build/config/chromebox_for_meetings:buildflags",
@@ -284,6 +297,7 @@ source_set("crosapi") {
"//chrome/browser/ash/system_web_apps/types:types",
"//chrome/browser/ash/telemetry_extension/diagnostics",
"//chrome/browser/ash/telemetry_extension/events",
+ "//chrome/browser/ash/telemetry_extension/routines",
"//chrome/browser/ash/telemetry_extension/telemetry",
"//chrome/browser/ash/video_conference",
"//chrome/browser/image_decoder:image_decoder",
@@ -291,7 +305,6 @@ source_set("crosapi") {
"//chrome/browser/profiles:profile",
"//chrome/browser/screen_ai:screen_ai_install_state",
"//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
- "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
"//chrome/browser/web_applications",
"//chrome/common",
"//chrome/common:channel_info",
@@ -313,6 +326,7 @@ source_set("crosapi") {
"//chromeos/ash/components/login/auth",
"//chromeos/ash/components/login/login_state",
"//chromeos/ash/components/network",
+ "//chromeos/ash/components/osauth/public",
"//chromeos/ash/components/settings",
"//chromeos/ash/components/system",
"//chromeos/ash/components/tpm",
@@ -339,6 +353,7 @@ source_set("crosapi") {
"//components/arc",
"//components/crash/core/app",
"//components/exo",
+ "//components/feature_engagement",
"//components/flags_ui",
"//components/keyed_service/content",
"//components/language/core/browser:browser",
@@ -346,6 +361,7 @@ source_set("crosapi") {
"//components/metrics",
"//components/metrics/structured",
"//components/metrics_services_manager:metrics_services_manager",
+ "//components/payments/core:error_strings",
"//components/policy/core/common:common_constants",
"//components/prefs",
"//components/printing/browser",
@@ -394,6 +410,21 @@ source_set("crosapi") {
]
}
+# TODO(b/291266467): Merge this with the `test_support` target.
+source_set("fake_device_ownership_waiter") {
+ testonly = true
+ sources = [
+ "fake_device_ownership_waiter.cc",
+ "fake_device_ownership_waiter.h",
+ ]
+ deps = [
+ ":crosapi",
+ "//base",
+ "//chrome/browser",
+ "//components/user_manager",
+ ]
+}
+
source_set("test_support") {
testonly = true
sources = [
@@ -419,6 +450,7 @@ source_set("test_support") {
":crosapi",
"//ash",
"//ash/app_list",
+ "//ash/components/arc:arc_test_support",
"//base",
"//chrome/browser/apps/app_service",
"//chrome/browser/ash:test_support",
@@ -447,15 +479,16 @@ source_set("unit_tests") {
"cert_provisioning_ash_unittest.cc",
"chrome_app_window_tracker_ash_unittest.cc",
"clipboard_history_ash_unittest.cc",
- "copy_migrator_unittest.cc",
"crosapi_util_unittest.cc",
"desk_ash_unittest.cc",
"device_attributes_ash_unittest.cc",
"device_oauth2_token_service_ash_unittest.cc",
+ "device_ownership_waiter_unittest.cc",
"device_settings_ash_unittest.cc",
"dlp_ash_unittest.cc",
"document_scan_ash_unittest.cc",
"download_controller_ash_unittest.cc",
+ "download_status_updater_ash_unittest.cc",
"drive_integration_service_ash_unittest.cc",
"fake_migration_progress_tracker.h",
"field_trial_service_ash_unittest.cc",
@@ -473,10 +506,12 @@ source_set("unit_tests") {
"network_settings_translation_unittest.cc",
"networking_attributes_ash_unittest.cc",
"parent_access_ash_unittest.cc",
+ "payment_app_instance_ash_unittest.cc",
"persistent_forced_extension_keep_alive_unittest.cc",
"prefs_ash_unittest.cc",
"primary_profile_creation_waiter_unittest.cc",
"rootfs_lacros_loader_unittest.cc",
+ "screen_ai_downloader_ash_unittest.cc",
"stateful_lacros_loader_unittest.cc",
"test_local_printer_ash.cc",
"test_local_printer_ash.h",
@@ -490,9 +525,11 @@ source_set("unit_tests") {
deps = [
":crosapi",
+ ":fake_device_ownership_waiter",
":test_support",
"//ash",
"//ash/components/arc:arc_test_support",
+ "//ash/public/cpp/external_arc:test_support",
"//base",
"//base/test:test_config",
"//base/test:test_support",
diff --git a/chromium/chrome/browser/ash/fusebox/BUILD.gn b/chromium/chrome/browser/ash/fusebox/BUILD.gn
index 1e1ab4db73f..c41ab260f94 100644
--- a/chromium/chrome/browser/ash/fusebox/BUILD.gn
+++ b/chromium/chrome/browser/ash/fusebox/BUILD.gn
@@ -10,7 +10,3 @@ proto_library("fusebox_proto") {
sources = [ "//third_party/cros_system_api/dbus/fusebox/fusebox.proto" ]
proto_out_dir = "chrome/browser/ash/fusebox"
}
-
-proto_library("fusebox_staging_proto") {
- sources = [ "fusebox_staging.proto" ]
-}
diff --git a/chromium/chrome/browser/ash/input_method/mojom/BUILD.gn b/chromium/chrome/browser/ash/input_method/mojom/BUILD.gn
new file mode 100644
index 00000000000..f5a459ab444
--- /dev/null
+++ b/chromium/chrome/browser/ash/input_method/mojom/BUILD.gn
@@ -0,0 +1,13 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(is_chromeos_ash)
+
+mojom("mojom") {
+ sources = [ "editor.mojom" ]
+
+ public_deps = [ "//mojo/public/mojom/base" ]
+}
diff --git a/chromium/chrome/browser/ash/input_method/mojom/editor.mojom b/chromium/chrome/browser/ash/input_method/mojom/editor.mojom
new file mode 100644
index 00000000000..c88d8b3d3d4
--- /dev/null
+++ b/chromium/chrome/browser/ash/input_method/mojom/editor.mojom
@@ -0,0 +1,40 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Next MinVersion: 2
+
+module ash.input_method.mojom;
+
+[Stable, Extensible]
+enum TextQueryCategory {
+ kCustom,
+ [Default] kUnknown,
+};
+
+// Next ordinal: 4
+[Stable]
+struct PresetTextQuery {
+ string text_query_id@0;
+ string name@1;
+ string description@2;
+ TextQueryCategory category@3;
+};
+
+// Provides a minimal interface for UI clients to interact with an Editor
+// instance (one such UI client is the Mako WebUI). Clients can request the
+// current available preset text queries, request rewrites, and insert text
+// with this interface.
+//
+// Next ordinal: 2
+[Stable]
+interface EditorInstance {
+ // Returns the currently available preset text queries. The ids returned
+ // from this method can be used with the `RequestPresetRewrite` method.
+ GetRewritePresetTextQueries@0() => (array<PresetTextQuery> text_queries);
+
+ // Commits the given text into the currently focused text input. The success
+ // parameter returned in the callback specifies if the insertion was
+ // successful or not.
+ CommitEditorResult@1(string text) => (bool success);
+};
diff --git a/chromium/chrome/browser/ash/login/oobe_quick_start/BUILD.gn b/chromium/chrome/browser/ash/login/oobe_quick_start/BUILD.gn
index 46dbf2f3fbb..0ba91e11b5b 100644
--- a/chromium/chrome/browser/ash/login/oobe_quick_start/BUILD.gn
+++ b/chromium/chrome/browser/ash/login/oobe_quick_start/BUILD.gn
@@ -19,6 +19,8 @@ source_set("oobe_quick_start") {
"connectivity",
"//base",
"//chrome/browser:browser_process",
+ "//chrome/browser/ash/nearby:quick_start_connectivity_service",
+ "//chrome/browser/nearby_sharing/public/cpp:cpp",
"//chrome/common:channel_info",
"//chromeos/ash/components/attestation:attestation",
"//chromeos/ash/components/dbus/attestation:attestation_proto",
@@ -29,7 +31,6 @@ source_set("oobe_quick_start") {
"//components/account_id:account_id",
"//components/endpoint_fetcher:endpoint_fetcher",
"//components/prefs:prefs",
- "//components/qr_code_generator:qr_code_generator",
"//components/version_info:channel",
"//google_apis:google_apis",
"//google_apis/common:common",
@@ -56,6 +57,7 @@ source_set("unit_tests") {
"//chrome/test:test_support",
"//chromeos/ash/components/attestation:test_support",
"//chromeos/ash/components/dbus/constants:constants",
+ "//chromeos/ash/components/quick_start:test_support",
"//components/account_id:account_id",
"//google_apis:google_apis",
"//google_apis/common:common",
diff --git a/chromium/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn b/chromium/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn
index a55142fb1e5..2503c931473 100644
--- a/chromium/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn
+++ b/chromium/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn
@@ -12,19 +12,22 @@ source_set("connectivity") {
"//base",
"//chrome/browser:browser_process",
"//chrome/browser/ash/login/oobe_quick_start:oobe_quick_start_pref_names",
- "//chrome/browser/nearby_sharing/public/cpp",
+ "//chrome/browser/ash/nearby:quick_start_connectivity_service",
"//chrome/browser/nearby_sharing/public/cpp",
"//chromeos/ash/components/quick_start",
"//chromeos/ash/services/nearby/public/cpp",
"//chromeos/ash/services/nearby/public/mojom",
"//components/cbor",
"//components/prefs",
+ "//components/qr_code_generator:qr_code_generator",
"//crypto",
"//device/bluetooth",
"//ui/chromeos",
"//url",
]
sources = [
+ "account_transfer_client_data.cc",
+ "account_transfer_client_data.h",
"connection.cc",
"connection.h",
"fast_pair_advertiser.cc",
@@ -33,8 +36,12 @@ source_set("connectivity") {
"fido_assertion_info.h",
"handshake_helpers.cc",
"handshake_helpers.h",
+ "qr_code.cc",
+ "qr_code.h",
"random_session_id.cc",
"random_session_id.h",
+ "session_context.cc",
+ "session_context.h",
"target_device_connection_broker.cc",
"target_device_connection_broker.h",
"target_device_connection_broker_factory.cc",
@@ -49,6 +56,7 @@ source_set("test_support") {
public_deps = [ ":connectivity" ]
deps = [
"//base",
+ "//chrome/browser/ash/nearby:quick_start_connectivity_service",
"//chrome/browser/nearby_sharing/public/cpp",
"//chrome/test:test_support",
"//chromeos/ash/components/quick_start:test_support",
@@ -84,9 +92,12 @@ source_set("unit_tests") {
"//url",
]
sources = [
+ "account_transfer_client_data_unittest.cc",
"connection_unittest.cc",
"fast_pair_advertiser_unittest.cc",
"handshake_helpers_unittest.cc",
+ "qr_code_unittest.cc",
+ "session_context_unittest.cc",
"target_device_connection_broker_impl_unittest.cc",
]
}
diff --git a/chromium/chrome/browser/ash/nearby/BUILD.gn b/chromium/chrome/browser/ash/nearby/BUILD.gn
index 02d2a07a484..f6a1c1a2cf3 100644
--- a/chromium/chrome/browser/ash/nearby/BUILD.gn
+++ b/chromium/chrome/browser/ash/nearby/BUILD.gn
@@ -24,3 +24,14 @@ source_set("bluetooth_adapter_manager") {
"//mojo/public/cpp/bindings",
]
}
+
+source_set("quick_start_connectivity_service") {
+ sources = [ "quick_start_connectivity_service.h" ]
+
+ deps = [
+ "//base",
+ "//chromeos/ash/services/nearby/public/mojom",
+ "//components/keyed_service/core",
+ "//mojo/public/cpp/bindings",
+ ]
+}
diff --git a/chromium/chrome/browser/ash/system_extensions/api/managed_device_health_services/BUILD.gn b/chromium/chrome/browser/ash/system_extensions/api/managed_device_health_services/BUILD.gn
index 24d75a15d1f..f99d9e32178 100644
--- a/chromium/chrome/browser/ash/system_extensions/api/managed_device_health_services/BUILD.gn
+++ b/chromium/chrome/browser/ash/system_extensions/api/managed_device_health_services/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2022 The Chromium Authors. All rights reserved.
+# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/chromium/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn b/chromium/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn
index 02a09f8e67c..dc2fee0a07e 100644
--- a/chromium/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn
+++ b/chromium/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn
@@ -51,6 +51,7 @@ source_set("browser_tests") {
":test_helper_js_data_deps",
":window_management",
"//chrome/browser/apps/app_service",
+ "//chrome/browser/apps/app_service:app_registry_cache_waiter",
"//chrome/browser/ash/system_extensions",
"//chrome/browser/ash/system_extensions/api/test_support",
"//chrome/browser/ash/system_web_apps/test_support",
diff --git a/chromium/chrome/browser/ash/system_web_apps/BUILD.gn b/chromium/chrome/browser/ash/system_web_apps/BUILD.gn
index 2e89f791b64..9f4c50218e2 100644
--- a/chromium/chrome/browser/ash/system_web_apps/BUILD.gn
+++ b/chromium/chrome/browser/ash/system_web_apps/BUILD.gn
@@ -95,6 +95,7 @@ source_set("browser_tests") {
"//chrome/app:command_ids",
"//chrome/browser/apps/app_service",
"//chrome/browser/ash:test_support",
+ "//chrome/browser/ash/system_web_apps/apps:browser_tests",
"//chrome/browser/ash/system_web_apps/test_support",
"//chrome/browser/ash/system_web_apps/test_support:test_support_ui",
"//chrome/browser/ash/system_web_apps/types",
diff --git a/chromium/chrome/browser/ash/system_web_apps/apps/BUILD.gn b/chromium/chrome/browser/ash/system_web_apps/apps/BUILD.gn
new file mode 100644
index 00000000000..83041563ca8
--- /dev/null
+++ b/chromium/chrome/browser/ash/system_web_apps/apps/BUILD.gn
@@ -0,0 +1,63 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash)
+
+source_set("browser_tests") {
+ testonly = true
+
+ defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+ sources = [
+ "camera_app/camera_app_integration_browsertest.cc",
+ "demo_mode_app_integration_browsertest.cc",
+ "diagnostics_app_integration_browsertest.cc",
+ "eche_app_integration_browsertest.cc",
+ "face_ml_integration_browsertest.cc",
+ "firmware_update_app_integration_browsertest.cc",
+ "help_app/help_app_integration_browsertest.cc",
+ "media_app/media_app_integration_browsertest.cc",
+ "os_feedback_app_integration_browsertest.cc",
+ "personalization_app/personalization_app_integration_browsertest.cc",
+ "personalization_app/personalization_app_time_of_day_browsertest.cc",
+ "print_management_app_integration_browsertest.cc",
+ "projector_app/projector_app_integration_browsertest.cc",
+ "scanning_app_integration_browsertest.cc",
+ "settings_app_integration_browsertest.cc",
+ "shimless_rma_integration_browsertest.cc",
+ "shortcut_customization_app_integration_browsertest.cc",
+ ]
+
+ if (!is_official_build) {
+ sources += [ "sample_system_web_app_integration_browsertest.cc" ]
+ }
+
+ deps = [
+ "//ash/webui/demo_mode_app_ui",
+ "//ash/webui/diagnostics_ui",
+ "//ash/webui/firmware_update_ui",
+ "//ash/webui/help_app_ui:buildflags",
+ "//ash/webui/help_app_ui/search:mojo_bindings",
+ "//ash/webui/media_app_ui:browser_test_support",
+ "//ash/webui/media_app_ui:buildflags",
+ "//ash/webui/os_feedback_ui",
+ "//ash/webui/print_management",
+ "//ash/webui/projector_app:buildflags",
+ "//ash/webui/web_applications/test:test_support",
+ "//chrome/browser/apps/app_service",
+ "//chrome/browser/ash:test_support",
+ "//chrome/browser/ash/system_web_apps/test_support:test_support_ui",
+ "//chrome/browser/error_reporting:test_support",
+ "//chrome/browser/ui/ash/system_web_apps",
+ "//components/crash/content/browser/error_reporting:mock_crash_endpoint",
+ "//components/language/core/browser",
+ "//ui/base/idle:test_support",
+ ]
+
+ if (!is_official_build) {
+ deps += [ "//ash/webui/sample_system_web_app_ui" ]
+ }
+}
diff --git a/chromium/chrome/browser/ash/system_web_apps/test_support/BUILD.gn b/chromium/chrome/browser/ash/system_web_apps/test_support/BUILD.gn
index 0f732b54ab7..6d8866dba4f 100644
--- a/chromium/chrome/browser/ash/system_web_apps/test_support/BUILD.gn
+++ b/chromium/chrome/browser/ash/system_web_apps/test_support/BUILD.gn
@@ -73,7 +73,7 @@ source_set("test_support_ui") {
"//chrome/browser/web_applications",
"//chrome/browser/web_applications:web_applications_test_support",
"//chrome/test:test_support_ui",
- "//components/services/app_service/public/cpp:types",
+ "//components/services/app_service",
"//content/test:test_support",
"//testing/gtest",
"//url",
diff --git a/chromium/chrome/browser/ash/telemetry_extension/BUILD.gn b/chromium/chrome/browser/ash/telemetry_extension/BUILD.gn
index 79b68164249..8251f428350 100644
--- a/chromium/chrome/browser/ash/telemetry_extension/BUILD.gn
+++ b/chromium/chrome/browser/ash/telemetry_extension/BUILD.gn
@@ -10,8 +10,10 @@ source_set("unit_tests") {
testonly = true
deps = [
+ "common:unit_tests",
"diagnostics:unit_tests",
"events:unit_tests",
+ "routines:unit_tests",
"telemetry:unit_tests",
]
}
diff --git a/chromium/chrome/browser/ash/telemetry_extension/common/BUILD.gn b/chromium/chrome/browser/ash/telemetry_extension/common/BUILD.gn
new file mode 100644
index 00000000000..9f0b54fe356
--- /dev/null
+++ b/chromium/chrome/browser/ash/telemetry_extension/common/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash, "Telemetry Services are ash-chrome only")
+
+source_set("common") {
+ sources = [
+ "telemetry_extension_converters.cc",
+ "telemetry_extension_converters.h",
+ ]
+
+ deps = [
+ "//base",
+ "//chromeos/ash/services/cros_healthd/public/mojom",
+ "//chromeos/crosapi/mojom",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "telemetry_extension_converters_unittest.cc" ]
+
+ deps = [
+ ":common",
+ "//chromeos/ash/services/cros_healthd/public/mojom",
+ "//chromeos/crosapi/mojom",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/chrome/browser/ash/telemetry_extension/events/BUILD.gn b/chromium/chrome/browser/ash/telemetry_extension/events/BUILD.gn
index d334b3ca1d9..3da9682e0c6 100644
--- a/chromium/chrome/browser/ash/telemetry_extension/events/BUILD.gn
+++ b/chromium/chrome/browser/ash/telemetry_extension/events/BUILD.gn
@@ -19,6 +19,8 @@ source_set("events") {
deps = [
"//ash/system/diagnostics/mojom",
"//base",
+ "//chrome/browser/ash/telemetry_extension/common",
+ "//chrome/browser/ash/telemetry_extension/telemetry:telemetry_converters",
"//chromeos/ash/services/cros_healthd/public/cpp",
"//chromeos/ash/services/cros_healthd/public/mojom",
"//chromeos/crosapi/mojom",
@@ -37,6 +39,7 @@ source_set("unit_tests") {
"//ash/system/diagnostics/mojom",
"//base",
"//base/test:test_support",
+ "//chrome/browser/ash/telemetry_extension/telemetry:telemetry_converters",
"//chromeos/ash/components/mojo_service_manager",
"//chromeos/ash/services/cros_healthd/public/cpp",
"//chromeos/ash/services/cros_healthd/public/mojom",
diff --git a/chromium/chrome/browser/ash/telemetry_extension/routines/BUILD.gn b/chromium/chrome/browser/ash/telemetry_extension/routines/BUILD.gn
new file mode 100644
index 00000000000..54cf85bd603
--- /dev/null
+++ b/chromium/chrome/browser/ash/telemetry_extension/routines/BUILD.gn
@@ -0,0 +1,48 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash, "Telemetry Services are ash-chrome only")
+
+source_set("routines") {
+ sources = [
+ "routine_control.cc",
+ "routine_control.h",
+ "routine_converters.cc",
+ "routine_converters.h",
+ "routine_events_forwarder.cc",
+ "routine_events_forwarder.h",
+ "self_owned_mojo_proxy.h",
+ "telemetry_diagnostic_routine_service_ash.cc",
+ "telemetry_diagnostic_routine_service_ash.h",
+ ]
+
+ deps = [
+ "//base",
+ "//chrome/browser/ash/telemetry_extension/common",
+ "//chromeos/ash/services/cros_healthd/public/cpp",
+ "//chromeos/ash/services/cros_healthd/public/mojom",
+ "//chromeos/crosapi/mojom",
+ "//mojo/public/cpp/bindings",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "routine_converters_unittest.cc",
+ "telemetry_diagnostic_routine_service_ash_unittest.cc",
+ ]
+
+ deps = [
+ ":routines",
+ "//base",
+ "//base/test:test_support",
+ "//chromeos/ash/components/mojo_service_manager:mojo_service_manager",
+ "//chromeos/ash/services/cros_healthd/public/cpp",
+ "//chromeos/ash/services/cros_healthd/public/mojom",
+ "//chromeos/crosapi/mojom",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/chrome/browser/ash/telemetry_extension/telemetry/BUILD.gn b/chromium/chrome/browser/ash/telemetry_extension/telemetry/BUILD.gn
index afe05a940bf..08487961e21 100644
--- a/chromium/chrome/browser/ash/telemetry_extension/telemetry/BUILD.gn
+++ b/chromium/chrome/browser/ash/telemetry_extension/telemetry/BUILD.gn
@@ -10,13 +10,12 @@ source_set("telemetry") {
sources = [
"probe_service_ash.cc",
"probe_service_ash.h",
- "probe_service_converters.cc",
- "probe_service_converters.h",
]
public_configs = [ "//build/config/linux/dbus" ]
deps = [
+ ":telemetry_converters",
"//base",
"//chromeos/ash/components/dbus/debug_daemon",
"//chromeos/ash/services/cros_healthd/public/cpp",
@@ -28,6 +27,23 @@ source_set("telemetry") {
]
}
+source_set("telemetry_converters") {
+ sources = [
+ "probe_service_converters.cc",
+ "probe_service_converters.h",
+ ]
+
+ deps = [
+ "//base",
+ "//chromeos/ash/services/cros_healthd/public/cpp",
+ "//chromeos/ash/services/cros_healthd/public/mojom",
+ "//chromeos/crosapi/mojom",
+ "//chromeos/services/network_health/public/mojom:types",
+ "//mojo/public/cpp/bindings",
+ "//third_party/abseil-cpp:absl",
+ ]
+}
+
source_set("unit_tests") {
testonly = true
@@ -40,6 +56,7 @@ source_set("unit_tests") {
deps = [
":telemetry",
+ ":telemetry_converters",
"//base",
"//base/test:test_support",
"//chromeos/ash/components/dbus/debug_daemon",
diff --git a/chromium/chrome/browser/autofill/BUILD.gn b/chromium/chrome/browser/autofill/BUILD.gn
index 2d1a61601ff..26b6787aba2 100644
--- a/chromium/chrome/browser/autofill/BUILD.gn
+++ b/chromium/chrome/browser/autofill/BUILD.gn
@@ -5,6 +5,7 @@
import("//build/buildflag_header.gni")
import("//build/config/chromeos/ui_mode.gni")
import("//chrome/version.gni")
+import("//components/optimization_guide/features.gni")
import("//extensions/buildflags/buildflags.gni")
static_library("autofill") {
@@ -17,26 +18,33 @@ static_library("autofill") {
"autofill_gstatic_reader.h",
"autofill_image_fetcher_factory.cc",
"autofill_image_fetcher_factory.h",
- "autofill_image_fetcher_impl.cc",
- "autofill_image_fetcher_impl.h",
"autofill_offer_manager_factory.cc",
"autofill_offer_manager_factory.h",
"autofill_optimization_guide_factory.cc",
"autofill_optimization_guide_factory.h",
- "autofill_popup_controller_utils.cc",
- "autofill_popup_controller_utils.h",
"iban_manager_factory.cc",
"iban_manager_factory.h",
"merchant_promo_code_manager_factory.cc",
"merchant_promo_code_manager_factory.h",
"personal_data_manager_factory.cc",
"personal_data_manager_factory.h",
+ "shopping_service_delegate_impl.cc",
+ "shopping_service_delegate_impl.h",
"strike_database_factory.cc",
"strike_database_factory.h",
+ "subkey_requester_factory.cc",
+ "subkey_requester_factory.h",
"validation_rules_storage_factory.cc",
"validation_rules_storage_factory.h",
]
+ if (build_with_tflite_lib) {
+ sources += [
+ "autofill_ml_prediction_model_service_factory.cc",
+ "autofill_ml_prediction_model_service_factory.h",
+ ]
+ }
+
public_deps = [
"//base",
"//chrome/browser/profiles:profile",
@@ -55,6 +63,7 @@ static_library("autofill") {
"//chrome/browser/profiles",
"//chrome/common:constants",
"//components/autofill/content/browser",
+ "//components/commerce/core:shopping_service",
"//components/image_fetcher/core",
"//components/prefs",
"//components/strings:components_strings_grit",
@@ -68,6 +77,8 @@ static_library("autofill") {
"address_accessory_controller.h",
"address_accessory_controller_impl.cc",
"address_accessory_controller_impl.h",
+ "android/autofill_image_fetcher_impl.cc",
+ "android/autofill_image_fetcher_impl.h",
"android/personal_data_manager_android.cc",
"android/personal_data_manager_android.h",
"android/phone_number_util_android.cc",
@@ -100,6 +111,10 @@ static_library("autofill") {
]
} else {
# !is_android
+ sources += [
+ "ui/autofill_image_fetcher_impl.cc",
+ "ui/autofill_image_fetcher_impl.h",
+ ]
deps += [ "//components/sync/base" ]
}
}
@@ -188,6 +203,8 @@ if (!is_android) {
testonly = true
sources = [
+ "autofill_flow_test_util.cc",
+ "autofill_flow_test_util.h",
"autofill_uitest.cc",
"autofill_uitest.h",
]
diff --git a/chromium/chrome/browser/autofill/android/BUILD.gn b/chromium/chrome/browser/autofill/android/BUILD.gn
index 56bfa9a6b1f..b18165dc04b 100644
--- a/chromium/chrome/browser/autofill/android/BUILD.gn
+++ b/chromium/chrome/browser/autofill/android/BUILD.gn
@@ -7,20 +7,20 @@ import("//build/config/android/rules.gni")
java_cpp_enum("autofill_generated_enums") {
sources = [
"//chrome/browser/android/preferences/autofill/autofill_profile_bridge.h",
- "//components/autofill/core/browser/data_model/autofill_profile.h",
- "//components/autofill/core/browser/data_model/autofill_structured_address_component.h",
]
}
android_library("java") {
sources = [
+ "java/src/org/chromium/chrome/browser/autofill/AddressNormalizerFactory.java",
"java/src/org/chromium/chrome/browser/autofill/AutofillAddress.java",
"java/src/org/chromium/chrome/browser/autofill/AutofillEditorBase.java",
+ "java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcher.java",
"java/src/org/chromium/chrome/browser/autofill/AutofillProfileBridge.java",
"java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java",
- "java/src/org/chromium/chrome/browser/autofill/LegalMessageLine.java",
"java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java",
"java/src/org/chromium/chrome/browser/autofill/PhoneNumberUtil.java",
+ "java/src/org/chromium/chrome/browser/autofill/SubKeyRequesterFactory.java",
"java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java",
"java/src/org/chromium/chrome/browser/autofill/editors/DropdownFieldAdapter.java",
@@ -29,11 +29,18 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogToolbar.java",
"java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java",
"java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogViewBinder.java",
+ "java/src/org/chromium/chrome/browser/autofill/editors/EditorFieldValidator.java",
"java/src/org/chromium/chrome/browser/autofill/editors/EditorObserverForTest.java",
"java/src/org/chromium/chrome/browser/autofill/editors/EditorProperties.java",
"java/src/org/chromium/chrome/browser/autofill/editors/FieldView.java",
"java/src/org/chromium/chrome/browser/autofill/editors/HintedDropDownAdapter.java",
"java/src/org/chromium/chrome/browser/autofill/editors/TextFieldView.java",
+ "java/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsCoordinator.java",
+ "java/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsFragment.java",
+ "java/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsMediator.java",
+ "java/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsProperties.java",
+ "java/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsViewBinder.java",
+ "java/src/org/chromium/chrome/browser/autofill/options/RadioButtonGroupThirdPartyPreference.java",
]
resources_package = "org.chromium.chrome.browser.autofill"
@@ -51,6 +58,7 @@ android_library("java") {
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
"//chrome/browser/profiles/android:java",
+ "//chrome/browser/settings:java",
"//chrome/browser/signin/services/android:java",
"//chrome/browser/sync/android:java",
"//chrome/browser/util:java",
@@ -64,6 +72,7 @@ android_library("java") {
"//components/browser_ui/util/android:java",
"//components/browser_ui/widget/android:java",
"//components/browser_ui/widget/android:java_resources",
+ "//components/embedder_support/android:simple_factory_key_java",
"//components/image_fetcher:java",
"//components/payments/mojom:mojom_java",
"//components/prefs/android:java",
@@ -80,11 +89,18 @@ android_library("java") {
"//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_fragment_fragment_java",
+ "//third_party/androidx:androidx_interpolator_interpolator_java",
+ "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
+ "//third_party/androidx:androidx_lifecycle_lifecycle_livedata_core_java",
+ "//third_party/androidx:androidx_preference_preference_java",
+ "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
"//ui/android:ui_no_recycler_view_java",
"//ui/android:ui_utils_java",
"//url:gurl_java",
]
+ public_deps = [ "//components/autofill/android:autofill_java" ]
+
srcjar_deps = [
":autofill_generated_enums",
":jni_headers",
@@ -104,9 +120,12 @@ android_library("bottom_sheet_utils_java") {
generate_jni("jni_headers") {
sources = [
+ "java/src/org/chromium/chrome/browser/autofill/AddressNormalizerFactory.java",
+ "java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcher.java",
"java/src/org/chromium/chrome/browser/autofill/AutofillProfileBridge.java",
"java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java",
"java/src/org/chromium/chrome/browser/autofill/PhoneNumberUtil.java",
+ "java/src/org/chromium/chrome/browser/autofill/SubKeyRequesterFactory.java",
]
}
@@ -122,8 +141,10 @@ android_resources("java_resources") {
"java/res/layout/payment_request_editor.xml",
"java/res/layout/payment_request_editor_dropdown.xml",
"java/res/layout/payments_request_editor_textview.xml",
+ "java/res/layout/radio_button_group_third_party_preference.xml",
"java/res/menu/prefeditor_editor_menu.xml",
"java/res/values/dimens.xml",
+ "java/res/xml/autofill_options_preferences.xml",
]
}
@@ -131,15 +152,20 @@ android_library("test_java") {
testonly = true
resources_package = "org.chromium.chrome.browser.autofill"
- sources = [ "javatest/src/org/chromium/chrome/browser/autofill/editors/AddressEditorRenderTest.java" ]
+ sources = [
+ "javatest/src/org/chromium/chrome/browser/autofill/editors/AddressEditorRenderTest.java",
+ "javatest/src/org/chromium/chrome/browser/autofill/editors/DropdownFieldViewTest.java",
+ ]
deps = [
":java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
"//chrome/android:chrome_test_java",
"//chrome/android:chrome_test_util_java",
+ "//chrome/browser/feedback/android:java",
"//chrome/browser/flags:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/signin/services/android:java",
@@ -147,9 +173,11 @@ android_library("test_java") {
"//chrome/browser/ui/android/night_mode:night_mode_java_test_support",
"//chrome/browser/util:java",
"//chrome/test/android:chrome_java_integration_test_support",
+ "//components/autofill/android:main_autofill_java",
"//components/signin/public/android:java",
"//components/sync/android:sync_java",
"//content/public/test/android:content_java_test_support",
+ "//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/hamcrest:hamcrest_java",
"//third_party/junit",
@@ -163,7 +191,12 @@ android_library("test_java") {
robolectric_library("junit") {
resources_package = "org.chromium.chrome.browser.autofill"
- sources = [ "junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java" ]
+ sources = [
+ "junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java",
+ "junit/src/org/chromium/chrome/browser/autofill/editors/DropdownFieldViewUnitTest.java",
+ "junit/src/org/chromium/chrome/browser/autofill/editors/TextFieldViewUnitTest.java",
+ "junit/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsTest.java",
+ ]
deps = [
":java",
@@ -171,17 +204,30 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/feedback/android:java",
"//chrome/browser/flags:java",
+ "//chrome/browser/preferences:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/signin/services/android:java",
"//chrome/browser/sync/android:java",
"//chrome/test/android:chrome_java_unit_test_support",
+ "//components/autofill/android:main_autofill_java",
+ "//components/browser_ui/settings/android:java",
+ "//components/browser_ui/widget/android:java",
+ "//components/prefs/android:java",
"//components/signin/public/android:java",
"//components/sync/android:sync_java",
+ "//components/user_prefs/android:java",
+ "//content/public/android:content_full_java",
+ "//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_core_core_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
+ "//third_party/androidx:androidx_fragment_fragment_testing_java",
+ "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
+ "//third_party/androidx:androidx_lifecycle_lifecycle_runtime_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_ext_junit_java",
diff --git a/chromium/chrome/browser/autofill/test/BUILD.gn b/chromium/chrome/browser/autofill/test/BUILD.gn
index 969cbd69ac3..79262a1a6d6 100644
--- a/chromium/chrome/browser/autofill/test/BUILD.gn
+++ b/chromium/chrome/browser/autofill/test/BUILD.gn
@@ -8,7 +8,10 @@ android_library("test_java") {
testonly = true
resources_package = "org.chromium.chrome.browser.autofill"
- sources = [ "android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java" ]
+ sources = [
+ "android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java",
+ "android/java/src/org/chromium/chrome/browser/autofill/SubKeyRequesterIntegrationTest.java",
+ ]
deps = [
":test_support_java",
@@ -18,6 +21,7 @@ android_library("test_java") {
"//chrome/test/android:chrome_java_integration_test_support",
"//components/autofill/android:autofill_java_resources",
"//components/autofill/android:autofill_payments_java_resources",
+ "//components/image_fetcher:java",
"//components/image_fetcher:test_support_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_test_runner_java",
@@ -37,6 +41,7 @@ android_library("test_support_java") {
deps = [
":jni_headers",
"//base:jni_java",
+ "//content/public/android:content_java",
"//url:gurl_java",
]
diff --git a/chromium/chrome/browser/back_press/android/BUILD.gn b/chromium/chrome/browser/back_press/android/BUILD.gn
index b799e562898..388d6debcd4 100644
--- a/chromium/chrome/browser/back_press/android/BUILD.gn
+++ b/chromium/chrome/browser/back_press/android/BUILD.gn
@@ -8,6 +8,7 @@ android_library("java") {
sources = [
"java/src/org/chromium/chrome/browser/back_press/BackPressHelper.java",
"java/src/org/chromium/chrome/browser/back_press/BackPressManager.java",
+ "java/src/org/chromium/chrome/browser/back_press/CloseListenerManager.java",
"java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandler.java",
"java/src/org/chromium/chrome/browser/back_press/SecondaryActivityBackPressUma.java",
]
@@ -42,6 +43,7 @@ robolectric_library("junit") {
"//chrome/browser/flags:java",
"//components/browser_ui/widget/android:java",
"//third_party/androidx:androidx_activity_activity_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
"//third_party/androidx:androidx_lifecycle_lifecycle_runtime_java",
"//third_party/junit:junit",
@@ -62,6 +64,7 @@ android_library("unit_device_javatests") {
":java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:base_java_test_support_uncommon",
"//chrome/browser/flags:java",
"//chrome/browser/tab:java",
"//chrome/browser/tabmodel:java",
diff --git a/chromium/chrome/browser/banners/android/BUILD.gn b/chromium/chrome/browser/banners/android/BUILD.gn
index fdabaca5c76..198d8024d68 100644
--- a/chromium/chrome/browser/banners/android/BUILD.gn
+++ b/chromium/chrome/browser/banners/android/BUILD.gn
@@ -63,11 +63,13 @@ android_library("javatests") {
"//chrome/browser/profiles/android:java",
"//chrome/browser/tab:java",
"//chrome/browser/tabmodel:java",
+ "//chrome/browser/ui/android/appmenu:java",
"//chrome/browser/ui/android/appmenu/test:test_support_java",
"//chrome/browser/ui/android/strings:ui_strings_grd",
"//chrome/browser/webapps/android:java",
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/feature_engagement/public:public_java",
"//components/infobars/android:java",
"//components/infobars/android:java_resources",
diff --git a/chromium/chrome/browser/bluetooth/android/BUILD.gn b/chromium/chrome/browser/bluetooth/android/BUILD.gn
index 18d6136bc8a..4d9be050d3a 100644
--- a/chromium/chrome/browser/bluetooth/android/BUILD.gn
+++ b/chromium/chrome/browser/bluetooth/android/BUILD.gn
@@ -50,13 +50,14 @@ robolectric_library("junit") {
":java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/test/android:chrome_java_unit_test_support",
+ "//components/browser_ui/notifications/android:java",
"//components/browser_ui/notifications/android:test_support_java",
"//components/url_formatter/android:url_formatter_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//url:gurl_java",
- "//url:gurl_junit_shadows",
"//url:gurl_junit_test_support",
]
}
diff --git a/chromium/chrome/browser/breadcrumbs/BUILD.gn b/chromium/chrome/browser/breadcrumbs/BUILD.gn
index 851c275e0f8..820cfe0e8cc 100644
--- a/chromium/chrome/browser/breadcrumbs/BUILD.gn
+++ b/chromium/chrome/browser/breadcrumbs/BUILD.gn
@@ -54,6 +54,7 @@ if (!is_android) {
":breadcrumbs",
"//chrome/test:test_support",
"//components/breadcrumbs/core",
+ "//components/breadcrumbs/core:status",
]
sources = [ "breadcrumb_manager_tab_helper_desktop_browsertest.cc" ]
diff --git a/chromium/chrome/browser/browser_resources.grd b/chromium/chrome/browser/browser_resources.grd
index 3b93dd29ebc..6eb0244f1dc 100644
--- a/chromium/chrome/browser/browser_resources.grd
+++ b/chromium/chrome/browser/browser_resources.grd
@@ -99,16 +99,11 @@
<include name="IDR_ARC_SUPPORT_MANIFEST" file="resources\chromeos\arc_support\manifest.json" type="BINDATA" />
<!-- Fingerprint resources. -->
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_LAPTOP_BOTTOM_LEFT_ANIMATION_DARK" file="resources\chromeos\quick_unlock\fingerprint_laptop_bottom_left_dark.json"/>
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_LAPTOP_BOTTOM_LEFT_ANIMATION_LIGHT" file="resources\chromeos\quick_unlock\fingerprint_laptop_bottom_left_light.json"/>
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_LAPTOP_BOTTOM_RIGHT_ANIMATION_DARK" file="resources\chromeos\quick_unlock\fingerprint_laptop_bottom_right_dark.json"/>
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_LAPTOP_BOTTOM_RIGHT_ANIMATION_LIGHT" file="resources\chromeos\quick_unlock\fingerprint_laptop_bottom_right_light.json"/>
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_LAPTOP_LEFT_OF_POWER_BUTTON_TOP_RIGHT_ANIMATION_LIGHT" file="resources\chromeos\quick_unlock\fingerprint_left_of_power_button_top_right_light.json"/>
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_LAPTOP_LEFT_OF_POWER_BUTTON_TOP_RIGHT_ANIMATION_DARK" file="resources\chromeos\quick_unlock\fingerprint_left_of_power_button_top_right_dark.json"/>
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_TABLET_ANIMATION_DARK" file="resources\chromeos\quick_unlock\fingerprint_tablet_dark.json"/>
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_TABLET_ANIMATION_LIGHT" file="resources\chromeos\quick_unlock\fingerprint_tablet_light.json"/>
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_DEFAULT_ANIMATION_DARK" file="resources\chromeos\quick_unlock\fingerprint_default_dark.json"/>
- <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_DEFAULT_ANIMATION_LIGHT" file="resources\chromeos\quick_unlock\fingerprint_default_light.json"/>
+ <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_LAPTOP_BOTTOM_LEFT_ANIMATION" file="resources\chromeos\quick_unlock\fingerprint_laptop_bottom_left.json"/>
+ <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_LAPTOP_BOTTOM_RIGHT_ANIMATION" file="resources\chromeos\quick_unlock\fingerprint_laptop_bottom_right.json"/>
+ <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_LAPTOP_LEFT_OF_POWER_BUTTON_TOP_RIGHT_ANIMATION" file="resources\chromeos\quick_unlock\fingerprint_left_of_power_button_top_right.json"/>
+ <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_TABLET_ANIMATION" file="resources\chromeos\quick_unlock\fingerprint_tablet.json"/>
+ <include type="BINDATA" compress="gzip" name="IDR_FINGERPRINT_DEFAULT_ANIMATION" file="resources\chromeos\quick_unlock\fingerprint_default.json"/>
<include name="IDR_VM_INDEX_HTML" file="resources\chromeos\vm\index.html" type="BINDATA" />
<include name="IDR_VM_APP_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\vm\app.js" type="BINDATA" use_base_dir="false" />
@@ -199,17 +194,15 @@
<include name="IDR_SYS_INTERNALS_IMAGE_CPU_SVG" file="resources\chromeos\sys_internals\img\cpu.svg" type="BINDATA" />
<include name="IDR_SYS_INTERNALS_IMAGE_MEMORY_SVG" file="resources\chromeos\sys_internals\img\memory.svg" type="BINDATA" />
<include name="IDR_SYS_INTERNALS_IMAGE_ZRAM_SVG" file="resources\chromeos\sys_internals\img\zram.svg" type="BINDATA" />
- <include name="IDR_ADD_SUPERVISION_HTML" file="resources\chromeos\add_supervision\add_supervision.html" type="chrome_html" />
- <include name="IDR_ADD_SUPERVISION_APP_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\add_supervision\add_supervision_app.js" use_base_dir="false" type="chrome_html" />
- <include name="IDR_ADD_SUPERVISION_UI_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\add_supervision\add_supervision_ui.js" use_base_dir="false" type="chrome_html" />
- <include name="IDR_ADD_SUPERVISION_API_SERVER_JS" file="resources\chromeos\add_supervision\add_supervision_api_server.js" type="BINDATA" />
- <include name="IDR_ADD_SUPERVISION_MOJOM_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\ash\add_supervision\add_supervision.mojom-lite.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_PARENT_ACCESS_HTML" file="resources\chromeos\parent_access\parent_access.html" type="chrome_html" />
<include name="IDR_PARENT_ACCESS_APP_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_app.js" use_base_dir="false" type="BINDATA" />
+ <include name="IDR_PARENT_ACCESS_TEMPLATE_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_template.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_PARENT_ACCESS_UI_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_ui.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_PARENT_ACCESS_AFTER_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_after.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_PARENT_ACCESS_BEFORE_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_before.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_PARENT_ACCESS_DISABLED_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_disabled.js" use_base_dir="false" type="BINDATA" />
+ <include name="IDR_PARENT_ACCESS_ERROR_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_error.js" use_base_dir="false" type="BINDATA" />
+ <include name="IDR_PARENT_ACCESS_OFFLINE_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_offline.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_PARENT_ACCESS_WEBVIEW_MANAGER_JS" file="resources\chromeos\parent_access\webview_manager.js" type="BINDATA" />
<include name="IDR_PARENT_ACCESS_UTILS_JS" file="resources\chromeos\parent_access\utils.js" type="BINDATA" />
<include name="IDR_LOCAL_WEB_APPROVALS_AFTER_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\flows\local_web_approvals_after.js" use_base_dir="false" type="BINDATA" />
@@ -257,8 +250,6 @@
<include name="IDR_DESK_API_MANIFEST" file="resources\chromeos\desk_api\manifest.json" type="BINDATA" />
</if>
- <include name="IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST" file="resources\identity_scope_approval_dialog\manifest.json" type="BINDATA" />
-
<include name="IDR_INSPECT_CSS" file="resources\inspect\inspect.css" flattenhtml="true" type="BINDATA" />
<include name="IDR_INSPECT_HTML" file="resources\inspect\inspect.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_INSPECT_JS" file="resources\inspect\inspect.js" type="BINDATA" />
diff --git a/chromium/chrome/browser/browsing_data/android/BUILD.gn b/chromium/chrome/browser/browsing_data/android/BUILD.gn
new file mode 100644
index 00000000000..07607d53298
--- /dev/null
+++ b/chromium/chrome/browser/browsing_data/android/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+ sources = [
+ "java/src/org/chromium/chrome/browser/browsing_data/TimePeriodUtils.java",
+ ]
+
+ deps = [
+ "//chrome/browser/ui/android/strings:ui_strings_grd",
+ "//components/browsing_data/core:java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ ]
+
+ resources_package = "org.chromium.chrome.browser.browsing_data"
+}
diff --git a/chromium/chrome/browser/chrome_for_testing/BUILD.gn b/chromium/chrome/browser/chrome_for_testing/BUILD.gn
index ec406ea458b..e5cf21002bd 100644
--- a/chromium/chrome/browser/chrome_for_testing/BUILD.gn
+++ b/chromium/chrome/browser/chrome_for_testing/BUILD.gn
@@ -6,28 +6,18 @@ import("//build/buildflag_header.gni")
import("//build/config/chrome_build.gni")
import("//components/nacl/features.gni")
-if (is_linux) {
- import("//build/linux/strip_binary.gni")
-}
-
-buildflag_header("buildflags") {
- header = "buildflags.h"
+if (is_chrome_for_testing) {
+ group("chrome_for_testing") {
+ deps = [ ":generate_about_file" ]
- if (is_chrome_for_testing) {
- flags = [ "CHROME_FOR_TESTING=1" ]
- } else {
- flags = [ "CHROME_FOR_TESTING=0" ]
+ if (is_linux) {
+ deps += [ ":strip_linux_files" ]
+ }
}
+}
- # Note:
- # - `GOOGLE_CHROME_FOR_TESTING_BRANDING` and `GOOGLE_CHROME_BRANDING` are
- # mutually exclusive.
- # - `GOOGLE_CHROME_FOR_TESTING_BRANDING` and `CHROMIUM_BRANDING` are not.
- if (is_chrome_for_testing_branded) {
- flags += [ "GOOGLE_CHROME_FOR_TESTING_BRANDING=1" ]
- } else {
- flags += [ "GOOGLE_CHROME_FOR_TESTING_BRANDING=0" ]
- }
+if (is_linux) {
+ import("//build/linux/strip_binary.gni")
}
if (is_linux && is_chrome_for_testing) {
@@ -36,6 +26,7 @@ if (is_linux && is_chrome_for_testing) {
":strip_chrome_binary",
":strip_chrome_crashpad_handler",
":strip_chrome_sandbox",
+ ":strip_headless_shell_binary",
":strip_libEGL_shlib",
":strip_libGLESv2_shlib",
":strip_libvk_swiftshader_shlib",
@@ -84,6 +75,11 @@ if (is_linux && is_chrome_for_testing) {
deps = [ "//third_party/swiftshader/src/Vulkan:swiftshader_libvulkan" ]
}
+ strip_binary("strip_headless_shell_binary") {
+ binary_input = "$root_out_dir/headless_shell"
+ deps = [ "//headless:headless_shell" ]
+ }
+
if (enable_nacl) {
strip_binary("strip_nacl_helper") {
binary_input = "$root_out_dir/nacl_helper"
@@ -96,3 +92,14 @@ if (is_linux && is_chrome_for_testing) {
}
}
}
+
+if (is_chrome_for_testing) {
+ action("generate_about_file") {
+ script = "//chrome/browser/chrome_for_testing/tools/generate_about_file.py"
+ args = [
+ "--output-file",
+ rebase_path("$root_out_dir/ABOUT"),
+ ]
+ outputs = [ "$root_out_dir/ABOUT" ]
+ }
+}
diff --git a/chromium/chrome/browser/chrome_notification_types.h b/chromium/chrome/browser/chrome_notification_types.h
index 94b4b3350e5..3b6ebdb5016 100644
--- a/chromium/chrome/browser/chrome_notification_types.h
+++ b/chromium/chrome/browser/chrome_notification_types.h
@@ -7,19 +7,8 @@
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
-#include "extensions/buildflags/buildflags.h"
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/browser/notification_types.h"
-#else
#include "content/public/browser/notification_types.h"
-#endif
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#define PREVIOUS_END extensions::NOTIFICATION_EXTENSIONS_END
-#else
-#define PREVIOUS_END content::NOTIFICATION_CONTENT_END
-#endif
+#include "extensions/buildflags/buildflags.h"
// **
// ** NOTICE
@@ -35,7 +24,7 @@
namespace chrome {
enum NotificationType {
- NOTIFICATION_CHROME_START = PREVIOUS_END,
+ NOTIFICATION_CHROME_START = content::NOTIFICATION_CONTENT_END,
// Authentication ----------------------------------------------------------
diff --git a/chromium/chrome/browser/chromeos/BUILD.gn b/chromium/chrome/browser/chromeos/BUILD.gn
index 116592df9da..b2d25afe6f3 100644
--- a/chromium/chrome/browser/chromeos/BUILD.gn
+++ b/chromium/chrome/browser/chromeos/BUILD.gn
@@ -16,14 +16,6 @@ assert(use_ozone)
source_set("chromeos") {
configs += [ "//build/config/compiler:wexit_time_destructors" ]
sources = [
- "app_mode/app_session.cc",
- "app_mode/app_session.h",
- "app_mode/app_session_browser_window_handler.cc",
- "app_mode/app_session_browser_window_handler.h",
- "app_mode/app_session_metrics_service.cc",
- "app_mode/app_session_metrics_service.h",
- "app_mode/app_session_policies.cc",
- "app_mode/app_session_policies.h",
"app_mode/chrome_kiosk_app_installer.cc",
"app_mode/chrome_kiosk_app_installer.h",
"app_mode/chrome_kiosk_app_launcher.cc",
@@ -34,12 +26,22 @@ source_set("chromeos") {
"app_mode/kiosk_app_external_loader.h",
"app_mode/kiosk_app_service_launcher.cc",
"app_mode/kiosk_app_service_launcher.h",
+ "app_mode/kiosk_browser_session.cc",
+ "app_mode/kiosk_browser_session.h",
+ "app_mode/kiosk_browser_window_handler.cc",
+ "app_mode/kiosk_browser_window_handler.h",
+ "app_mode/kiosk_metrics_service.cc",
+ "app_mode/kiosk_metrics_service.h",
+ "app_mode/kiosk_policies.cc",
+ "app_mode/kiosk_policies.h",
"app_mode/kiosk_settings_navigation_throttle.cc",
"app_mode/kiosk_settings_navigation_throttle.h",
"app_mode/kiosk_troubleshooting_controller.cc",
"app_mode/kiosk_troubleshooting_controller.h",
"app_mode/startup_app_launcher_update_checker.cc",
"app_mode/startup_app_launcher_update_checker.h",
+ "app_mode/web_kiosk_app_installer.cc",
+ "app_mode/web_kiosk_app_installer.h",
"app_mode/web_kiosk_browser_controller_base.cc",
"app_mode/web_kiosk_browser_controller_base.h",
"arc/arc_external_protocol_dialog.cc",
@@ -50,8 +52,12 @@ source_set("chromeos") {
"arc/open_with_menu.h",
"arc/start_smart_selection_action_menu.cc",
"arc/start_smart_selection_action_menu.h",
+ "enterprise/cloud_storage/policy_utils.cc",
+ "enterprise/cloud_storage/policy_utils.h",
"enterprise/incognito_navigation_throttle.cc",
"enterprise/incognito_navigation_throttle.h",
+ "extensions/accessibility_service_private.cc",
+ "extensions/accessibility_service_private.h",
"extensions/contact_center_insights/contact_center_insights_extension_manager.cc",
"extensions/contact_center_insights/contact_center_insights_extension_manager.h",
"extensions/device_local_account_external_policy_loader.cc",
@@ -67,6 +73,8 @@ source_set("chromeos") {
"extensions/login_screen/login/cleanup/cleanup_manager.h",
"extensions/login_screen/login/cleanup/extension_cleanup_handler.cc",
"extensions/login_screen/login/cleanup/extension_cleanup_handler.h",
+ "extensions/login_screen/login/cleanup/web_app_cleanup_handler.cc",
+ "extensions/login_screen/login/cleanup/web_app_cleanup_handler.h",
"extensions/login_screen/login/external_logout_request/external_logout_request_event_handler.cc",
"extensions/login_screen/login/external_logout_request/external_logout_request_event_handler.h",
"extensions/login_screen/login/external_logout_request/external_logout_request_event_handler_factory.cc",
@@ -83,16 +91,6 @@ source_set("chromeos") {
"extensions/login_screen/login_state/session_state_changed_event_dispatcher.h",
"extensions/system_log/system_log_api.cc",
"extensions/system_log/system_log_api.h",
- "kcer_nss/cert_cache_nss.cc",
- "kcer_nss/cert_cache_nss.h",
- "kcer_nss/kcer_token_impl_nss.cc",
- "kcer_nss/kcer_token_impl_nss.h",
- "platform_keys/chaps_slot_session.cc",
- "platform_keys/chaps_slot_session.h",
- "platform_keys/chaps_util.cc",
- "platform_keys/chaps_util.h",
- "platform_keys/chaps_util_impl.cc",
- "platform_keys/chaps_util_impl.h",
"platform_keys/extension_key_permissions_service.cc",
"platform_keys/extension_key_permissions_service.h",
"platform_keys/extension_key_permissions_service_factory.cc",
@@ -101,8 +99,6 @@ source_set("chromeos") {
"platform_keys/extension_platform_keys_service.h",
"platform_keys/extension_platform_keys_service_factory.cc",
"platform_keys/extension_platform_keys_service_factory.h",
- "platform_keys/pkcs12_reader.cc",
- "platform_keys/pkcs12_reader.h",
"platform_keys/platform_keys.cc",
"platform_keys/platform_keys.h",
"policy/dlp/clipboard_bubble.cc",
@@ -135,6 +131,10 @@ source_set("chromeos") {
"policy/dlp/dlp_copy_or_move_hook_delegate.h",
"policy/dlp/dlp_data_transfer_notifier.cc",
"policy/dlp/dlp_data_transfer_notifier.h",
+ "policy/dlp/dlp_download_observer.cc",
+ "policy/dlp/dlp_download_observer.h",
+ "policy/dlp/dlp_download_observer_factory.cc",
+ "policy/dlp/dlp_download_observer_factory.h",
"policy/dlp/dlp_drag_drop_notifier.cc",
"policy/dlp/dlp_drag_drop_notifier.h",
"policy/dlp/dlp_file_access_copy_or_move_delegate_factory.cc",
@@ -159,20 +159,27 @@ source_set("chromeos") {
"policy/dlp/dlp_rules_manager_impl.h",
"policy/dlp/dlp_scoped_file_access_delegate.cc",
"policy/dlp/dlp_scoped_file_access_delegate.h",
+ "quickoffice/quickoffice_prefs.cc",
+ "quickoffice/quickoffice_prefs.h",
"reporting/metric_default_utils.cc",
"reporting/metric_default_utils.h",
"reporting/metric_reporting_manager_delegate_base.cc",
"reporting/metric_reporting_manager_delegate_base.h",
+ "reporting/metric_reporting_prefs.cc",
+ "reporting/metric_reporting_prefs.h",
"reporting/network/network_bandwidth_sampler.cc",
"reporting/network/network_bandwidth_sampler.h",
"reporting/user_reporting_settings.cc",
"reporting/user_reporting_settings.h",
+ "reporting/websites/website_metrics_retriever_interface.h",
"smart_reader/smart_reader_client_impl.cc",
"smart_reader/smart_reader_client_impl.h",
"tablet_mode/chrome_content_browser_client_tablet_mode_part.cc",
"tablet_mode/chrome_content_browser_client_tablet_mode_part.h",
"tablet_mode/tablet_mode_page_behavior.cc",
"tablet_mode/tablet_mode_page_behavior.h",
+ "upload_office_to_cloud/upload_office_to_cloud.cc",
+ "upload_office_to_cloud/upload_office_to_cloud.h",
"video_conference/video_conference_manager_client.cc",
"video_conference/video_conference_manager_client.h",
"video_conference/video_conference_manager_client_common.cc",
@@ -226,8 +233,6 @@ source_set("chromeos") {
"//components/reporting/client:report_queue_configuration",
"//components/reporting/metrics:metrics_data_collection",
"//components/reporting/util:status",
- "//components/services/app_service/public/cpp:app_types",
- "//components/services/app_service/public/cpp:app_update",
"//components/url_matcher",
"//content/public/browser",
"//extensions/browser",
@@ -255,6 +260,7 @@ source_set("chromeos") {
"//chrome/browser:browser_process",
"//chrome/browser:resources",
"//chrome/browser/apps/app_service",
+ "//chrome/browser/apps/link_capturing",
"//chrome/browser/browsing_data:constants",
"//chrome/browser/enterprise/data_controls",
"//chrome/browser/favicon",
@@ -265,11 +271,13 @@ source_set("chromeos") {
"//chrome/common:non_code_constants",
"//chromeos/components/kcer",
"//chromeos/components/kcer:key_permissions_proto",
+ "//chromeos/constants",
"//chromeos/dbus/dlp",
"//chromeos/ui/base",
"//components/app_constants",
"//components/arc/common:arc_intent_helper_constants",
"//components/device_event_log",
+ "//components/download/content/public:public",
"//components/policy:generated",
"//components/policy/core/common",
"//components/policy/core/common:common_constants",
@@ -277,7 +285,8 @@ source_set("chromeos") {
"//components/pref_registry",
"//components/reporting/client:report_queue_factory",
"//components/reporting/proto:metric_data_proto",
- "//components/services/app_service/public/cpp:intents",
+ "//components/reporting/util:rate_limiter_interface",
+ "//components/services/app_service",
"//components/strings:components_strings",
"//components/sync/protocol",
"//components/sync_device_info",
@@ -340,6 +349,8 @@ source_set("chromeos") {
"extensions/login_screen/login/login_api_lock_handler.h",
"extensions/login_screen/login/shared_session_handler.cc",
"extensions/login_screen/login/shared_session_handler.h",
+ "reporting/websites/website_metrics_retriever_ash.cc",
+ "reporting/websites/website_metrics_retriever_ash.h",
]
allow_circular_includes_from += [ "//chrome/browser/ash/crosapi" ]
public_deps += [
@@ -355,6 +366,7 @@ source_set("chromeos") {
"//ash/webui/file_manager:file_manager_ui",
"//ash/webui/file_manager:file_manager_untrusted_ui",
"//chrome/browser/ash/video_conference",
+ "//chromeos/ash/components/chaps_util",
"//chromeos/ash/components/login/auth/public:authpublic",
"//chromeos/ash/components/settings",
"//components/session_manager:base",
@@ -380,6 +392,8 @@ source_set("chromeos") {
"reporting/device_reporting_settings_lacros.h",
"reporting/metric_reporting_manager_lacros.cc",
"reporting/metric_reporting_manager_lacros.h",
+ "reporting/websites/website_metrics_retriever_lacros.cc",
+ "reporting/websites/website_metrics_retriever_lacros.h",
]
public_deps += [
"//chromeos/lacros",
@@ -414,29 +428,38 @@ static_library("test_support") {
"extensions/login_screen/login/cleanup/mock_cleanup_handler.h",
"policy/dlp/dialogs/mock_dlp_warn_notifier.cc",
"policy/dlp/dialogs/mock_dlp_warn_notifier.h",
- "policy/dlp/dlp_content_manager_test_helper.cc",
- "policy/dlp/dlp_content_manager_test_helper.h",
- "policy/dlp/dlp_reporting_manager_test_helper.cc",
- "policy/dlp/dlp_reporting_manager_test_helper.h",
- "policy/dlp/dlp_rules_manager_test_utils.cc",
- "policy/dlp/dlp_rules_manager_test_utils.h",
- "policy/dlp/mock_dlp_content_manager.cc",
- "policy/dlp/mock_dlp_content_manager.h",
- "policy/dlp/mock_dlp_content_observer.cc",
- "policy/dlp/mock_dlp_content_observer.h",
- "policy/dlp/mock_dlp_rules_manager.cc",
- "policy/dlp/mock_dlp_rules_manager.h",
+ "policy/dlp/test/dlp_content_manager_test_helper.cc",
+ "policy/dlp/test/dlp_content_manager_test_helper.h",
+ "policy/dlp/test/dlp_files_test_base.cc",
+ "policy/dlp/test/dlp_files_test_base.h",
+ "policy/dlp/test/dlp_reporting_manager_test_helper.cc",
+ "policy/dlp/test/dlp_reporting_manager_test_helper.h",
+ "policy/dlp/test/dlp_rules_manager_test_utils.cc",
+ "policy/dlp/test/dlp_rules_manager_test_utils.h",
+ "policy/dlp/test/mock_dlp_content_manager.cc",
+ "policy/dlp/test/mock_dlp_content_manager.h",
+ "policy/dlp/test/mock_dlp_content_observer.cc",
+ "policy/dlp/test/mock_dlp_content_observer.h",
+ "policy/dlp/test/mock_dlp_rules_manager.cc",
+ "policy/dlp/test/mock_dlp_rules_manager.h",
]
public_deps = [
":chromeos",
"//base",
"//build:chromeos_buildflags",
+ "//chrome/test:test_support",
+ "//content/test:test_support",
"//crypto:test_support",
"//testing/gmock",
"//testing/gtest",
"//ui/views",
"//url",
]
+
+ if (is_chromeos_ash) {
+ public_deps += [ "//chrome/browser/ash:test_support" ]
+ }
+
deps = [
":dlp_policy_event_proto",
"//components/reporting/client:test_support",
@@ -451,8 +474,10 @@ source_set("unit_tests") {
testonly = true
sources = [
- "app_mode/app_session_policies_unittest.cc",
"app_mode/chrome_kiosk_external_loader_broker_unittest.cc",
+ "app_mode/kiosk_app_service_launcher_unittest.cc",
+ "app_mode/kiosk_browser_session_unittest.cc",
+ "app_mode/kiosk_policies_unittest.cc",
"arc/arc_external_protocol_dialog_unittest.cc",
"arc/open_with_menu_unittest.cc",
"extensions/contact_center_insights/contact_center_insights_extension_manager_unittest.cc",
@@ -461,10 +486,6 @@ source_set("unit_tests") {
"extensions/login_screen/login/cleanup/cleanup_manager_unittest.cc",
"extensions/login_screen/login/external_logout_request/external_logout_request_event_handler_unittest.cc",
"extensions/login_screen/login_state/login_state_api_unittest.cc",
- "kcer_nss/cert_cache_nss_unittest.cc",
- "kcer_nss/kcer_nss_unittest.cc",
- "platform_keys/chaps_util_impl_unittest.cc",
- "platform_keys/pkcs12_reader_unittest.cc",
"policy/dlp/data_transfer_dlp_controller_unittest.cc",
"policy/dlp/dlp_clipboard_notifier_unittest.cc",
"policy/dlp/dlp_confidential_contents_unittest.cc",
@@ -499,6 +520,7 @@ source_set("unit_tests") {
"//chrome/app:generated_resources",
"//chrome/browser",
"//chrome/browser:browser_process",
+ "//chrome/browser/apps/app_service:test_support",
"//chrome/browser/enterprise/data_controls:data_controls",
"//chrome/browser/extensions",
"//chrome/browser/profiles:profile",
@@ -529,6 +551,7 @@ source_set("unit_tests") {
"//components/reporting/util:test_callbacks_support",
"//components/strings:components_strings",
"//components/ukm",
+ "//components/user_manager",
"//content/public/browser",
"//content/test:test_support",
"//extensions:test_support",
@@ -559,9 +582,7 @@ source_set("unit_tests") {
if (is_chromeos_ash) {
sources += [
- "app_mode/app_session_unittest.cc",
"app_mode/chrome_kiosk_app_launcher_unittest.cc",
- "app_mode/kiosk_app_service_launcher_unittest.cc",
"extensions/login_screen/login/cleanup/extension_cleanup_handler_unittest.cc",
"extensions/login_screen/login/cleanup/lacros_cleanup_handler_unittest.cc",
"extensions/login_screen/login/cleanup/print_jobs_cleanup_handler_unittest.cc",
@@ -569,12 +590,12 @@ source_set("unit_tests") {
"extensions/login_screen/login/login_api_ash_unittest.cc",
"extensions/login_screen/login_screen_storage/login_screen_storage_api_ash_unittest.cc",
"extensions/login_screen/login_state/session_state_changed_event_dispatcher_ash_unittest.cc",
+ "reporting/websites/website_metrics_retriever_ash_unittest.cc",
"tablet_mode/chrome_content_browser_client_tablet_mode_part_unittest.cc",
]
deps += [
"//ash:test_support",
"//ash/constants",
- "//chrome/browser/apps/app_service:test_support",
"//chrome/browser/ash",
"//chrome/browser/ash:test_support",
"//chrome/browser/ash/crosapi",
@@ -587,7 +608,7 @@ source_set("unit_tests") {
"//chromeos/dbus/power",
"//components/history/core/test",
"//components/prefs:test_support",
- "//components/services/app_service/public/cpp:app_types",
+ "//components/services/app_service",
"//components/session_manager:base",
"//components/session_manager/core",
"//components/sync_preferences:test_support",
@@ -615,6 +636,7 @@ source_set("unit_tests") {
"policy/dlp/dlp_files_controller_lacros_unittest.cc",
"reporting/device_reporting_settings_lacros_unittest.cc",
"reporting/metric_reporting_manager_lacros_unittest.cc",
+ "reporting/websites/website_metrics_retriever_lacros_unittest.cc",
]
deps += [
"//chromeos/lacros",
diff --git a/chromium/chrome/browser/chromeos/extensions/telemetry/api/BUILD.gn b/chromium/chrome/browser/chromeos/extensions/telemetry/api/BUILD.gn
index f308c058e16..6932a53e7e6 100644
--- a/chromium/chrome/browser/chromeos/extensions/telemetry/api/BUILD.gn
+++ b/chromium/chrome/browser/chromeos/extensions/telemetry/api/BUILD.gn
@@ -32,7 +32,10 @@ source_set("keyed_service_factory") {
"telemetry_extension_api_browser_context_keyed_service_factories.h",
]
- deps = [ "events:event_manager" ]
+ deps = [
+ "events:event_manager",
+ "routines:routine_manager",
+ ]
}
source_set("browser_tests") {
@@ -42,6 +45,7 @@ source_set("browser_tests") {
"common:browser_tests",
"diagnostics:browser_tests",
"events:browser_tests",
+ "routines:browser_tests",
"telemetry:browser_tests",
]
}
@@ -59,6 +63,7 @@ source_set("unit_tests") {
"common:unit_tests",
"diagnostics:unit_tests",
"events:unit_tests",
+ "routines:unit_tests",
"telemetry:unit_tests",
]
}
diff --git a/chromium/chrome/browser/chromeos/extensions/telemetry/api/common/BUILD.gn b/chromium/chrome/browser/chromeos/extensions/telemetry/api/common/BUILD.gn
index b431c025899..991a2e14f06 100644
--- a/chromium/chrome/browser/chromeos/extensions/telemetry/api/common/BUILD.gn
+++ b/chromium/chrome/browser/chromeos/extensions/telemetry/api/common/BUILD.gn
@@ -11,21 +11,20 @@ source_set("common") {
sources = [
"api_guard_delegate.cc",
"api_guard_delegate.h",
+ "app_ui_observer.cc",
+ "app_ui_observer.h",
"base_telemetry_extension_api_guard_function.cc",
"base_telemetry_extension_api_guard_function.h",
]
deps = [
":hardware_info_delegate",
+ ":util",
"//base",
"//build:chromeos_buildflags",
"//chrome/browser/extensions",
"//chrome/browser/profiles:profile",
- "//chrome/browser/ui",
"//chrome/common/chromeos/extensions",
- "//components/account_id",
- "//components/security_state/content",
- "//components/security_state/core",
"//content/public/browser",
"//extensions/browser",
"//extensions/common",
@@ -33,7 +32,12 @@ source_set("common") {
]
if (is_chromeos_ash) {
- deps += [ "//components/user_manager" ]
+ deps += [
+ "//ash/constants",
+ "//chromeos/ash/components/browser_context_helper",
+ "//components/account_id",
+ "//components/user_manager",
+ ]
}
if (is_chromeos_lacros) {
@@ -45,6 +49,30 @@ source_set("common") {
}
}
+source_set("util") {
+ sources = [
+ "util.cc",
+ "util.h",
+ ]
+
+ deps = [
+ "//chrome/browser/profiles:profile",
+ "//chrome/browser/ui",
+ "//chrome/common",
+ "//chromeos/constants",
+ "//components/security_state/content",
+ "//components/security_state/core",
+ "//extensions/common",
+ ]
+
+ if (is_chromeos_ash) {
+ deps += [
+ "//ash/constants",
+ "//ash/webui/shimless_rma/3p_diagnostics",
+ ]
+ }
+}
+
source_set("remote_probe_strategy") {
sources = [
"remote_probe_service_strategy.cc",
@@ -105,7 +133,9 @@ source_set("test_support") {
":common",
"//base",
"//chrome/test:test_support",
+ "//content/public/browser",
"//extensions:test_support",
+ "//net",
"//testing/gtest",
"//third_party/abseil-cpp:absl",
]
@@ -179,6 +209,7 @@ source_set("unit_tests") {
if (is_chromeos_ash) {
deps += [
"//chrome/browser/ash:test_support",
+ "//chrome/common/chromeos/extensions",
"//components/user_manager",
]
}
diff --git a/chromium/chrome/browser/chromeos/extensions/telemetry/api/diagnostics/BUILD.gn b/chromium/chrome/browser/chromeos/extensions/telemetry/api/diagnostics/BUILD.gn
index e57fa161c37..4fbca77a8cb 100644
--- a/chromium/chrome/browser/chromeos/extensions/telemetry/api/diagnostics/BUILD.gn
+++ b/chromium/chrome/browser/chromeos/extensions/telemetry/api/diagnostics/BUILD.gn
@@ -22,9 +22,11 @@ source_set("diagnostics") {
"//base",
"//build:chromeos_buildflags",
"//chrome/browser/chromeos/extensions/telemetry/api/common",
+ "//chrome/browser/chromeos/extensions/telemetry/api/routines:routine_manager",
"//chrome/common/chromeos/extensions/api",
"//chromeos/crosapi/mojom",
"//extensions/browser",
+ "//extensions/common",
"//mojo/public/cpp/bindings",
]
@@ -42,6 +44,7 @@ source_set("browser_tests") {
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
sources = [
"diagnostics_api_browsertest.cc",
+ "diagnostics_api_v2_browsertest.cc",
"fake_diagnostics_service.cc",
"fake_diagnostics_service.h",
]
@@ -51,8 +54,10 @@ source_set("browser_tests") {
"//base",
"//build:chromeos_buildflags",
"//chrome/browser/chromeos/extensions/telemetry/api/common:test_support",
+ "//chrome/browser/chromeos/extensions/telemetry/api/routines:test_support",
"//chromeos/crosapi/mojom",
"//content/test:test_support",
+ "//extensions/common",
"//testing/gtest",
"//third_party/abseil-cpp:absl",
]
diff --git a/chromium/chrome/browser/chromeos/extensions/telemetry/api/events/BUILD.gn b/chromium/chrome/browser/chromeos/extensions/telemetry/api/events/BUILD.gn
index c844a24baea..097dacc8558 100644
--- a/chromium/chrome/browser/chromeos/extensions/telemetry/api/events/BUILD.gn
+++ b/chromium/chrome/browser/chromeos/extensions/telemetry/api/events/BUILD.gn
@@ -80,15 +80,17 @@ source_set("event_manager") {
deps = [
":event_router",
"//base",
- "//chrome/browser/profiles:profile",
- "//chrome/browser/ui",
+ "//chrome/browser/chromeos/extensions/telemetry/api/common",
+ "//chrome/browser/chromeos/extensions/telemetry/api/common:util",
"//chrome/common/chromeos/extensions",
"//chromeos/crosapi/mojom",
"//content/public/browser",
- "//extensions/common",
]
- public_deps = [ "//extensions/browser" ]
+ public_deps = [
+ "//extensions/browser",
+ "//extensions/common",
+ ]
}
source_set("event_converters") {
@@ -105,17 +107,38 @@ source_set("event_converters") {
]
}
-source_set("browser_tests") {
+source_set("test_support") {
testonly = true
- defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
sources = [
- "events_api_browsertest.cc",
"fake_events_service.cc",
"fake_events_service.h",
]
+ deps = [ "//base/test:test_support" ]
+
+ public_deps = [
+ "//chromeos/crosapi/mojom",
+ "//mojo/public/cpp/bindings",
+ ]
+
+ if (is_chromeos_ash) {
+ sources += [
+ "fake_events_service_factory.cc",
+ "fake_events_service_factory.h",
+ ]
+
+ public_deps += [ "//chrome/browser/ash/telemetry_extension/events" ]
+ }
+}
+
+source_set("browser_tests") {
+ testonly = true
+ defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+ sources = [ "events_api_browsertest.cc" ]
+
deps = [
":events",
+ ":test_support",
"//chrome/browser/chromeos/extensions/telemetry/api/common:test_support",
"//chrome/test:test_support_ui",
"//chromeos/crosapi/mojom",
@@ -129,13 +152,7 @@ source_set("browser_tests") {
data = [ "//chrome/test/data" ]
if (is_chromeos_ash) {
- sources += [
- "fake_events_service_factory.cc",
- "fake_events_service_factory.h",
- ]
-
deps += [
- "//chrome/browser/ash/telemetry_extension/events",
"//chrome/browser/profiles:profile",
"//chrome/browser/ui",
]
@@ -148,15 +165,39 @@ source_set("browser_tests") {
source_set("unit_tests") {
testonly = true
- sources = [ "events_api_converters_unittest.cc" ]
+ sources = [
+ "event_manager_unittest.cc",
+ "events_api_converters_unittest.cc",
+ ]
deps = [
":event_converters",
+ ":event_manager",
+ ":event_router",
+ ":test_support",
+ "//chrome/browser/chromeos/extensions/telemetry/api/common:common",
+ "//chrome/browser/ui",
+ "//chrome/common/chromeos/extensions",
"//chrome/common/chromeos/extensions/api",
+ "//chrome/test:test_support",
"//chromeos/crosapi/mojom",
+ "//content/public/browser",
+ "//net",
+ "//net:test_support",
"//testing/gmock",
"//testing/gtest",
]
+
+ if (is_chromeos_ash) {
+ deps += [
+ "//chrome/common",
+ "//chromeos/constants",
+ ]
+ }
+
+ if (is_chromeos_lacros) {
+ deps += [ "//chromeos/lacros" ]
+ }
}
source_set("extensions_unittests") {
diff --git a/chromium/chrome/browser/chromeos/extensions/telemetry/api/routines/BUILD.gn b/chromium/chrome/browser/chromeos/extensions/telemetry/api/routines/BUILD.gn
new file mode 100644
index 00000000000..48720aeb19e
--- /dev/null
+++ b/chromium/chrome/browser/chromeos/extensions/telemetry/api/routines/BUILD.gn
@@ -0,0 +1,138 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import("//build/config/chromeos/ui_mode.gni")
+
+source_set("routines") {
+ sources = [
+ "diagnostic_routine.cc",
+ "diagnostic_routine.h",
+ "diagnostic_routine_converters.cc",
+ "diagnostic_routine_converters.h",
+ "diagnostic_routine_observation.cc",
+ "diagnostic_routine_observation.h",
+ "remote_diagnostic_routines_service_strategy.cc",
+ "remote_diagnostic_routines_service_strategy.h",
+ ]
+
+ deps = [
+ "//base",
+ "//chrome/common/chromeos/extensions/api",
+ "//chromeos/crosapi/mojom",
+ "//content/public/browser",
+ "//extensions/common",
+ "//mojo/public/cpp/bindings",
+ ]
+
+ public_deps = [ "//extensions/browser" ]
+
+ if (is_chromeos_ash) {
+ deps += [ "//chrome/browser/ash/telemetry_extension/routines" ]
+ }
+
+ if (is_chromeos_lacros) {
+ deps += [ "//chromeos/lacros" ]
+ }
+}
+
+source_set("test_support") {
+ testonly = true
+ sources = [
+ "fake_diagnostic_routine_control.cc",
+ "fake_diagnostic_routine_control.h",
+ "fake_diagnostic_routines_service.cc",
+ "fake_diagnostic_routines_service.h",
+ ]
+
+ deps = [ "//base/test:test_support" ]
+
+ public_deps = [
+ "//chromeos/crosapi/mojom",
+ "//mojo/public/cpp/bindings",
+ ]
+
+ if (is_chromeos_ash) {
+ sources += [
+ "fake_diagnostic_routines_service_factory.cc",
+ "fake_diagnostic_routines_service_factory.h",
+ ]
+
+ public_deps += [ "//chrome/browser/ash/telemetry_extension/routines" ]
+ }
+}
+
+source_set("routine_manager") {
+ sources = [
+ "diagnostic_routine_manager.cc",
+ "diagnostic_routine_manager.h",
+ ]
+
+ deps = [
+ ":routines",
+ "//base",
+ "//chrome/browser/chromeos/extensions/telemetry/api/common",
+ "//chrome/browser/chromeos/extensions/telemetry/api/common:util",
+ "//chromeos/crosapi/mojom",
+ "//content/public/browser",
+ ]
+
+ public_deps = [
+ "//extensions/browser",
+ "//extensions/common",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "diagnostic_routine_converters_unittest.cc",
+ "diagnostic_routine_manager_unittest.cc",
+ ]
+
+ deps = [
+ ":routine_manager",
+ ":routines",
+ ":test_support",
+ "//chrome/browser/ui",
+ "//chrome/browser/ui/tabs:tab_enums",
+ "//chrome/common/chromeos/extensions/api",
+ "//chrome/test:test_support",
+ "//chromeos/crosapi/mojom",
+ "//extensions:test_support",
+ "//extensions/common",
+ "//net:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+
+ if (is_chromeos_ash) {
+ deps += [
+ "//chrome/browser/ash/telemetry_extension/routines",
+ "//chrome/common/chromeos/extensions",
+ ]
+ }
+
+ if (is_chromeos_lacros) {
+ deps += [ "//chromeos/lacros" ]
+ }
+}
+
+source_set("browser_tests") {
+ testonly = true
+ defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+ sources = [ "diagnostic_routine_observation_browsertest.cc" ]
+
+ deps = [
+ ":routines",
+ "//base",
+ "//chrome/browser/chromeos/extensions/telemetry/api/common:test_support",
+ "//chrome/common/chromeos/extensions/api",
+ "//chrome/test:test_support_ui",
+ "//chromeos/crosapi/mojom",
+ "//extensions/common",
+ "//mojo/public/cpp/bindings",
+ "//testing/gtest",
+ ]
+
+ data = [ "//chrome/test/data" ]
+}
diff --git a/chromium/chrome/browser/chromeos/extensions/telemetry/api/telemetry/BUILD.gn b/chromium/chrome/browser/chromeos/extensions/telemetry/api/telemetry/BUILD.gn
index 6c49bbf220c..cf55eaf5c21 100644
--- a/chromium/chrome/browser/chromeos/extensions/telemetry/api/telemetry/BUILD.gn
+++ b/chromium/chrome/browser/chromeos/extensions/telemetry/api/telemetry/BUILD.gn
@@ -8,6 +8,22 @@ import("//extensions/buildflags/buildflags.gni")
assert(enable_extensions,
"Cannot depend on extensions because enable_extensions=false.")
+source_set("test_support") {
+ testonly = true
+
+ sources = [
+ "fake_probe_service.cc",
+ "fake_probe_service.h",
+ ]
+
+ deps = [
+ "//base",
+ "//chromeos/crosapi/mojom",
+ "//mojo/public/cpp/bindings",
+ "//testing/gtest",
+ ]
+}
+
source_set("telemetry") {
sources = [
"telemetry_api.cc",
@@ -36,14 +52,11 @@ source_set("telemetry") {
source_set("browser_tests") {
testonly = true
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
- sources = [
- "fake_probe_service.cc",
- "fake_probe_service.h",
- "telemetry_api_browsertest.cc",
- ]
+ sources = [ "telemetry_api_browsertest.cc" ]
deps = [
":telemetry",
+ ":test_support",
"//base",
"//build:chromeos_buildflags",
"//chrome/browser/chromeos/extensions/telemetry/api/common:test_support",
diff --git a/chromium/chrome/browser/chromeos/office_web_app/BUILD.gn b/chromium/chrome/browser/chromeos/office_web_app/BUILD.gn
index d0e9faf36a3..59e35ab1876 100644
--- a/chromium/chrome/browser/chromeos/office_web_app/BUILD.gn
+++ b/chromium/chrome/browser/chromeos/office_web_app/BUILD.gn
@@ -12,10 +12,6 @@ source_set("office_web_app") {
deps = [
"//chrome/browser/apps/app_service",
"//chrome/browser/web_applications",
-
- # CrosAPI is a transitive dependency of //c/b/apps/app_service, but
- # compilation breaks unless it is depended upon directly here.
- "//chromeos/crosapi/mojom",
"//components/webapps/browser",
"//url",
]
diff --git a/chromium/chrome/browser/commerce/android/BUILD.gn b/chromium/chrome/browser/commerce/android/BUILD.gn
index d3b5bf13240..33ab9387a10 100644
--- a/chromium/chrome/browser/commerce/android/BUILD.gn
+++ b/chromium/chrome/browser/commerce/android/BUILD.gn
@@ -34,6 +34,7 @@ android_library("java") {
"//components/prefs/android:java",
"//components/signin/public/android:java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_preference_preference_java",
"//ui/android:ui_java",
@@ -63,6 +64,7 @@ android_library("javatests") {
":java",
"//base:base_java",
"//base:base_java_test_support",
+ "//chrome/android:chrome_java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
"//chrome/browser/profiles/android:java",
@@ -74,6 +76,8 @@ android_library("javatests") {
"//components/prefs/android:java",
"//components/signin/public/android:java",
"//content/public/android:content_full_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
+ "//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
diff --git a/chromium/chrome/browser/commerce/merchant_viewer/android/BUILD.gn b/chromium/chrome/browser/commerce/merchant_viewer/android/BUILD.gn
index 10ee0c6927b..f3af901ea7e 100644
--- a/chromium/chrome/browser/commerce/merchant_viewer/android/BUILD.gn
+++ b/chromium/chrome/browser/commerce/merchant_viewer/android/BUILD.gn
@@ -97,6 +97,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android:base_module_java",
"//chrome/android/features/tab_ui:java_resources",
"//chrome/android/features/tab_ui:java_strings_grd",
@@ -161,6 +162,7 @@ android_library("javatests") {
"//chrome/browser/flags:java",
"//chrome/browser/optimization_guide/android:java",
"//chrome/browser/profiles/android:java",
+ "//chrome/browser/tab:java",
"//chrome/browser/tabmodel:java",
"//chrome/browser/ui/android/page_info:java",
"//chrome/test/android:chrome_java_integration_test_support",
diff --git a/chromium/chrome/browser/companion/core/mojom/companion.mojom b/chromium/chrome/browser/companion/core/mojom/companion.mojom
index 2eab135b44d..20f5e258e9f 100644
--- a/chromium/chrome/browser/companion/core/mojom/companion.mojom
+++ b/chromium/chrome/browser/companion/core/mojom/companion.mojom
@@ -45,6 +45,9 @@ enum MethodType {
// Method corresponding to `CompanionPageHandler.UpdateLoadingState`.
kCompanionLoadingState = 11,
+ // Method corresponding to `CompanionPage.RefreshCompanionPage`.
+ kRefreshCompanionPage = 12,
+
// Methods used in browser -> renderer communication.
// Method corresponding to `CompanionPage.UpdateCompanionPage`.
kUpdateCompanionPage = 31,
@@ -74,6 +77,9 @@ enum PromoType {
// IPH about region search capability.
kRegionSearchIPH = 4,
+
+ // Promo to enable page content opt-in.
+ kPco = 5,
};
// Loading states currently in the Companion UI.
@@ -182,9 +188,13 @@ enum UiSurface {
kPHResult = 10
};
+// Container for sending visual suggestion results to the side panel renderer.
struct VisualSearchResult {
- // Encoded image bytes
+ // Base64 encoded image bytes.
string data_uri;
+
+ // Alt-text for the image specified by the data uri.
+ string alt_text;
};
// Factory method for creating a new WebUI page handler.
@@ -253,6 +263,9 @@ interface CompanionPageHandler {
// Called to update the browser with the appropriate companion loading state.
OnLoadingState(LoadingState state);
+
+ // Does a hard refresh of the companion page with new state.
+ RefreshCompanionPage();
};
// WebUI page handler for request from Browser side. (C++ -> TypeScript)
diff --git a/chromium/chrome/browser/content_creation/notes/internal/android/BUILD.gn b/chromium/chrome/browser/content_creation/notes/internal/android/BUILD.gn
index 9dca0b88173..320070753b8 100644
--- a/chromium/chrome/browser/content_creation/notes/internal/android/BUILD.gn
+++ b/chromium/chrome/browser/content_creation/notes/internal/android/BUILD.gn
@@ -47,6 +47,7 @@ android_library("java") {
"//components/browser_ui/styles/android:java_resources",
"//components/browser_ui/widget/android:java",
"//components/content_creation/notes/android:java",
+ "//components/embedder_support/android:simple_factory_key_java",
"//components/image_fetcher:java",
"//components/url_formatter/android:url_formatter_java",
"//content/public/android:content_java_resources",
diff --git a/chromium/chrome/browser/creator/android/BUILD.gn b/chromium/chrome/browser/creator/android/BUILD.gn
index fd7ed5f6895..efb7adf1283 100644
--- a/chromium/chrome/browser/creator/android/BUILD.gn
+++ b/chromium/chrome/browser/creator/android/BUILD.gn
@@ -36,6 +36,7 @@ android_library("java") {
"//chrome/browser/xsurface:java",
"//components/browser_ui/bottomsheet/android:factory_java",
"//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/browser_ui/styles/android:java",
"//components/browser_ui/widget/android:java",
"//components/embedder_support/android:content_view_java",
@@ -53,6 +54,7 @@ android_library("java") {
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+ "//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//ui/android:ui_no_recycler_view_java",
"//ui/android:ui_utils_java",
@@ -96,6 +98,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/feed/android:feed_java_resources",
"//chrome/browser/feed/android:java",
"//chrome/browser/feed/android:junit",
@@ -118,7 +121,6 @@ robolectric_library("junit") {
"//ui/android:ui_java_test_support",
"//ui/android:ui_no_recycler_view_java",
"//url:gurl_java",
- "//url:gurl_junit_shadows",
"//url:gurl_junit_test_support",
]
resources_package = "org.chromium.chrome.browser.creator.test"
diff --git a/chromium/chrome/browser/devtools/BUILD.gn b/chromium/chrome/browser/devtools/BUILD.gn
index 7843b466108..35e190313b0 100644
--- a/chromium/chrome/browser/devtools/BUILD.gn
+++ b/chromium/chrome/browser/devtools/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/config/chromeos/ui_mode.gni")
+import("//chrome/browser/devtools/buildflags.gni")
import("//extensions/buildflags/buildflags.gni")
import("//printing/buildflags/buildflags.gni")
@@ -96,6 +97,13 @@ static_library("devtools") {
"//build/config:precompiled_headers",
]
+ if (enable_aida) {
+ defines = [
+ "AIDA_ENDPOINT=\"$aida_endpoint\"",
+ "AIDA_SCOPE=\"$aida_scope\"",
+ ]
+ }
+
deps = [
"//base",
@@ -235,11 +243,16 @@ static_library("devtools") {
"device/cast_device_provider.h",
]
}
+ if (enable_aida) {
+ sources += [
+ "aida_client.cc",
+ "aida_client.h",
+ ]
+ }
}
if (is_mac) {
sources += [ "devtools_dock_tile_mac.mm" ]
- configs += [ "//build/config/compiler:enable_arc" ]
} else {
sources += [ "devtools_dock_tile.cc" ]
}
diff --git a/chromium/chrome/browser/devtools/aida_client.cc b/chromium/chrome/browser/devtools/aida_client.cc
new file mode 100644
index 00000000000..878e3eb5d1d
--- /dev/null
+++ b/chromium/chrome/browser/devtools/aida_client.cc
@@ -0,0 +1,168 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/devtools/aida_client.h"
+#include <string>
+#include "base/json/json_string_value_serializer.h"
+#include "base/json/string_escape.h"
+#include "base/strings/string_util.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/url_loader.mojom.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+
+#if !defined(AIDA_ENDPOINT)
+#define AIDA_ENDPOINT ""
+#endif
+
+#if !defined(AIDA_SCOPE)
+#define AIDA_SCOPE ""
+#endif
+
+AidaClient::AidaClient(
+ Profile* profile,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : profile_(*profile),
+ url_loader_factory_(url_loader_factory),
+ aida_endpoint_(AIDA_ENDPOINT),
+ aida_scope_(AIDA_SCOPE) {}
+
+AidaClient::~AidaClient() = default;
+
+void AidaClient::OverrideAidaEndpointAndScopeForTesting(
+ const std::string& aida_endpoint,
+ const std::string& aida_scope) {
+ aida_endpoint_ = aida_endpoint;
+ aida_scope_ = aida_scope;
+}
+
+void AidaClient::DoConversation(
+ std::string request,
+ base::OnceCallback<void(const std::string&)> callback) {
+ if (aida_scope_.empty()) {
+ std::move(callback).Run(R"([{"error": "AIDA scope is not configured"}])");
+ return;
+ }
+ if (!access_token_.empty()) {
+ SendAidaRequest(std::move(request), std::move(callback));
+ return;
+ }
+ auto* identity_manager = IdentityManagerFactory::GetForProfile(&*profile_);
+ CoreAccountId account_id =
+ identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync);
+ access_token_fetcher_ = identity_manager->CreateAccessTokenFetcherForAccount(
+ account_id, "AIDA client", signin::ScopeSet{aida_scope_},
+ base::BindOnce(&AidaClient::AccessTokenFetchFinished,
+ base::Unretained(this), std::move(request),
+ std::move(callback)),
+ signin::AccessTokenFetcher::Mode::kImmediate);
+}
+
+void AidaClient::AccessTokenFetchFinished(
+ std::string request,
+ base::OnceCallback<void(const std::string&)> callback,
+ GoogleServiceAuthError error,
+ signin::AccessTokenInfo access_token_info) {
+ if (error.state() != GoogleServiceAuthError::NONE) {
+ std::move(callback).Run(base::ReplaceStringPlaceholders(
+ R"([{"error": "Cannot get OAuth credentials", "detail": $1}])",
+ {base::GetQuotedJSONString(error.ToString())}, nullptr));
+ return;
+ }
+
+ access_token_ = access_token_info.token;
+ SendAidaRequest(std::move(request), std::move(callback));
+}
+
+void AidaClient::SendAidaRequest(
+ std::string request,
+ base::OnceCallback<void(const std::string&)> callback) {
+ CHECK(!access_token_.empty());
+
+ if (aida_endpoint_.empty()) {
+ std::move(callback).Run(
+ R"([{"error": "AIDA endpoint is not configured"}])");
+ return;
+ }
+
+ auto aida_request = std::make_unique<network::ResourceRequest>();
+ aida_request->url = GURL(aida_endpoint_);
+ aida_request->load_flags = net::LOAD_DISABLE_CACHE;
+ aida_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+ aida_request->method = "POST";
+ aida_request->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
+ std::string("Bearer ") + access_token_);
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("devtools_cdp_network_resource", R"(
+ semantics {
+ sender: "Developer Tools via CDP"
+ description:
+ "When user opens Developer Tools, the browser may fetch additional "
+ "resources from the network to enrich the debugging experience "
+ "(e.g. source map resources)."
+ trigger: "User opens Developer Tools to debug a web page."
+ data: "Any resources requested by Developer Tools."
+ destination: WEBSITE
+ }
+ policy {
+ cookies_allowed: YES
+ cookies_store: "user"
+ setting:
+ "It's not possible to disable this feature from settings."
+ chrome_policy {
+ DeveloperToolsAvailability {
+ policy_options {mode: MANDATORY}
+ DeveloperToolsAvailability: 2
+ }
+ }
+ })");
+ auto simple_url_loader = network::SimpleURLLoader::Create(
+ std::move(aida_request), traffic_annotation);
+ simple_url_loader->SetAllowHttpErrorResults(true);
+ simple_url_loader->AttachStringForUpload(request);
+
+ network::SimpleURLLoader* simple_url_loader_ptr = simple_url_loader.get();
+ simple_url_loader_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&AidaClient::OnSimpleLoaderComplete,
+ base::Unretained(this), std::move(request),
+ std::move(callback), std::move(simple_url_loader)));
+}
+
+void AidaClient::OnSimpleLoaderComplete(
+ std::string request,
+ base::OnceCallback<void(const std::string&)> callback,
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader,
+ std::unique_ptr<std::string> response_body) {
+ int response_code = -1;
+ if (simple_url_loader->ResponseInfo() &&
+ simple_url_loader->ResponseInfo()->headers) {
+ response_code = simple_url_loader->ResponseInfo()->headers->response_code();
+ }
+ if (!response_body) {
+ std::move(callback).Run(R"([{"error": "Got empty response from AIDA"}])");
+ return;
+ }
+ if (response_code == net::HTTP_UNAUTHORIZED) {
+ auto* identity_manager = IdentityManagerFactory::GetForProfile(&*profile_);
+ CoreAccountId account_id =
+ identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync);
+ identity_manager->RemoveAccessTokenFromCache(
+ account_id, signin::ScopeSet{aida_scope_}, access_token_);
+ access_token_.clear();
+ DoConversation(request, std::move(callback));
+ return;
+ }
+ if (response_code != net::HTTP_OK) {
+ std::move(callback).Run(base::ReplaceStringPlaceholders(
+ R"([{"error": "Got error response from AIDA", "detail": $1}])",
+ {std::move(*response_body)}, nullptr));
+ return;
+ }
+ std::move(callback).Run(std::move(*response_body));
+}
diff --git a/chromium/chrome/browser/devtools/aida_client.h b/chromium/chrome/browser/devtools/aida_client.h
new file mode 100644
index 00000000000..30100c0bb61
--- /dev/null
+++ b/chromium/chrome/browser/devtools/aida_client.h
@@ -0,0 +1,58 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_AIDA_CLIENT_H_
+#define CHROME_BROWSER_DEVTOOLS_AIDA_CLIENT_H_
+
+#include <string>
+#include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/signin/public/identity_manager/access_token_fetcher.h"
+#include "components/signin/public/identity_manager/access_token_info.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+// class Profile;
+namespace network {
+class SharedURLLoaderFactory;
+}
+
+class AidaClient {
+ public:
+ AidaClient(Profile* profile,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+ ~AidaClient();
+
+ void DoConversation(std::string request,
+ base::OnceCallback<void(const std::string&)> callback);
+
+ void OverrideAidaEndpointAndScopeForTesting(const std::string& aida_endpoint,
+ const std::string& aida_scope);
+
+ private:
+ void SendAidaRequest(std::string request,
+ base::OnceCallback<void(const std::string&)> callback);
+ void AccessTokenFetchFinished(
+ std::string request,
+ base::OnceCallback<void(const std::string&)> callback,
+ GoogleServiceAuthError error,
+ signin::AccessTokenInfo access_token_info);
+ void OnSimpleLoaderComplete(
+ std::string request,
+ base::OnceCallback<void(const std::string&)> callback,
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader,
+ std::unique_ptr<std::string> response_body);
+
+ const raw_ref<Profile> profile_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+ std::unique_ptr<signin::AccessTokenFetcher> access_token_fetcher_;
+ std::string aida_endpoint_;
+ std::string aida_scope_;
+ std::string access_token_;
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_AIDA_CLIENT_H_
diff --git a/chromium/chrome/browser/devtools/aida_client_unittest.cc b/chromium/chrome/browser/devtools/aida_client_unittest.cc
new file mode 100644
index 00000000000..ea020c59db0
--- /dev/null
+++ b/chromium/chrome/browser/devtools/aida_client_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/devtools/aida_client.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/functional/bind.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/network/network_service.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+using net::test_server::BasicHttpResponse;
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+} // namespace
+
+const char kEmail[] = "alice@example.com";
+const char kEndpointPath[] = "/foo";
+const char kScope[] = "bar";
+const char kRequest[] =
+ R"({"input": "What does this code do: 1+1", "client": "GENERAL"})";
+const char kResponse[] =
+ R"([{"textChunk":{"text":"The function `foo()` takes no arguments and returns nothing."}}])";
+
+class AidaClientTest : public testing::Test {
+ public:
+ AidaClientTest()
+ : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
+ profile_(IdentityTestEnvironmentProfileAdaptor::
+ CreateProfileForIdentityTestEnvironment()),
+ identity_test_env_adaptor_(
+ std::make_unique<IdentityTestEnvironmentProfileAdaptor>(
+ profile_.get())),
+ identity_test_env_(identity_test_env_adaptor_->identity_test_env()) {
+ content::GetNetworkService();
+ content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
+
+ shared_url_loader_factory_ =
+ base::MakeRefCounted<network::TestSharedURLLoaderFactory>(
+ network::NetworkService::GetNetworkServiceForTesting());
+
+ identity_test_env_->MakePrimaryAccountAvailable(
+ kEmail, signin::ConsentLevel::kSync);
+ }
+
+ protected:
+ content::BrowserTaskEnvironment task_environment_;
+ std::unique_ptr<network::mojom::NetworkContextClient> network_context_client_;
+ scoped_refptr<network::TestSharedURLLoaderFactory> shared_url_loader_factory_;
+ net::EmbeddedTestServer test_server_;
+ std::unique_ptr<TestingProfile> profile_;
+ std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+ identity_test_env_adaptor_;
+ signin::IdentityTestEnvironment* identity_test_env_;
+};
+
+class Delegate {
+ public:
+ Delegate() = default;
+
+ std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
+ request_ = request.content;
+ authorization_header_ =
+ request.headers.at(net::HttpRequestHeaders::kAuthorization);
+
+ auto http_response = std::make_unique<BasicHttpResponse>();
+ http_response->set_code(api_response_code_);
+ http_response->set_content(api_response_);
+ http_response->set_content_type("application/json");
+ return http_response;
+ }
+
+ void FinishCallback(base::RunLoop* run_loop, const std::string& response) {
+ response_ = response;
+ if (run_loop) {
+ run_loop->Quit();
+ }
+ }
+
+ std::string request_;
+ std::string api_response_ = kResponse;
+ net::HttpStatusCode api_response_code_ = net::HTTP_OK;
+ std::string response_;
+ std::string authorization_header_;
+};
+
+constexpr char kOAuthToken[] = "5678";
+
+TEST_F(AidaClientTest, DoesNothingIfNoScope) {
+ Delegate delegate;
+ test_server_.RegisterRequestHandler(base::BindRepeating(
+ &Delegate::HandleRequest, base::Unretained(&delegate)));
+
+ AidaClient aida_client(profile_.get(), shared_url_loader_factory_);
+ aida_client.OverrideAidaEndpointAndScopeForTesting("", "");
+ aida_client.DoConversation(
+ kRequest, base::BindOnce(&Delegate::FinishCallback,
+ base::Unretained(&delegate), nullptr));
+ EXPECT_EQ("", delegate.request_);
+ EXPECT_EQ(R"([{"error": "AIDA scope is not configured"}])",
+ delegate.response_);
+}
+
+TEST_F(AidaClientTest, FailsIfNotAuthorized) {
+ base::RunLoop run_loop;
+ Delegate delegate;
+
+ AidaClient aida_client(profile_.get(), shared_url_loader_factory_);
+ aida_client.OverrideAidaEndpointAndScopeForTesting("https://example.com/foo",
+ kScope);
+ aida_client.DoConversation(
+ kRequest, base::BindOnce(&Delegate::FinishCallback,
+ base::Unretained(&delegate), &run_loop));
+ identity_test_env_->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+ GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
+
+ EXPECT_EQ("", delegate.request_);
+ EXPECT_EQ(
+ R"([{"error": "Cannot get OAuth credentials", "detail": "Request canceled."}])",
+ delegate.response_);
+}
+
+TEST_F(AidaClientTest, Succeeds) {
+ base::RunLoop run_loop;
+ Delegate delegate;
+ test_server_.RegisterRequestHandler(base::BindRepeating(
+ &Delegate::HandleRequest, base::Unretained(&delegate)));
+
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL endpoint_url = test_server_.GetURL(kEndpointPath);
+ AidaClient aida_client(profile_.get(), shared_url_loader_factory_);
+ aida_client.OverrideAidaEndpointAndScopeForTesting(endpoint_url.spec(),
+ kScope);
+ aida_client.DoConversation(
+ kRequest, base::BindOnce(&Delegate::FinishCallback,
+ base::Unretained(&delegate), &run_loop));
+ identity_test_env_
+ ->WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes(
+ kOAuthToken, base::Time::Now() + base::Seconds(10),
+ std::string() /*id_token*/, signin::ScopeSet{kScope});
+ run_loop.Run();
+
+ EXPECT_EQ(kRequest, delegate.request_);
+ EXPECT_EQ(kResponse, delegate.response_);
+}
+
+TEST_F(AidaClientTest, ReusesOAuthToken) {
+ base::RunLoop run_loop;
+ Delegate delegate;
+ test_server_.RegisterRequestHandler(base::BindRepeating(
+ &Delegate::HandleRequest, base::Unretained(&delegate)));
+
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL endpoint_url = test_server_.GetURL(kEndpointPath);
+ AidaClient aida_client(profile_.get(), shared_url_loader_factory_);
+ aida_client.OverrideAidaEndpointAndScopeForTesting(endpoint_url.spec(),
+ kScope);
+ aida_client.DoConversation(
+ kRequest, base::BindOnce(&Delegate::FinishCallback,
+ base::Unretained(&delegate), &run_loop));
+ identity_test_env_
+ ->WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes(
+ kOAuthToken, base::Time::Now() + base::Seconds(10),
+ std::string() /*id_token*/, signin::ScopeSet{kScope});
+ run_loop.Run();
+
+ EXPECT_EQ(kRequest, delegate.request_);
+ EXPECT_EQ(kResponse, delegate.response_);
+ std::string authorization_header = delegate.authorization_header_;
+
+ const char kAnotherRequest[] = "another request";
+ const char kAnotherResponse[] = "another response";
+ delegate.api_response_ = kAnotherResponse;
+ base::RunLoop run_loop2;
+ aida_client.DoConversation(
+ kAnotherRequest, base::BindOnce(&Delegate::FinishCallback,
+ base::Unretained(&delegate), &run_loop2));
+ run_loop2.Run();
+ EXPECT_EQ(kAnotherRequest, delegate.request_);
+ EXPECT_EQ(kAnotherResponse, delegate.response_);
+ EXPECT_EQ(authorization_header, delegate.authorization_header_);
+}
+
+TEST_F(AidaClientTest, RefetchesTokenIfUnauthorized) {
+ base::RunLoop run_loop;
+ Delegate delegate;
+ test_server_.RegisterRequestHandler(base::BindRepeating(
+ &Delegate::HandleRequest, base::Unretained(&delegate)));
+
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL endpoint_url = test_server_.GetURL(kEndpointPath);
+ AidaClient aida_client(profile_.get(), shared_url_loader_factory_);
+ aida_client.OverrideAidaEndpointAndScopeForTesting(endpoint_url.spec(),
+ kScope);
+ aida_client.DoConversation(
+ kRequest, base::BindOnce(&Delegate::FinishCallback,
+ base::Unretained(&delegate), &run_loop));
+ identity_test_env_
+ ->WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes(
+ kOAuthToken, base::Time::Now() + base::Seconds(10),
+ std::string() /*id_token*/, signin::ScopeSet{kScope});
+ run_loop.Run();
+
+ EXPECT_EQ(kRequest, delegate.request_);
+ EXPECT_EQ(kResponse, delegate.response_);
+ std::string authorization_header = delegate.authorization_header_;
+
+ delegate.api_response_code_ = net::HTTP_UNAUTHORIZED;
+ base::RunLoop run_loop2;
+ const char kAnotherRequest[] = "another request";
+ const char kAnotherResponse[] = "another response";
+ const char kAnotherOAuthToken[] = "another token";
+
+ aida_client.DoConversation(
+ kAnotherRequest, base::BindOnce(&Delegate::FinishCallback,
+ base::Unretained(&delegate), &run_loop2));
+ identity_test_env_
+ ->WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes(
+ kAnotherOAuthToken, base::Time::Now() + base::Seconds(10),
+ std::string() /*id_token*/, signin::ScopeSet{kScope});
+ delegate.api_response_code_ = net::HTTP_OK;
+ delegate.api_response_ = kAnotherResponse;
+
+ run_loop2.Run();
+ EXPECT_EQ(kAnotherRequest, delegate.request_);
+ EXPECT_EQ(kAnotherResponse, delegate.response_);
+ EXPECT_NE(authorization_header, delegate.authorization_header_);
+}
diff --git a/chromium/chrome/browser/devtools/buildflags.gni b/chromium/chrome/browser/devtools/buildflags.gni
new file mode 100644
index 00000000000..ac3226f51bc
--- /dev/null
+++ b/chromium/chrome/browser/devtools/buildflags.gni
@@ -0,0 +1,5 @@
+declare_args() {
+ enable_aida = false
+ aida_endpoint = ""
+ aida_scope = ""
+}
diff --git a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index d021f6ed2d4..b0804f8d117 100644
--- a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -303,17 +303,18 @@ void ChromeDevToolsManagerDelegate::ClientDetached(
scoped_refptr<DevToolsAgentHost> ChromeDevToolsManagerDelegate::CreateNewTarget(
const GURL& url,
- bool for_tab) {
+ DevToolsManagerDelegate::TargetType target_type) {
NavigateParams params(ProfileManager::GetLastUsedProfile(), url,
ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(&params);
if (!params.navigated_or_inserted_contents)
return nullptr;
- return for_tab ? DevToolsAgentHost::GetOrCreateForTab(
- params.navigated_or_inserted_contents)
- : DevToolsAgentHost::GetOrCreateFor(
- params.navigated_or_inserted_contents);
+ return target_type == DevToolsManagerDelegate::kTab
+ ? DevToolsAgentHost::GetOrCreateForTab(
+ params.navigated_or_inserted_contents)
+ : DevToolsAgentHost::GetOrCreateFor(
+ params.navigated_or_inserted_contents);
}
std::vector<content::BrowserContext*>
diff --git a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h
index 5a1519d7d9d..2f41d047120 100644
--- a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h
+++ b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h
@@ -91,7 +91,7 @@ class ChromeDevToolsManagerDelegate : public content::DevToolsManagerDelegate {
content::DevToolsAgentHostClientChannel* channel) override;
scoped_refptr<content::DevToolsAgentHost> CreateNewTarget(
const GURL& url,
- bool for_tab) override;
+ TargetType target_type) override;
bool HasBundledFrontendResources() override;
void DevicesAvailable(
diff --git a/chromium/chrome/browser/devtools/chrome_devtools_session.cc b/chromium/chrome/browser/devtools/chrome_devtools_session.cc
index 68a5ce0ab14..9c2d52bb49a 100644
--- a/chromium/chrome/browser/devtools/chrome_devtools_session.cc
+++ b/chromium/chrome/browser/devtools/chrome_devtools_session.cc
@@ -66,6 +66,10 @@ ChromeDevToolsSession::ChromeDevToolsSession(
storage_handler_ = std::make_unique<StorageHandler>(
agent_host->GetWebContents(), &dispatcher_);
}
+ }
+ if (agent_host->GetWebContents() &&
+ (agent_host->GetType() == content::DevToolsAgentHost::kTypePage ||
+ agent_host->GetType() == content::DevToolsAgentHost::kTypeFrame)) {
if (IsDomainAvailableToUntrustedClient<AutofillHandler>() ||
channel->GetClient()->IsTrusted()) {
autofill_handler_ =
diff --git a/chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc b/chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc
index b1a8ca33618..73316ca9d80 100644
--- a/chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc
+++ b/chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc
@@ -566,14 +566,14 @@ void MockAndroidConnection::Receive(const std::string& data) {
if (socket_name_ == "chrome_devtools_remote") {
if (path == kJsonVersionPath)
SendHTTPResponse(kSampleChromeVersion);
- else if (path == kJsonListPath)
+ else if (base::StartsWith(path, kJsonListPath))
SendHTTPResponse(kSampleChromePages);
else
NOTREACHED() << "Unknown command " << request;
} else if (socket_name_ == "chrome_devtools_remote_1002") {
if (path == kJsonVersionPath)
SendHTTPResponse(kSampleChromeBetaVersion);
- else if (path == kJsonListPath)
+ else if (base::StartsWith(path, kJsonListPath))
SendHTTPResponse(kSampleChromeBetaPages);
else
NOTREACHED() << "Unknown command " << request;
@@ -581,21 +581,21 @@ void MockAndroidConnection::Receive(const std::string& data) {
base::CompareCase::SENSITIVE)) {
if (path == kJsonVersionPath)
SendHTTPResponse("{}");
- else if (path == kJsonListPath)
+ else if (base::StartsWith(path, kJsonListPath))
SendHTTPResponse("[]");
else
NOTREACHED() << "Unknown command " << request;
} else if (socket_name_ == "webview_devtools_remote_2425") {
if (path == kJsonVersionPath)
SendHTTPResponse(kSampleWebViewVersion);
- else if (path == kJsonListPath)
+ else if (base::StartsWith(path, kJsonListPath))
SendHTTPResponse(kSampleWebViewPages);
else
NOTREACHED() << "Unknown command " << request;
} else if (socket_name_ == "node_devtools_remote") {
if (path == kJsonVersionPath)
SendHTTPResponse(kSampleNodeVersion);
- else if (path == kJsonListPath)
+ else if (base::StartsWith(path, kJsonListPath))
SendHTTPResponse(kSampleNodePage);
else
NOTREACHED() << "Unknown command " << request;
diff --git a/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc b/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc
index 69168072338..e1c7fc28aee 100644
--- a/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc
+++ b/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc
@@ -18,6 +18,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
+#include "chrome/browser/browser_features.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -33,7 +34,7 @@ using RemotePage = DevToolsDeviceDiscovery::RemotePage;
namespace {
-const char kPageListRequest[] = "/json";
+const char kPageListRequest[] = "/json/list";
const char kVersionRequest[] = "/json/version";
const char kClosePageRequest[] = "/json/close/%s";
const char kActivatePageRequest[] = "/json/activate/%s";
@@ -462,8 +463,12 @@ void DevToolsDeviceDiscovery::DiscoveryRequest::ReceivedVersion(
const std::string& response) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ std::string url = kPageListRequest;
+ if (base::FeatureList::IsEnabled(::features::kDevToolsTabTarget)) {
+ url += "?for_tab";
+ }
device->SendJsonRequest(
- browser->socket(), kPageListRequest,
+ browser->socket(), url,
base::BindOnce(&DiscoveryRequest::ReceivedPages, this, device, browser));
if (result < 0) {
diff --git a/chromium/chrome/browser/devtools/devtools_browsertest.cc b/chromium/chrome/browser/devtools/devtools_browsertest.cc
index a00bc525549..0e28803ef07 100644
--- a/chromium/chrome/browser/devtools/devtools_browsertest.cc
+++ b/chromium/chrome/browser/devtools/devtools_browsertest.cc
@@ -26,6 +26,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
+#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_restrictions.h"
@@ -1035,6 +1036,11 @@ IN_PROC_BROWSER_TEST_F(DevToolsTest, TestShowScriptsTab) {
RunTest("testShowScriptsTab", kDebuggerTestPage);
}
+// Tests recorder panel showing.
+IN_PROC_BROWSER_TEST_F(DevToolsTest, TestShowRecorderTab) {
+ RunTest("testShowRecorderTab", kDebuggerTestPage);
+}
+
// Tests that chrome.devtools extension is correctly exposed.
IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, TestDevToolsExtensionAPI) {
LoadExtension("devtools_extension");
@@ -1758,6 +1764,18 @@ IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
extension_id, "/simple_test_page.html"}));
}
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ CantInspectFileUrlWithoutFileAccess) {
+ LoadExtension("can_inspect_url");
+ std::string file_url =
+ net::FilePathToFileURL(
+ base::PathService::CheckedGet(base::DIR_SOURCE_ROOT)
+ .AppendASCII("content/test/data/devtools/navigation.html"))
+ .spec();
+ RunTest("waitForTestResultsAsMessage",
+ base::StrCat({kArbitraryPage, "#", file_url}));
+}
+
class DevToolsExtensionSidePanelTest
: public DevToolsExtensionTest,
public ::testing::WithParamInterface<bool> {
@@ -2132,7 +2150,7 @@ class BrowserAutofillManagerTestDelegateDevtoolsImpl
"console.log('didShowSuggestions');"));
}
- void OnTextFieldChanged() override {}
+ void DidHideSuggestions() override {}
private:
const raw_ptr<WebContents> inspected_contents_;
@@ -2154,12 +2172,11 @@ IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestDispatchKeyEventShowsAutoFill) {
autofill::ContentAutofillDriver* autofill_driver =
autofill::ContentAutofillDriverFactory::FromWebContents(GetInspectedTab())
->DriverForFrame(GetInspectedTab()->GetPrimaryMainFrame());
- auto* autofill_manager = static_cast<autofill::BrowserAutofillManager*>(
- autofill_driver->autofill_manager());
- ASSERT_TRUE(autofill_manager);
- BrowserAutofillManagerTestDelegateDevtoolsImpl autoFillTestDelegate(
+ auto& autofill_manager = static_cast<autofill::BrowserAutofillManager&>(
+ autofill_driver->GetAutofillManager());
+ BrowserAutofillManagerTestDelegateDevtoolsImpl autofill_test_delegate(
GetInspectedTab());
- autofill_manager->SetTestDelegate(&autoFillTestDelegate);
+ autofill_test_delegate.Observe(autofill_manager);
RunTestFunction(window_, "testDispatchKeyEventShowsAutoFill");
CloseDevToolsWindow();
@@ -2181,6 +2198,14 @@ IN_PROC_BROWSER_TEST_F(DevToolsTest, testForwardedKeysChanged) {
CloseDevToolsWindow();
}
+IN_PROC_BROWSER_TEST_F(DevToolsTest, testCloseActionRecorded) {
+ base::UserActionTester user_action_tester;
+ OpenDevToolsWindow("about:blank", true);
+ CloseDevToolsWindow();
+
+ EXPECT_EQ(1, user_action_tester.GetActionCount("DevTools_Close"));
+}
+
// Test that showing a certificate in devtools does not crash the process.
// Disabled on windows as this opens a modal in its own thread, which leads to a
// test timeout.
@@ -2222,7 +2247,8 @@ IN_PROC_BROWSER_TEST_F(DevToolsTest, TestDevToolsExternalNavigation) {
}
// Tests that toolbox window is loaded when DevTools window is undocked.
-IN_PROC_BROWSER_TEST_F(DevToolsTest, TestToolboxLoadedUndocked) {
+// TODO(https://crbug.com/1478411) - Fix this failing browser test.
+IN_PROC_BROWSER_TEST_F(DevToolsTest, DISABLED_TestToolboxLoadedUndocked) {
OpenDevToolsWindow(kDebuggerTestPage, false);
ASSERT_TRUE(toolbox_web_contents());
DevToolsWindow* on_self =
@@ -2801,7 +2827,15 @@ IN_PROC_BROWSER_TEST_F(DevToolsTest,
DevToolsWindowTesting::CloseDevToolsWindowSync(window);
}
-IN_PROC_BROWSER_TEST_F(DevToolsTest, TestRawHeadersWithRedirectAndHSTS) {
+// TODO(crbug.com/1471349): The bug is flaky on chromeos (mostly dbg).
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
+#define MAYBE_TestRawHeadersWithRedirectAndHSTS \
+ DISABLED_TestRawHeadersWithRedirectAndHSTS
+#else
+#define MAYBE_TestRawHeadersWithRedirectAndHSTS \
+ TestRawHeadersWithRedirectAndHSTS
+#endif
+IN_PROC_BROWSER_TEST_F(DevToolsTest, MAYBE_TestRawHeadersWithRedirectAndHSTS) {
net::EmbeddedTestServer https_test_server(
net::EmbeddedTestServer::TYPE_HTTPS);
https_test_server.SetSSLConfig(
@@ -3506,7 +3540,30 @@ class DevToolsProcessPerSiteUpToMainFrameThresholdTest : public DevToolsTest {
};
IN_PROC_BROWSER_TEST_F(DevToolsProcessPerSiteUpToMainFrameThresholdTest,
- DontReuseProcess) {
+ DevToolsWasAttachedBefore) {
+ const GURL url = embedded_test_server()->GetURL("foo.test", "/hello.html");
+
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+
+ Browser* browser1 = CreateBrowser(browser()->profile());
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser1, url));
+
+ Browser* browser2 = CreateBrowser(browser()->profile());
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser2, url));
+
+ ASSERT_NE(browser1->tab_strip_model()
+ ->GetActiveWebContents()
+ ->GetPrimaryMainFrame()
+ ->GetProcess(),
+ browser2->tab_strip_model()
+ ->GetActiveWebContents()
+ ->GetPrimaryMainFrame()
+ ->GetProcess());
+}
+
+// TODO(crbug.com/1468206): The test is failing on multiple builders.
+IN_PROC_BROWSER_TEST_F(DevToolsProcessPerSiteUpToMainFrameThresholdTest,
+ DISABLED_DontReuseProcess) {
OpenDevToolsWindow(kDebuggerTestPage, false);
DevToolsWindow* window =
DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), true);
diff --git a/chromium/chrome/browser/devtools/devtools_dock_tile_mac.mm b/chromium/chrome/browser/devtools/devtools_dock_tile_mac.mm
index 9983e54244b..a36197bdfc2 100644
--- a/chromium/chrome/browser/devtools/devtools_dock_tile_mac.mm
+++ b/chromium/chrome/browser/devtools/devtools_dock_tile_mac.mm
@@ -8,10 +8,6 @@
#include "base/strings/sys_string_conversions.h"
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
// static
void DevToolsDockTile::Update(const std::string& label, gfx::Image image) {
NSDockTile* dockTile = NSApplication.sharedApplication.dockTile;
diff --git a/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc b/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
index 8329ed66d8a..dde6df52150 100644
--- a/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
+++ b/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
@@ -270,5 +270,9 @@ DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(
d->RegisterHandlerWithCallback("showSurvey", &Delegate::ShowSurvey, delegate);
d->RegisterHandlerWithCallback("canShowSurvey", &Delegate::CanShowSurvey,
delegate);
+#if defined(AIDA_SCOPE)
+ d->RegisterHandlerWithCallback("doAidaConversation",
+ &Delegate::DoAidaConversation, delegate);
+#endif
return d;
}
diff --git a/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.h b/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
index 5e7797ab1f2..48162a4c794 100644
--- a/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
+++ b/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
@@ -115,6 +115,10 @@ class DevToolsEmbedderMessageDispatcher {
const std::string& trigger) = 0;
virtual void CanShowSurvey(DispatchCallback callback,
const std::string& trigger) = 0;
+#if defined(AIDA_SCOPE)
+ virtual void DoAidaConversation(DispatchCallback callback,
+ const std::string& request) = 0;
+#endif
};
using DispatchCallback = Delegate::DispatchCallback;
diff --git a/chromium/chrome/browser/devtools/devtools_interactive_browsertest.cc b/chromium/chrome/browser/devtools/devtools_interactive_browsertest.cc
index 8faae4d1e31..90aa74fc47c 100644
--- a/chromium/chrome/browser/devtools/devtools_interactive_browsertest.cc
+++ b/chromium/chrome/browser/devtools/devtools_interactive_browsertest.cc
@@ -15,6 +15,7 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
#include "ui/display/types/display_constants.h"
@@ -22,59 +23,6 @@
#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
#endif
-// Encapsulates waiting for the browser window to change state. This is
-// needed for example on Chrome desktop linux, where window state change is done
-// asynchronously as an event received from a different process.
-class CheckWaiter {
- public:
- CheckWaiter(base::RepeatingCallback<bool()> callback, bool expected)
- : callback_(callback),
- expected_(expected),
- timeout_(base::TimeTicks::Now() + base::Seconds(1)) {}
-
- CheckWaiter(const CheckWaiter&) = delete;
- CheckWaiter& operator=(const CheckWaiter&) = delete;
-
- ~CheckWaiter() = default;
-
- // Blocks until the browser window becomes maximized.
- void Wait() {
- if (Check())
- return;
-
- base::RunLoop run_loop;
- quit_ = run_loop.QuitClosure();
- run_loop.Run();
- }
-
- private:
- bool Check() {
- if (callback_.Run() != expected_ && base::TimeTicks::Now() < timeout_) {
- // Check again after a short timeout. Important: Don't use an immediate
- // task to check again, because the pump would be allowed to run it
- // immediately without processing system events (system events are
- // required for the state to change).
- base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(base::IgnoreResult(&CheckWaiter::Check),
- base::Unretained(this)),
- TestTimeouts::tiny_timeout());
- return false;
- }
-
- // Quit the run_loop to end the wait.
- if (!quit_.is_null())
- std::move(quit_).Run();
- return true;
- }
-
- base::RepeatingCallback<bool()> callback_;
- bool expected_;
- const base::TimeTicks timeout_;
- // The waiter's RunLoop quit closure.
- base::RepeatingClosure quit_;
-};
-
class DevToolsManagerDelegateTest : public InProcessBrowserTest {
public:
void SendCommand(const std::string& state) {
@@ -97,25 +45,28 @@ class DevToolsManagerDelegateTest : public InProcessBrowserTest {
}
void CheckIsMaximized(bool maximized) {
- CheckWaiter(base::BindRepeating(&BrowserWindow::IsMaximized,
- base::Unretained(browser()->window())),
- maximized)
+ ui_test_utils::CheckWaiter(
+ base::BindRepeating(&BrowserWindow::IsMaximized,
+ base::Unretained(browser()->window())),
+ maximized, base::Seconds(1))
.Wait();
EXPECT_EQ(maximized, browser()->window()->IsMaximized());
}
void CheckIsMinimized(bool minimized) {
- CheckWaiter(base::BindRepeating(&BrowserWindow::IsMinimized,
- base::Unretained(browser()->window())),
- minimized)
+ ui_test_utils::CheckWaiter(
+ base::BindRepeating(&BrowserWindow::IsMinimized,
+ base::Unretained(browser()->window())),
+ minimized, base::Seconds(1))
.Wait();
EXPECT_EQ(minimized, browser()->window()->IsMinimized());
}
void CheckIsFullscreen(bool fullscreen) {
- CheckWaiter(base::BindRepeating(&BrowserWindow::IsFullscreen,
- base::Unretained(browser()->window())),
- fullscreen)
+ ui_test_utils::CheckWaiter(
+ base::BindRepeating(&BrowserWindow::IsFullscreen,
+ base::Unretained(browser()->window())),
+ fullscreen, base::Seconds(1))
.Wait();
EXPECT_EQ(fullscreen, browser()->window()->IsFullscreen());
}
@@ -125,10 +76,10 @@ class DevToolsManagerDelegateTest : public InProcessBrowserTest {
}
void CheckWindowBounds(gfx::Rect expected) {
- CheckWaiter(
+ ui_test_utils::CheckWaiter(
base::BindRepeating(&DevToolsManagerDelegateTest::IsWindowBoundsEqual,
base::Unretained(this), expected),
- true)
+ true, base::Seconds(1))
.Wait();
EXPECT_EQ(expected, browser()->window()->GetBounds());
}
diff --git a/chromium/chrome/browser/devtools/devtools_ui_bindings.cc b/chromium/chrome/browser/devtools/devtools_ui_bindings.cc
index e6ee7df5f53..3bd9c44cb01 100644
--- a/chromium/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chromium/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -86,12 +86,12 @@
#include "extensions/common/permissions/permissions_data.h"
#include "google_apis/google_api_keys.h"
#include "ipc/ipc_channel.h"
+#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/url_util.h"
#include "net/http/http_response_headers.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
@@ -361,6 +361,16 @@ std::string SanitizeFrontendQueryParam(
return value;
}
+ if (key == "noJavaScriptCompletion" && value == "true") {
+ return value;
+ }
+
+#if defined(AIDA_SCOPE)
+ if (key == "enableAida" && value == "true") {
+ return value;
+ }
+#endif
+
return std::string();
}
@@ -797,6 +807,17 @@ void DevToolsUIBindings::SetIsDocked(DispatchCallback callback,
std::move(callback).Run(nullptr);
}
+#if defined(AIDA_SCOPE)
+void DevToolsUIBindings::OnAidaConverstaionResponse(
+ DispatchCallback callback,
+ const std::string& response) {
+ base::Value::Dict response_dict;
+ response_dict.Set("response", response);
+ auto response_value = base::Value(std::move(response_dict));
+ std::move(callback).Run(&response_value);
+}
+#endif
+
void DevToolsUIBindings::InspectElementCompleted() {
delegate_->InspectElementCompleted();
}
@@ -1655,6 +1676,23 @@ void DevToolsUIBindings::CanShowSurvey(DispatchCallback callback,
std::move(callback).Run(&response);
}
+#if defined(AIDA_SCOPE)
+void DevToolsUIBindings::DoAidaConversation(DispatchCallback callback,
+ const std::string& request) {
+ if (!aida_client_) {
+ aida_client_ = std::make_unique<AidaClient>(
+ profile_, DevToolsWindow::AsDevToolsWindow(web_contents_)
+ ->GetInspectedWebContents()
+ ->GetPrimaryMainFrame()
+ ->GetStoragePartition()
+ ->GetURLLoaderFactoryForBrowserProcess());
+ }
+ aida_client_->DoConversation(
+ request, base::BindOnce(&DevToolsUIBindings::OnAidaConverstaionResponse,
+ base::Unretained(this), std::move(callback)));
+}
+#endif
+
void DevToolsUIBindings::SetDelegate(Delegate* delegate) {
delegate_.reset(delegate);
}
diff --git a/chromium/chrome/browser/devtools/devtools_ui_bindings.h b/chromium/chrome/browser/devtools/devtools_ui_bindings.h
index 83ef792064e..cd43d85a6b9 100644
--- a/chromium/chrome/browser/devtools/devtools_ui_bindings.h
+++ b/chromium/chrome/browser/devtools/devtools_ui_bindings.h
@@ -13,6 +13,9 @@
#include "base/containers/unique_ptr_adapters.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
+#if defined(AIDA_SCOPE)
+#include "chrome/browser/devtools/aida_client.h"
+#endif
#include "chrome/browser/devtools/device/devtools_android_bridge.h"
#include "chrome/browser/devtools/devtools_embedder_message_dispatcher.h"
#include "chrome/browser/devtools/devtools_file_helper.h"
@@ -21,8 +24,13 @@
#include "chrome/browser/devtools/devtools_settings.h"
#include "chrome/browser/devtools/devtools_targets_ui.h"
#include "components/prefs/pref_change_registrar.h"
+#include "components/signin/public/identity_manager/access_token_fetcher.h"
+#include "components/signin/public/identity_manager/access_token_info.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/devtools_frontend_host.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
#include "ui/gfx/geometry/size.h"
class DevToolsAndroidBridge;
@@ -200,6 +208,10 @@ class DevToolsUIBindings : public DevToolsEmbedderMessageDispatcher::Delegate,
const std::string& trigger) override;
void CanShowSurvey(DispatchCallback callback,
const std::string& trigger) override;
+#if defined(AIDA_SCOPE)
+ void DoAidaConversation(DispatchCallback callback,
+ const std::string& request) override;
+#endif
void EnableRemoteDeviceCounter(bool enable);
@@ -256,6 +268,10 @@ class DevToolsUIBindings : public DevToolsEmbedderMessageDispatcher::Delegate,
static DevToolsUIBindingsList& GetDevToolsUIBindings();
+#if defined(AIDA_SCOPE)
+ void OnAidaConverstaionResponse(DispatchCallback callback,
+ const std::string& response);
+#endif
class FrontendWebContentsObserver;
std::unique_ptr<FrontendWebContentsObserver> frontend_contents_observer_;
@@ -292,6 +308,9 @@ class DevToolsUIBindings : public DevToolsEmbedderMessageDispatcher::Delegate,
DevToolsSettings settings_;
+#if defined(AIDA_SCOPE)
+ std::unique_ptr<AidaClient> aida_client_;
+#endif
base::WeakPtrFactory<DevToolsUIBindings> weak_factory_{this};
};
diff --git a/chromium/chrome/browser/devtools/devtools_window.cc b/chromium/chrome/browser/devtools/devtools_window.cc
index 6688c261e42..eac3f57ec95 100644
--- a/chromium/chrome/browser/devtools/devtools_window.cc
+++ b/chromium/chrome/browser/devtools/devtools_window.cc
@@ -61,7 +61,6 @@
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/file_select_listener.h"
#include "content/public/browser/keyboard_event_processing_result.h"
-#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
@@ -72,7 +71,9 @@
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
+#include "content/public/common/input/native_web_keyboard_event.h"
#include "content/public/common/url_constants.h"
+#include "net/cert/x509_certificate.h"
#include "third_party/blink/public/common/input/web_gesture_event.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
@@ -450,6 +451,8 @@ DevToolsWindow::~DevToolsWindow() {
life_stage_ = kClosing;
+ base::RecordAction(base::UserMetricsAction("DevTools_Close"));
+
UpdateBrowserWindow();
UpdateBrowserToolbar();
@@ -626,7 +629,8 @@ void DevToolsWindow::OpenDevToolsWindow(
std::string type = agent_host->GetType();
bool is_worker = type == DevToolsAgentHost::kTypeServiceWorker ||
- type == DevToolsAgentHost::kTypeSharedWorker;
+ type == DevToolsAgentHost::kTypeSharedWorker ||
+ type == DevToolsAgentHost::kTypeSharedStorageWorklet;
if (!agent_host->GetFrontendURL().empty()) {
DevToolsWindow::OpenExternalFrontend(profile, agent_host->GetFrontendURL(),
@@ -707,13 +711,21 @@ void DevToolsWindow::OpenExternalFrontend(
/* browser_connection */ false);
} else {
bool is_worker = type == DevToolsAgentHost::kTypeServiceWorker ||
- type == DevToolsAgentHost::kTypeSharedWorker;
+ type == DevToolsAgentHost::kTypeSharedWorker ||
+ type == DevToolsAgentHost::kTypeSharedStorageWorklet;
FrontendType frontend_type =
is_worker ? kFrontendRemoteWorker : kFrontendRemote;
std::string effective_frontend_url =
use_bundled_frontend ? kFallbackFrontendURL
: DevToolsUI::GetProxyURL(frontend_url).spec();
+ if (type == "tab") {
+ if (effective_frontend_url.find("?") == std::string::npos) {
+ effective_frontend_url += "?targetType=tab";
+ } else {
+ effective_frontend_url += "&targetType=tab";
+ }
+ }
window =
Create(profile, nullptr, frontend_type, effective_frontend_url, false,
std::string(), std::string(), agent_host->IsAttached(),
@@ -1205,6 +1217,9 @@ GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
if (base::FeatureList::IsEnabled(::features::kDevToolsTabTarget)) {
url += "&targetType=tab";
}
+#if defined(AIDA_SCOPE)
+ url += "&enableAida=true";
+#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (channel >= version_info::Channel::DEV &&
!base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/chromium/chrome/browser/devtools/protocol/autofill_handler.cc b/chromium/chrome/browser/devtools/protocol/autofill_handler.cc
index ffa68649ccd..1bf5a860ab9 100644
--- a/chromium/chrome/browser/devtools/protocol/autofill_handler.cc
+++ b/chromium/chrome/browser/devtools/protocol/autofill_handler.cc
@@ -6,15 +6,19 @@
#include "base/memory/scoped_refptr.h"
#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/devtools/protocol/autofill.h"
#include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h"
#include "chrome/browser/ui/autofill/chrome_autofill_client.h"
#include "components/autofill/content/browser/content_autofill_driver.h"
+#include "components/autofill/core/browser/autofill_address_util.h"
+#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/browser_autofill_manager.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/manual_testing_import.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
#include "components/autofill/core/common/unique_ids.h"
@@ -22,6 +26,7 @@
#include "content/public/browser/render_frame_host.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+using autofill::AutofillField;
using autofill::AutofillTriggerSource;
using autofill::CreditCard;
using autofill::FieldGlobalId;
@@ -36,7 +41,7 @@ absl::optional<std::pair<FormData, FormFieldData>> FindFieldWithFormData(
autofill::ContentAutofillDriver* driver,
autofill::FieldGlobalId id) {
for (const auto& [key, form] :
- driver->autofill_manager()->form_structures()) {
+ driver->GetAutofillManager().form_structures()) {
for (const auto& field : form->fields()) {
if (field->global_id() == id) {
return std::make_pair(form->ToFormData(), FormFieldData(*field));
@@ -52,10 +57,21 @@ AutofillHandler::AutofillHandler(protocol::UberDispatcher* dispatcher,
const std::string& target_id)
: target_id_(target_id) {
protocol::Autofill::Dispatcher::wire(dispatcher, this);
+
+ if (base::FeatureList::IsEnabled(
+ autofill::features::kAutofillTestFormWithDevtools)) {
+ frontend_ =
+ std::make_unique<protocol::Autofill::Frontend>(dispatcher->channel());
+ }
}
AutofillHandler::~AutofillHandler() = default;
+void AutofillHandler::OnAutofillManagerDestroyed(
+ autofill::AutofillManager& manager) {
+ observation_.Reset();
+}
+
void AutofillHandler::Trigger(
int field_id,
Maybe<String> frame_id,
@@ -86,43 +102,54 @@ void AutofillHandler::FinishTrigger(
content::RenderFrameHost* outermost_primary_rfh =
host->GetWebContents()->GetOutermostWebContents()->GetPrimaryMainFrame();
+ content::RenderFrameHost* frame_rfh = nullptr;
- autofill::LocalFrameToken frame_token(
- outermost_primary_rfh->GetFrameToken().value());
- if (frame_id.isJust()) {
- bool found = false;
+ if (frame_id.has_value()) {
outermost_primary_rfh->ForEachRenderFrameHost(
- [&frame_token, &frame_id, &found](content::RenderFrameHost* rfh) {
- if (rfh->GetDevToolsFrameToken().ToString() == frame_id.fromJust()) {
- frame_token =
- autofill::LocalFrameToken(rfh->GetFrameToken().value());
- found = true;
+ [&frame_id, &frame_rfh](content::RenderFrameHost* rfh) {
+ if (rfh->GetDevToolsFrameToken().ToString() == frame_id.value()) {
+ frame_rfh = rfh;
}
});
-
- if (!found) {
+ if (!frame_rfh) {
std::move(callback)->sendFailure(
Response::ServerError("Frame not found"));
return;
}
+ } else {
+ frame_rfh = outermost_primary_rfh;
}
- autofill::ContentAutofillDriver* autofill_driver = GetAutofillDriver();
- if (!autofill_driver) {
- std::move(callback)->sendFailure(
- Response::ServerError("RenderFrameHost is being destroyed"));
- return;
- }
-
+ autofill::LocalFrameToken frame_token(frame_rfh->GetFrameToken().value());
autofill::FieldGlobalId global_field_id = {
frame_token, autofill::FieldRendererId(field_id)};
- const auto& field_data =
- FindFieldWithFormData(autofill_driver, global_field_id);
+ autofill::ContentAutofillDriver* autofill_driver = nullptr;
+ absl::optional<std::pair<FormData, FormFieldData>> field_data;
+ while (frame_rfh) {
+ autofill_driver =
+ autofill::ContentAutofillDriver::GetForRenderFrameHost(frame_rfh);
+
+ if (!autofill_driver) {
+ continue;
+ }
+
+ field_data = FindFieldWithFormData(autofill_driver, global_field_id);
+ if (field_data.has_value()) {
+ break;
+ }
+ frame_rfh = frame_rfh->GetParent();
+ }
if (!field_data.has_value()) {
std::move(callback)->sendFailure(
- Response::InvalidRequest("field not found."));
+ Response::InvalidRequest("Field not found"));
+ return;
+ }
+
+ if (!autofill_driver) {
+ std::move(callback)->sendFailure(
+ Response::ServerError("RenderFrameHost is being destroyed"));
return;
}
@@ -138,9 +165,10 @@ void AutofillHandler::FinishTrigger(
tmp_autofill_card.SetRawInfo(autofill::CREDIT_CARD_VERIFICATION_CODE,
base::UTF8ToUTF16(card->GetCvc()));
- autofill_driver->autofill_manager()->FillCreditCardForm(
+ autofill_driver->GetAutofillManager().FillCreditCardForm(
field_data->first, field_data->second, tmp_autofill_card,
- base::UTF8ToUTF16(card->GetCvc()), AutofillTriggerSource::kPopup);
+ base::UTF8ToUTF16(card->GetCvc()),
+ {.trigger_source = AutofillTriggerSource::kPopup});
std::move(callback)->sendSuccess();
}
@@ -179,12 +207,116 @@ void AutofillHandler::SetAddresses(
return;
}
- static_cast<autofill::BrowserAutofillManager*>(
- autofill_driver->autofill_manager())
- ->set_test_addresses(test_address_for_countries);
+ static_cast<autofill::BrowserAutofillManager&>(
+ autofill_driver->GetAutofillManager())
+ .set_test_addresses(test_address_for_countries);
std::move(callback)->sendSuccess();
}
+void AutofillHandler::OnFillOrPreviewDataModelForm(
+ autofill::AutofillManager& manager,
+ autofill::FormGlobalId form,
+ autofill::mojom::AutofillActionPersistence action_persistence,
+ base::span<const std::pair<const FormFieldData*, const AutofillField*>>
+ filled_fields,
+ absl::variant<const autofill::AutofillProfile*, const autofill::CreditCard*>
+ profile_or_credit_card) {
+ if (!base::FeatureList::IsEnabled(
+ autofill::features::kAutofillTestFormWithDevtools)) {
+ return;
+ }
+
+ // We only care about address forms that were filled.
+ if (action_persistence != autofill::mojom::AutofillActionPersistence::kFill ||
+ !absl::holds_alternative<const autofill::AutofillProfile*>(
+ profile_or_credit_card)) {
+ return;
+ }
+
+ const autofill::AutofillProfile* profile_used_to_fill_form =
+ absl::get<const autofill::AutofillProfile*>(profile_or_credit_card);
+
+ auto filled_fields_to_be_sent_to_devtools =
+ std::make_unique<protocol::Array<protocol::Autofill::FilledField>>();
+ filled_fields_to_be_sent_to_devtools->reserve(filled_fields.size());
+ for (const std::pair<const FormFieldData*, const AutofillField*>& field :
+ filled_fields) {
+ // Whether the field was classified from the autocomplete attribute or
+ // predictions. If no autocomplete attribute exists OR the actual ServerType
+ // differs from what it would have been with only autocomplete, autofill
+ // inferred the type.
+ bool autofill_inferred =
+ field.second->html_type() ==
+ autofill::mojom::HtmlFieldType::kUnspecified ||
+ field.second->html_type() ==
+ autofill::mojom::HtmlFieldType::kUnrecognized ||
+ (autofill::AutofillType(field.second->html_type(),
+ field.second->html_mode())
+ .GetStorableType() != field.second->Type().GetStorableType());
+ filled_fields_to_be_sent_to_devtools->push_back(
+ protocol::Autofill::FilledField::Create()
+ .SetId(base::UTF16ToASCII(field.second->id_attribute))
+ .SetName(base::UTF16ToASCII(field.second->name_attribute))
+ .SetValue(base::UTF16ToASCII(field.first->value))
+ .SetHtmlType(field.second->form_control_type)
+ .SetAutofillType(
+ std::string(FieldTypeToDeveloperRepresentationString(
+ field.second->Type().GetStorableType())))
+ .SetFillingStrategy(
+ autofill_inferred
+ ? protocol::Autofill::FillingStrategyEnum::AutofillInferred
+ : protocol::Autofill::FillingStrategyEnum::
+ AutocompleteAttribute)
+ .Build());
+ }
+
+ // Send profile information to devtools so that it can build the UI.
+ // We use the same format we see in the settings page.
+ std::vector<std::vector<autofill::AutofillAddressUIComponent>> components;
+ // Devtools is already in english, so we can default the local to en-US.
+ const std::string locale = "en-US";
+ autofill::GetAddressComponents(
+ base::UTF16ToASCII(profile_used_to_fill_form->GetInfo(
+ autofill::ServerFieldType::ADDRESS_HOME_COUNTRY, locale)),
+ locale,
+ /*include_literals=*/false, &components, nullptr);
+
+ // `profile_address_fields` is used to represent a profile as seen in the
+ // settings page. It consists of a 2D array where each inner array is used
+ // build a "profile line". The following `profile_address_fields` for
+ // instance:
+ // [[{name: "GIVE_NAME", value: "Jon"}, {name: "FAMILY_NAME", value: "Doe"}],
+ // [{name: "CITY", value: "Munich"}, {name: "ZIP", value: "81456"}]] should
+ // allow the receiver to render:
+ // Jon Doe
+ // Munich 81456
+ auto profile_address_fields =
+ std::make_unique<protocol::Array<protocol::Autofill::AddressFields>>();
+ for (const std::vector<autofill::AutofillAddressUIComponent>& line :
+ components) {
+ auto profile_values =
+ std::make_unique<protocol::Array<protocol::Autofill::AddressField>>();
+ profile_values->reserve(line.size());
+ for (const autofill::AutofillAddressUIComponent& component : line) {
+ profile_values->push_back(
+ protocol::Autofill::AddressField::Create()
+ .SetName(std::string(FieldTypeToStringPiece(component.field)))
+ .SetValue(base::UTF16ToASCII(
+ profile_used_to_fill_form->GetInfo(component.field, locale)))
+ .Build());
+ }
+ profile_address_fields->push_back(
+ protocol::Autofill::AddressFields::Create()
+ .SetFields(std::move(profile_values))
+ .Build());
+ }
+ frontend_->AddressFormFilled(
+ std::move(filled_fields_to_be_sent_to_devtools),
+ protocol::Autofill::AddressUI::Create()
+ .SetAddressFields(std::move(profile_address_fields))
+ .Build());
+}
+
autofill::ContentAutofillDriver* AutofillHandler::GetAutofillDriver() {
auto host = content::DevToolsAgentHost::GetForId(target_id_);
DCHECK(host);
@@ -195,3 +327,21 @@ autofill::ContentAutofillDriver* AutofillHandler::GetAutofillDriver() {
return autofill::ContentAutofillDriver::GetForRenderFrameHost(
outermost_primary_rfh);
}
+
+Response AutofillHandler::Enable() {
+ enabled_ = true;
+ if (base::FeatureList::IsEnabled(
+ autofill::features::kAutofillTestFormWithDevtools)) {
+ autofill::ContentAutofillDriver* autofill_driver = GetAutofillDriver();
+ if (autofill_driver) {
+ observation_.Observe(&autofill_driver->GetAutofillManager());
+ }
+ }
+ return Response::FallThrough();
+}
+
+Response AutofillHandler::Disable() {
+ enabled_ = false;
+ observation_.Reset();
+ return Response::FallThrough();
+}
diff --git a/chromium/chrome/browser/devtools/protocol/autofill_handler.h b/chromium/chrome/browser/devtools/protocol/autofill_handler.h
index 23b7deb31d7..fbac9345284 100644
--- a/chromium/chrome/browser/devtools/protocol/autofill_handler.h
+++ b/chromium/chrome/browser/devtools/protocol/autofill_handler.h
@@ -5,8 +5,11 @@
#ifndef CHROME_BROWSER_DEVTOOLS_PROTOCOL_AUTOFILL_HANDLER_H_
#define CHROME_BROWSER_DEVTOOLS_PROTOCOL_AUTOFILL_HANDLER_H_
+#include "base/scoped_observation.h"
#include "chrome/browser/devtools/protocol/autofill.h"
#include "chrome/browser/devtools/protocol/protocol.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/common/form_field_data.h"
#include "content/public/browser/web_contents.h"
using protocol::Maybe;
@@ -14,9 +17,13 @@ using protocol::String;
namespace autofill {
class ContentAutofillDriver;
+class AutofillProfile;
+class CreditCard;
+class AutofillField;
}
-class AutofillHandler : public protocol::Autofill::Backend {
+class AutofillHandler : public protocol::Autofill::Backend,
+ autofill::AutofillManager::Observer {
public:
AutofillHandler(protocol::UberDispatcher* dispatcher,
const std::string& target_id);
@@ -27,6 +34,8 @@ class AutofillHandler : public protocol::Autofill::Backend {
~AutofillHandler() override;
private:
+ protocol::Response Enable() override;
+ protocol::Response Disable() override;
void Trigger(int field_id,
Maybe<String> frame_id,
std::unique_ptr<protocol::Autofill::CreditCard> card,
@@ -35,15 +44,40 @@ class AutofillHandler : public protocol::Autofill::Backend {
std::unique_ptr<protocol::Autofill::CreditCard> card,
std::unique_ptr<TriggerCallback> callback,
uint64_t field_id);
+ // Sets a list of addresses inside `AutofillManager`, used to provide
+ // developers addresses from different countries so that they can be used for
+ // testing their form.
void SetAddresses(
std::unique_ptr<protocol::Array<protocol::Autofill::Address>> addresses,
std::unique_ptr<SetAddressesCallback> callback) override;
+ // Autofill::AutofillManagerObserver
+ // Observes form filled events. In the case of an address form, we emit to
+ // devtools the filled fields details and information about the profile used.
+ // These information is then used to build a UI inside devtools, which will
+ // provide developers more visibility on how autofill works on their form.
+ void OnFillOrPreviewDataModelForm(
+ autofill::AutofillManager& manager,
+ autofill::FormGlobalId form,
+ autofill::mojom::AutofillActionPersistence action_persistence,
+ base::span<const std::pair<const autofill::FormFieldData*,
+ const autofill::AutofillField*>> filled_fields,
+ absl::variant<const autofill::AutofillProfile*,
+ const autofill::CreditCard*> profile_or_credit_card)
+ override;
+ // Called when the manager is destroyed and used to reset the observer.
+ void OnAutofillManagerDestroyed(autofill::AutofillManager& manager) override;
+
// Returns the driver for the outermost frame, not the one that created the
// `DevToolsAgentHost` and iniated the session.
autofill::ContentAutofillDriver* GetAutofillDriver();
const std::string target_id_;
+ bool enabled_ = false;
+ std::unique_ptr<protocol::Autofill::Frontend> frontend_;
+ base::ScopedObservation<autofill::AutofillManager,
+ autofill::AutofillManager::Observer>
+ observation_{this};
base::WeakPtrFactory<AutofillHandler> weak_ptr_factory_{this};
};
diff --git a/chromium/chrome/browser/devtools/protocol/browser_handler.cc b/chromium/chrome/browser/devtools/protocol/browser_handler.cc
index 8cb4a98948d..4378da77e9c 100644
--- a/chromium/chrome/browser/devtools/protocol/browser_handler.cc
+++ b/chromium/chrome/browser/devtools/protocol/browser_handler.cc
@@ -81,7 +81,7 @@ Response BrowserHandler::GetWindowForTarget(
int* out_window_id,
std::unique_ptr<protocol::Browser::Bounds>* out_bounds) {
auto host =
- content::DevToolsAgentHost::GetForId(target_id.fromMaybe(target_id_));
+ content::DevToolsAgentHost::GetForId(target_id.value_or(target_id_));
if (!host)
return Response::ServerError("No target with given id");
content::WebContents* web_contents = host->GetWebContents();
@@ -185,9 +185,10 @@ protocol::Response BrowserHandler::SetDockTile(
protocol::Maybe<std::string> label,
protocol::Maybe<protocol::Binary> image) {
std::vector<gfx::ImagePNGRep> reps;
- if (image.isJust())
- reps.emplace_back(image.fromJust().bytes(), 1);
- DevToolsDockTile::Update(label.fromMaybe(std::string()),
+ if (image.has_value()) {
+ reps.emplace_back(image.value().bytes(), 1);
+ }
+ DevToolsDockTile::Update(label.value_or(std::string()),
!reps.empty() ? gfx::Image(reps) : gfx::Image());
return Response::Success();
}
diff --git a/chromium/chrome/browser/devtools/protocol/cast_handler.cc b/chromium/chrome/browser/devtools/protocol/cast_handler.cc
index 550fb4a882b..42be5dbdf0a 100644
--- a/chromium/chrome/browser/devtools/protocol/cast_handler.cc
+++ b/chromium/chrome/browser/devtools/protocol/cast_handler.cc
@@ -264,11 +264,11 @@ void CastHandler::StartObservingForSinks(
query_result_manager_->SetSourcesForCastMode(
media_router::MediaCastMode::TAB_MIRROR, {mirroring_source}, origin);
- if (presentation_url.isJust()) {
+ if (presentation_url.has_value()) {
url::Origin frame_origin =
web_contents_->GetPrimaryMainFrame()->GetLastCommittedOrigin();
std::vector<media_router::MediaSource> sources = {
- media_router::MediaSource(presentation_url.fromJust())};
+ media_router::MediaSource(presentation_url.value())};
query_result_manager_->SetSourcesForCastMode(
media_router::MediaCastMode::PRESENTATION, sources, frame_origin);
}
diff --git a/chromium/chrome/browser/devtools/protocol/devtools_autofill_browsertest.cc b/chromium/chrome/browser/devtools/protocol/devtools_autofill_browsertest.cc
index 706a76f818f..7daf458876d 100644
--- a/chromium/chrome/browser/devtools/protocol/devtools_autofill_browsertest.cc
+++ b/chromium/chrome/browser/devtools/protocol/devtools_autofill_browsertest.cc
@@ -2,22 +2,62 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/check_deref.h"
#include "base/strings/strcat.h"
#include "base/test/scoped_feature_list.h"
#include "build/chromeos_buildflags.h"
+#include "chrome/browser/autofill/autofill_uitest_util.h"
#include "chrome/browser/devtools/protocol/devtools_protocol_test_support.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/content/browser/test_autofill_manager_injector.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/browser_autofill_manager.h"
+#include "components/autofill/core/browser/browser_autofill_manager_test_api.h"
+#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/test_autofill_manager_waiter.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
+#include "net/dns/mock_host_resolver.h"
#include "testing/gmock/include/gmock/gmock.h"
+using testing::Eq;
+using testing::Not;
+using testing::ResultOf;
+
+namespace autofill {
+
namespace {
+// Asserts that a filled field sent to devtools has `attribute` set with
+// `expected_value`.
+auto FilledFieldHasAttributeWithValue(const std::string& attribute,
+ const std::string& expected_value) {
+ return ResultOf(
+ [&](const base::Value& filled_field) {
+ const std::string* value =
+ filled_field.GetDict().FindStringByDottedPath(attribute);
+ return value ? *value : "";
+ },
+ Eq(expected_value));
+}
+
+auto FilledFieldHasAttributeWithValue16(const std::string& attribute,
+ const std::u16string& expected_value) {
+ return FilledFieldHasAttributeWithValue(attribute,
+ base::UTF16ToASCII(expected_value));
+}
+
+std::string GetProfileInfoFromAddressField(const AutofillProfile profile,
+ const base::Value& address_field) {
+ return base::UTF16ToASCII(profile.GetRawInfo(TypeNameToFieldType(
+ *address_field.GetDict().FindStringByDottedPath("name"))));
+}
+
+} // namespace
+
// Adds waiting capabilities to BrowserAutofillManager.
class TestAutofillManager : public autofill::BrowserAutofillManager {
public:
@@ -25,11 +65,11 @@ class TestAutofillManager : public autofill::BrowserAutofillManager {
autofill::AutofillClient* client)
: BrowserAutofillManager(driver, client, "en-US") {}
- static TestAutofillManager* GetForRenderFrameHost(
+ static TestAutofillManager& GetForRenderFrameHost(
content::RenderFrameHost* rfh) {
- return static_cast<TestAutofillManager*>(
+ return static_cast<TestAutofillManager&>(
autofill::ContentAutofillDriver::GetForRenderFrameHost(rfh)
- ->autofill_manager());
+ ->GetAutofillManager());
}
[[nodiscard]] testing::AssertionResult WaitForFormsSeen(
@@ -45,32 +85,43 @@ class TestAutofillManager : public autofill::BrowserAutofillManager {
class DevToolsAutofillTest : public DevToolsProtocolTestBase {
public:
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ }
+
content::RenderFrameHost* main_frame() {
return web_contents()->GetPrimaryMainFrame();
}
- TestAutofillManager* main_autofill_manager() {
+ TestAutofillManager& main_autofill_manager() {
return TestAutofillManager::GetForRenderFrameHost(main_frame());
}
std::string EvaluateAndGetValue(const std::string& expression,
- const std::string& unique_context_id) {
+ const std::string& unique_context_id,
+ const std::string& session_id) {
base::Value::Dict params;
params.Set("expression", expression);
if (!unique_context_id.empty()) {
params.Set("uniqueContextId", unique_context_id);
}
- const base::Value::Dict* result =
- SendCommand("Runtime.evaluate", std::move(params));
+ const base::Value::Dict* result = SendSessionCommand(
+ "Runtime.evaluate", std::move(params), session_id, /*wait=*/true);
return *result->FindStringByDottedPath("result.value");
}
- int GetBackendNodeIdByIdAttribute(const std::string& expression) {
- return GetBackendNodeIdByIdAttribute(expression, "");
+ int GetBackendNodeIdByIdAttribute(const std::string& id_attribute) {
+ return GetBackendNodeIdByIdAttribute(id_attribute, "", "");
}
int GetBackendNodeIdByIdAttribute(const std::string& id_attribute,
const std::string& unique_context_id) {
+ return GetBackendNodeIdByIdAttribute(id_attribute, unique_context_id, "");
+ }
+
+ int GetBackendNodeIdByIdAttribute(const std::string& id_attribute,
+ const std::string& unique_context_id,
+ const std::string& session_id) {
std::string object_id;
{
base::Value::Dict params;
@@ -79,15 +130,15 @@ class DevToolsAutofillTest : public DevToolsProtocolTestBase {
if (!unique_context_id.empty()) {
params.Set("uniqueContextId", unique_context_id);
}
- const base::Value::Dict* result =
- SendCommand("Runtime.evaluate", std::move(params));
+ const base::Value::Dict* result = SendSessionCommand(
+ "Runtime.evaluate", std::move(params), session_id, /*wait=*/true);
object_id = *result->FindStringByDottedPath("result.objectId");
}
base::Value::Dict params;
params.Set("objectId", object_id);
- const base::Value::Dict* result =
- SendCommand("DOM.describeNode", std::move(params));
+ const base::Value::Dict* result = SendSessionCommand(
+ "DOM.describeNode", std::move(params), session_id, /*wait=*/true);
return *result->FindIntByDottedPath("node.backendNodeId");
}
@@ -101,31 +152,48 @@ class DevToolsAutofillTest : public DevToolsProtocolTestBase {
return card;
}
+ AutofillProfile CreateTestProfile() {
+ AutofillProfile profile = test::GetFullProfile();
+ AddTestProfile(browser()->profile(), profile);
+ return profile;
+ }
+
+ FormGlobalId form_id() const { return form_id_; }
+
base::Value::Dict GetFilledOutForm(const std::string& unique_context_id) {
+ return GetFilledOutForm(unique_context_id, "");
+ }
+
+ base::Value::Dict GetFilledOutForm(const std::string& unique_context_id,
+ const std::string& session_id) {
base::Value::Dict card;
card.Set("number",
EvaluateAndGetValue(
"document.getElementById('CREDIT_CARD_NUMBER').value",
- unique_context_id));
+ unique_context_id, session_id));
card.Set("name",
EvaluateAndGetValue(
"document.getElementById('CREDIT_CARD_NAME_FULL').value",
- unique_context_id));
+ unique_context_id, session_id));
card.Set("expiryMonth",
EvaluateAndGetValue(
"document.getElementById('CREDIT_CARD_EXP_MONTH').value",
- unique_context_id));
+ unique_context_id, session_id));
card.Set(
"expiryYear",
EvaluateAndGetValue(
"document.getElementById('CREDIT_CARD_EXP_4_DIGIT_YEAR').value",
- unique_context_id));
+ unique_context_id, session_id));
// CVC is not filled out in the form.
card.Set("cvc", "123");
return card;
}
private:
+ test::AutofillUnitTestEnvironment autofill_test_environment_;
+ base::test::ScopedFeatureList feature_list_{
+ features::kAutofillTestFormWithDevtools};
+ FormGlobalId form_id_ = test::MakeFormGlobalId();
autofill::TestAutofillManagerInjector<TestAutofillManager>
autofill_manager_injector_;
};
@@ -140,7 +208,7 @@ IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, SetAddresses) {
ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
Attach();
- EXPECT_TRUE(main_autofill_manager()->WaitForFormsSeen(1));
+ EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));
base::Value::Dict address_1_fields;
address_1_fields.Set("name", "ADDRESS_HOME_LINE1");
@@ -168,7 +236,7 @@ IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, SetAddresses) {
SendCommandSync("Autofill.setAddresses", std::move(params));
std::vector<autofill::AutofillProfile> res =
- main_autofill_manager()->test_addresses_for_test();
+ test_api(main_autofill_manager()).test_addresses();
ASSERT_EQ(res.size(), 2u);
ASSERT_EQ(res[0].GetAddress().GetRawInfo(
autofill::ServerFieldType::ADDRESS_HOME_LINE1),
@@ -188,7 +256,7 @@ IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerCreditCard) {
ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
Attach();
- EXPECT_TRUE(main_autofill_manager()->WaitForFormsSeen(1));
+ EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));
int backend_node_id = GetBackendNodeIdByIdAttribute("CREDIT_CARD_NUMBER");
@@ -211,7 +279,7 @@ IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerCreditCardInIframe) {
ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
Attach();
- EXPECT_TRUE(main_autofill_manager()->WaitForFormsSeen(1));
+ EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));
std::string frame_id;
{
@@ -262,4 +330,143 @@ IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerCreditCardInIframe) {
EXPECT_EQ(GetFilledOutForm(unique_context_id), GetTestCreditCard());
}
-} // namespace
+// Disabled due to nullptr deref; see https://crbug.com/1477600.
+IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest,
+ DISABLED_TriggerCreditCardInOOPIFIframe) {
+ embedded_test_server()->ServeFilesFromSourceDirectory(
+ "chrome/test/data/autofill");
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url = embedded_test_server()->GetURL(
+ "a.com", "/autofill_creditcard_form_in_oopif.html");
+
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+ ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
+ Attach();
+
+ EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));
+
+ const base::Value::Dict* result = SendCommandSync("Target.getTargets");
+
+ base::Value::Dict iframe_target;
+ for (const auto& target : *result->FindList("targetInfos")) {
+ if (*target.GetDict().FindString("type") == "iframe") {
+ iframe_target = target.Clone().TakeDict();
+ break;
+ }
+ }
+ std::string target_id = CHECK_DEREF(iframe_target.FindString("targetId"));
+
+ {
+ base::Value::Dict params;
+ params.Set("targetId", target_id);
+ params.Set("flatten", true);
+ result = SendCommandSync("Target.attachToTarget", std::move(params));
+ }
+ std::string session_id = CHECK_DEREF(result->FindString("sessionId"));
+
+ int backend_node_id =
+ GetBackendNodeIdByIdAttribute("CREDIT_CARD_NUMBER", "", session_id);
+
+ base::Value::Dict params;
+ params.Set("fieldId", backend_node_id);
+ params.Set("frameId", target_id);
+ params.Set("card", GetTestCreditCard());
+ result = SendSessionCommand("Autofill.trigger", std::move(params), session_id,
+ /*wait=*/true);
+
+ EXPECT_EQ(*result, base::Value::Dict());
+ EXPECT_EQ(GetFilledOutForm("", session_id), GetTestCreditCard());
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, AddressFormFilled) {
+ Attach();
+ // Create a profile to read information from and send to devtools.
+ AutofillProfile profile = CreateTestProfile();
+ // Create fake filled fields.
+ // First field. Please note that we only use `form_field_data` to grab the
+ // field value, everything else comes from `autofill_field`.
+ // TODO(crbug.com/1331312): Get rid of FormFieldData.
+ FormFieldData form_field_data =
+ test::CreateTestFormField(/*label*/ "", "name_1", "value_1", "text");
+ form_field_data.id_attribute = u"id_1";
+ AutofillField autofill_field(form_field_data);
+ // set `autofill_field_2` to empty to assert that we always use
+ // `form_field_data_2`.
+ autofill_field.value = u"";
+ autofill_field.set_server_predictions(
+ {test::CreateFieldPrediction(NAME_FULL)});
+ autofill_field.SetHtmlType(autofill::mojom::HtmlFieldType::kName,
+ autofill::mojom::HtmlFieldMode::kShipping);
+ // Second field.
+ FormFieldData form_field_data_2 =
+ test::CreateTestFormField(/*label*/ "", "name_2", "value_2", "text");
+ form_field_data_2.id_attribute = u"id_2";
+ AutofillField autofill_field_2(form_field_data_2);
+ // set `autofill_field_2` to empty to assert that we always use
+ // `form_field_data_2`.
+ autofill_field_2.value = u"";
+ autofill_field_2.set_server_predictions(
+ {test::CreateFieldPrediction(NAME_FULL)});
+ autofill_field_2.SetHtmlType(autofill::mojom::HtmlFieldType::kUnspecified,
+ autofill::mojom::HtmlFieldMode::kShipping);
+ std::vector<const std::pair<const FormFieldData*, const AutofillField*>>
+ filled_fields_by_autofill = {{{&form_field_data, &autofill_field},
+ {&form_field_data_2, &autofill_field_2}}};
+
+ // Enabled events and emit event about forming being filled.
+ SendCommandSync("Autofill.enable");
+ main_autofill_manager().NotifyObservers(
+ &autofill::AutofillManager::Observer::OnFillOrPreviewDataModelForm,
+ form_id(), autofill::mojom::AutofillActionPersistence::kFill,
+ filled_fields_by_autofill, &profile);
+
+ base::Value::Dict notification = WaitForNotification(
+ "Autofill.addressFormFilled", /*allow_existing=*/true);
+ for (const base::Value& address_line :
+ *notification.FindListByDottedPath("addressUi.addressFields")) {
+ for (const base::Value& address_field :
+ *address_line.GetDict().FindListByDottedPath("fields")) {
+ // Test that the profile address values sent to devtools match what we
+ // have in `profile`.
+ EXPECT_EQ(GetProfileInfoFromAddressField(profile, address_field),
+ *address_field.GetDict().FindStringByDottedPath("value"));
+ }
+ }
+
+ // Assert that the filled fields sent to devtools match exactly the ones
+ // filled by autofill.
+ const base::Value::List* filled_fields =
+ notification.FindListByDottedPath("filledFields");
+ ASSERT_EQ(filled_fields->size(), filled_fields_by_autofill.size());
+ for (size_t i = 0; i < filled_fields->size(); ++i) {
+ const base::Value& ff = (*filled_fields)[i];
+ const FormFieldData* ffd = filled_fields_by_autofill[i].first;
+ const AutofillField* af = filled_fields_by_autofill[i].second;
+
+ EXPECT_THAT(ff, FilledFieldHasAttributeWithValue16("id", af->id_attribute));
+ EXPECT_THAT(ff, FilledFieldHasAttributeWithValue(
+ "autofillType",
+ std::string(FieldTypeToDeveloperRepresentationString(
+ af->Type().GetStorableType()))));
+ // Note: we read the value from `FormFieldData`.
+ EXPECT_THAT(ff, FilledFieldHasAttributeWithValue16("value", ffd->value));
+ EXPECT_THAT(ff,
+ Not(FilledFieldHasAttributeWithValue16("value", af->value)));
+ EXPECT_THAT(ff, FilledFieldHasAttributeWithValue("htmlType",
+ af->form_control_type));
+ EXPECT_THAT(ff,
+ FilledFieldHasAttributeWithValue16("name", af->name_attribute));
+ }
+
+ // The first filled field uses autocomplete attribute as filling strategy.
+ EXPECT_EQ(*filled_fields->front().GetDict().FindStringByDottedPath(
+ "fillingStrategy"),
+ "autocompleteAttribute");
+ // The second one used autofill internals, either local heuristics or server
+ // predictions.
+ EXPECT_EQ(*filled_fields->back().GetDict().FindStringByDottedPath(
+ "fillingStrategy"),
+ "autofillInferred");
+}
+
+} // namespace autofill
diff --git a/chromium/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chromium/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 6191a4c4868..e8a656b64a1 100644
--- a/chromium/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/chromium/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -17,11 +17,14 @@
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "build/build_config.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/browser_app_launcher.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/data_saver/data_saver.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/devtools/protocol/devtools_protocol_test_support.h"
-#include "chrome/browser/dips/dips_features.h"
#include "chrome/browser/dips/dips_service.h"
#include "chrome/browser/dips/dips_storage.h"
#include "chrome/browser/extensions/extension_service.h"
@@ -33,6 +36,8 @@
#include "chrome/common/chrome_paths.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/ui_test_utils.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "components/content_settings/core/common/pref_names.h"
#include "components/custom_handlers/protocol_handler_registry.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/infobars/core/infobar.h"
@@ -42,9 +47,13 @@
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/preloading_test_util.h"
#include "content/public/test/prerender_test_util.h"
+#include "extensions/browser/app_window/app_window.h"
+#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/process_manager.h"
@@ -223,9 +232,24 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
EXPECT_EQ(nullptr, DevToolsWindow::FindDevToolsWindow(agent_host_.get()));
}
-IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, CheckReportedPreloadingState) {
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
+ PreloadEnabledStateUpdatedDefault) {
+ Attach();
+
+ SendCommandAsync("Preload.enable");
+ const base::Value::Dict result =
+ WaitForNotification("Preload.preloadEnabledStateUpdated", true);
+
+ EXPECT_THAT(result.FindBool("disabledByPreference"), false);
+ EXPECT_THAT(result.FindBool("disabledByHoldbackPrefetchSpeculationRules"),
+ false);
+ EXPECT_THAT(result.FindBool("disabledByHoldbackPrerenderSpeculationRules"),
+ false);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
+ PreloadEnabledStateUpdatedDisabledByPreference) {
Attach();
- SendCommandSync("Runtime.enable");
prefetch::SetPreloadPagesState(browser()->profile()->GetPrefs(),
prefetch::PreloadPagesState::kNoPreloading);
@@ -237,6 +261,104 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, CheckReportedPreloadingState) {
EXPECT_THAT(result.FindBool("disabledByPreference"), true);
}
+class DevToolsProtocolTest_PreloadEnabledStateUpdatedDisabledByHoldback
+ : public DevToolsProtocolTest {
+ protected:
+ void SetUp() override {
+ // This holds back speculation rules prefetch and prerender. Note that
+ // directly using enums (instead of strings) in the call to SetHoldback is
+ // usually preferred, but this is not possible here because
+ // content::content_preloading_predictor::kSpeculationRules, which is not
+ // exposed outside of content.
+ preloading_config_override_.SetHoldback("Prefetch", "SpeculationRules",
+ true);
+ preloading_config_override_.SetHoldback("Prerender", "SpeculationRules",
+ true);
+
+ DevToolsProtocolTest::SetUp();
+ }
+
+ private:
+ content::test::PreloadingConfigOverride preloading_config_override_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+ DevToolsProtocolTest_PreloadEnabledStateUpdatedDisabledByHoldback,
+ PreloadEnabledStateUpdatedDisabledByHoldback) {
+ Attach();
+
+ SendCommandAsync("Preload.enable");
+ const base::Value::Dict result =
+ WaitForNotification("Preload.preloadEnabledStateUpdated", true);
+
+ EXPECT_THAT(result.FindBool("disabledByHoldbackPrefetchSpeculationRules"),
+ true);
+ EXPECT_THAT(result.FindBool("disabledByHoldbackPrerenderSpeculationRules"),
+ true);
+}
+
+class DevToolsProtocolTest_PrefetchHoldbackDisabledIfCDPClientConnected
+ : public DevToolsProtocolTest {
+ protected:
+ void SetUp() override {
+ preloading_config_override_.SetHoldback("Prefetch", "SpeculationRules",
+ true);
+
+ DevToolsProtocolTest::SetUp();
+ }
+
+ private:
+ content::test::PreloadingConfigOverride preloading_config_override_;
+};
+
+// Check that prefetch is enabled if DevToolsAgentHost exists even if it is
+// disabled by PreloadingConfig.
+IN_PROC_BROWSER_TEST_F(
+ DevToolsProtocolTest_PrefetchHoldbackDisabledIfCDPClientConnected,
+ PrefetchHoldbackDisabledIfCDPClientConnected) {
+ Attach();
+
+ {
+ SendCommandAsync("Preload.enable");
+ const base::Value::Dict result =
+ WaitForNotification("Preload.preloadEnabledStateUpdated", true);
+
+ EXPECT_THAT(result.FindBool("disabledByHoldbackPrefetchSpeculationRules"),
+ true);
+ }
+
+ ASSERT_TRUE(embedded_test_server()->Start());
+ const GURL url(embedded_test_server()->GetURL("/empty.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ const std::string add_specrules = R"(
+ const specrules = document.createElement("script");
+ specrules.type = "speculationrules";
+ specrules.text = `
+ {
+ "prefetch":[
+ {
+ "source": "list",
+ "urls": ["title1.html"]
+ }
+ ]
+ }`;
+ document.body.appendChild(specrules);
+ )";
+
+ EXPECT_TRUE(content::EvalJs(web_contents(), add_specrules).error.empty());
+
+ {
+ base::Value::Dict result;
+ while (true) {
+ result = WaitForNotification("Preload.prefetchStatusUpdated", true);
+ if (*result.FindString("status") == "Ready") {
+ break;
+ }
+ }
+ }
+}
+
IN_PROC_BROWSER_TEST_F(
DevToolsProtocolTest,
NoPendingUrlShownWhenAttachedToBrowserInitiatedFailedNavigation) {
@@ -342,18 +464,27 @@ class DevToolsProtocolTest_BounceTrackingMitigations
protected:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
- dips::kFeature,
+ features::kDIPS,
{{"delete", "true"}, {"triggering_action", "stateful_bounce"}});
DevToolsProtocolTest::SetUp();
}
+ void SetBlockThirdPartyCookies(bool value) {
+ browser()->profile()->GetPrefs()->SetInteger(
+ prefs::kCookieControlsMode,
+ static_cast<int>(
+ value ? content_settings::CookieControlsMode::kBlockThirdParty
+ : content_settings::CookieControlsMode::kOff));
+ }
+
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest_BounceTrackingMitigations,
RunBounceTrackingMitigations) {
+ SetBlockThirdPartyCookies(true);
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/empty.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
@@ -390,13 +521,13 @@ class DIPSStatusDevToolsProtocolTest
: public DevToolsProtocolTest,
public testing::WithParamInterface<std::tuple<bool, bool, std::string>> {
// The fields of `GetParam()` indicate/control the following:
- // `std::get<0>(GetParam())` => `dips::kFeature`
- // `std::get<1>(GetParam())` => `dips::kDeletionEnabled`
- // `std::get<2>(GetParam())` => `dips::kTriggeringAction`
+ // `std::get<0>(GetParam())` => `features::kDIPS`
+ // `std::get<1>(GetParam())` => `features::kDIPSDeletionEnabled`
+ // `std::get<2>(GetParam())` => `features::kDIPSTriggeringAction`
//
- // In order for Bounce Tracking Mitigations to take effect, `kFeature` must
- // be true/enabled, `kDeletionEnabled` must be true, and `kTriggeringAction`
- // must NOT be `none`.
+ // In order for Bounce Tracking Mitigations to take effect, `features::kDIPS`
+ // must be true/enabled, `kDeletionEnabled` must be true, and
+ // `kTriggeringAction` must NOT be `none`.
//
// Note: Bounce Tracking Mitigations issues only report sites that would
// be affected when `kTriggeringAction` is set to 'stateful_bounce'.
@@ -405,11 +536,11 @@ class DIPSStatusDevToolsProtocolTest
void SetUp() override {
if (std::get<0>(GetParam())) {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
- dips::kFeature,
+ features::kDIPS,
{{"delete", (std::get<1>(GetParam()) ? "true" : "false")},
{"triggering_action", std::get<2>(GetParam())}});
} else {
- scoped_feature_list_.InitAndDisableFeature(dips::kFeature);
+ scoped_feature_list_.InitAndDisableFeature(features::kDIPS);
}
DevToolsProtocolTest::SetUp();
@@ -743,9 +874,9 @@ class ExtensionProtocolTest : public DevToolsProtocolTest {
return background_web_contents_;
}
- const extensions::Extension* LoadExtension(base::FilePath extension_path) {
+ const extensions::Extension* LoadExtensionOrApp(
+ const base::FilePath& extension_path) {
extensions::TestExtensionRegistryObserver observer(extension_registry_);
- ExtensionTestMessageListener activated_listener("WORKER_ACTIVATED");
extensions::UnpackedInstaller::Create(extension_service_)
->Load(extension_path);
observer.WaitForExtensionLoaded();
@@ -758,6 +889,13 @@ class ExtensionProtocolTest : public DevToolsProtocolTest {
}
}
CHECK(extension) << "Failed to find loaded extension " << extension_path;
+ return extension;
+ }
+
+ const extensions::Extension* LoadExtension(
+ const base::FilePath& extension_path) {
+ ExtensionTestMessageListener activated_listener("WORKER_ACTIVATED");
+ const extensions::Extension* extension = LoadExtensionOrApp(extension_path);
auto* process_manager =
extensions::ProcessManager::Get(browser()->profile());
if (extensions::BackgroundInfo::IsServiceWorkerBased(extension)) {
@@ -774,7 +912,16 @@ class ExtensionProtocolTest : public DevToolsProtocolTest {
return extension;
}
- void ReloadExtension(const std::string extension_id) {
+ void LaunchApp(const std::string& app_id) {
+ apps::AppLaunchParams params(
+ app_id, apps::LaunchContainer::kLaunchContainerNone,
+ WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
+ apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+ ->BrowserAppLauncher()
+ ->LaunchAppWithParamsForTesting(std::move(params));
+ }
+
+ void ReloadExtension(const std::string& extension_id) {
extensions::TestExtensionRegistryObserver observer(extension_registry_);
extension_service_->ReloadExtension(extension_id);
observer.WaitForExtensionLoaded();
@@ -858,6 +1005,120 @@ IN_PROC_BROWSER_TEST_F(ExtensionProtocolTest,
EXPECT_THAT(*detached.FindString("sessionId"), Eq("sessionId"));
}
+// Accepts a list of URL predicates and allows awaiting for all matching
+// WebContents to load.
+class WebContentsBarrier {
+ public:
+ using Predicate = base::FunctionRef<bool(const GURL& url)>;
+
+ WebContentsBarrier(std::initializer_list<Predicate> predicates)
+ : predicates_(predicates) {}
+
+ std::vector<content::WebContents*> Await() {
+ if (!IsReady()) {
+ base::RunLoop run_loop;
+ ready_callback_ = run_loop.QuitClosure();
+ run_loop.Run();
+ CHECK(IsReady());
+ }
+ return std::move(ready_web_contents_);
+ }
+
+ private:
+ class LoadObserver : public content::WebContentsObserver {
+ public:
+ LoadObserver(content::WebContents& wc, WebContentsBarrier& owner)
+ : WebContentsObserver(&wc), owner_(owner) {}
+
+ private:
+ void DidFinishLoad(content::RenderFrameHost* host,
+ const GURL& url) override {
+ if (host != web_contents()->GetPrimaryMainFrame()) {
+ return;
+ }
+ owner_->OnWebContentsLoaded(web_contents(), url);
+ }
+ const raw_ref<WebContentsBarrier> owner_;
+ };
+
+ bool IsReady() const { return !pending_contents_count_; }
+
+ void OnWebContentsCreated(content::WebContents* wc) {
+ observers_.push_back(std::make_unique<LoadObserver>(*wc, *this));
+ }
+
+ void OnWebContentsLoaded(content::WebContents* wc, const GURL& url) {
+ CHECK(!IsReady());
+ for (size_t i = 0; i < predicates_.size(); ++i) {
+ if (!predicates_[i](url)) {
+ continue;
+ }
+ CHECK(!ready_web_contents_[i])
+ << " predicate #" << i << " matches "
+ << ready_web_contents_[i]->GetLastCommittedURL() << " and " << url;
+ ready_web_contents_[i] = wc;
+ --pending_contents_count_;
+ }
+ if (IsReady() && ready_callback_) {
+ std::move(ready_callback_).Run();
+ }
+ }
+
+ const std::vector<Predicate> predicates_;
+
+ std::vector<content::WebContents*> ready_web_contents_{predicates_.size(),
+ nullptr};
+ size_t pending_contents_count_{predicates_.size()};
+ base::CallbackListSubscription creation_subscription_{
+ content::RegisterWebContentsCreationCallback(
+ base::BindRepeating(&WebContentsBarrier::OnWebContentsCreated,
+ base::Unretained(this)))};
+ std::vector<std::unique_ptr<LoadObserver>> observers_;
+ base::OnceClosure ready_callback_;
+};
+
+IN_PROC_BROWSER_TEST_F(ExtensionProtocolTest, TabTargetWithGuestView) {
+ base::FilePath extension_path =
+ base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
+ .AppendASCII("devtools")
+ .AppendASCII("extensions")
+ .AppendASCII("app_with_webview");
+ auto* extension = LoadExtensionOrApp(extension_path);
+ ASSERT_THAT(extension, testing::NotNull());
+
+ WebContentsBarrier barrier(
+ {[](const GURL& url) -> bool {
+ return base::EndsWith(url.path(), "host.html");
+ },
+ [](const GURL& url) -> bool { return url.SchemeIs(url::kDataScheme); }});
+
+ LaunchApp(extension->id());
+
+ std::vector<content::WebContents*> wcs = barrier.Await();
+ ASSERT_THAT(wcs, testing::SizeIs(2));
+ EXPECT_NE(wcs[0], wcs[1]);
+ // Assure host and view have different DevTools hosts.
+ EXPECT_NE(content::DevToolsAgentHost::GetOrCreateForTab(wcs[0]),
+ content::DevToolsAgentHost::GetOrCreateForTab(wcs[1]));
+ // Assure host does not auto-attach view.
+ AttachToTabTarget(wcs[0]);
+ base::Value::Dict command_params;
+ command_params = base::Value::Dict();
+ command_params.Set("autoAttach", true);
+ command_params.Set("waitForDebuggerOnStart", false);
+ command_params.Set("flatten", true);
+ SendCommandSync("Target.setAutoAttach", std::move(command_params));
+ EXPECT_FALSE(HasExistingNotificationMatching(
+ [](const base::Value::Dict& notification) {
+ if (*notification.FindString("method") != "Target.attachedToTarget") {
+ return false;
+ }
+ const std::string* url =
+ notification.FindStringByDottedPath("params.targetInfo.url");
+ return url && base::StartsWith(*url, "data:");
+ }));
+}
+
class PrerenderDataSaverProtocolTest : public DevToolsProtocolTest {
public:
PrerenderDataSaverProtocolTest()
@@ -867,7 +1128,7 @@ class PrerenderDataSaverProtocolTest : public DevToolsProtocolTest {
protected:
void SetUp() override {
- prerender_helper_.SetUp(embedded_test_server());
+ prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
data_saver::OverrideIsDataSaverEnabledForTesting(true);
DevToolsProtocolTest::SetUp();
}
diff --git a/chromium/chrome/browser/devtools/protocol/page_handler.cc b/chromium/chrome/browser/devtools/protocol/page_handler.cc
index 86e06c611fc..4b8cb2a93b6 100644
--- a/chromium/chrome/browser/devtools/protocol/page_handler.cc
+++ b/chromium/chrome/browser/devtools/protocol/page_handler.cc
@@ -27,7 +27,7 @@
template <typename T>
absl::optional<T> OptionalFromMaybe(const protocol::Maybe<T>& maybe) {
- return maybe.isJust() ? absl::optional<T>(maybe.fromJust()) : absl::nullopt;
+ return maybe.has_value() ? absl::optional<T>(maybe.value()) : absl::nullopt;
}
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
@@ -197,8 +197,8 @@ void PageHandler::GotManifestIcons(
protocol::Maybe<protocol::Binary> primaryIconAsBinary;
if (primary_icon && !primary_icon->empty()) {
- primaryIconAsBinary = std::move(protocol::Binary::fromRefCounted(
- gfx::Image::CreateFrom1xBitmap(*primary_icon).As1xPNGBytes()));
+ primaryIconAsBinary = protocol::Binary::fromRefCounted(
+ gfx::Image::CreateFrom1xBitmap(*primary_icon).As1xPNGBytes());
}
callback->sendSuccess(std::move(primaryIconAsBinary));
@@ -219,6 +219,7 @@ void PageHandler::PrintToPDF(protocol::Maybe<bool> landscape,
protocol::Maybe<protocol::String> footer_template,
protocol::Maybe<bool> prefer_css_page_size,
protocol::Maybe<protocol::String> transfer_mode,
+ protocol::Maybe<bool> generate_tagged_pdf,
std::unique_ptr<PrintToPDFCallback> callback) {
DCHECK(callback);
@@ -244,7 +245,8 @@ void PageHandler::PrintToPDF(protocol::Maybe<bool> landscape,
OptionalFromMaybe<double>(margin_right),
OptionalFromMaybe<std::string>(header_template),
OptionalFromMaybe<std::string>(footer_template),
- OptionalFromMaybe<bool>(prefer_css_page_size));
+ OptionalFromMaybe<bool>(prefer_css_page_size),
+ OptionalFromMaybe<bool>(generate_tagged_pdf));
if (absl::holds_alternative<std::string>(print_pages_params)) {
callback->sendFailure(protocol::Response::InvalidParams(
absl::get<std::string>(print_pages_params)));
@@ -255,7 +257,7 @@ void PageHandler::PrintToPDF(protocol::Maybe<bool> landscape,
print_pages_params));
bool return_as_stream =
- transfer_mode.fromMaybe("") ==
+ transfer_mode.value_or("") ==
protocol::Page::PrintToPDF::TransferModeEnum::ReturnAsStream;
// First check if headless printer manager is active and use it if so.
@@ -265,7 +267,7 @@ void PageHandler::PrintToPDF(protocol::Maybe<bool> landscape,
if (auto* print_manager = headless::HeadlessPrintManager::FromWebContents(
web_contents_.get())) {
print_manager->PrintToPdf(
- web_contents_->GetPrimaryMainFrame(), page_ranges.fromMaybe(""),
+ web_contents_->GetPrimaryMainFrame(), page_ranges.value_or(""),
std::move(absl::get<printing::mojom::PrintPagesParamsPtr>(
print_pages_params)),
base::BindOnce(&PageHandler::OnPDFCreated,
@@ -279,7 +281,7 @@ void PageHandler::PrintToPDF(protocol::Maybe<bool> landscape,
if (auto* print_manager =
ActivePrintManager::FromWebContents(web_contents_.get())) {
print_manager->PrintToPdf(
- web_contents_->GetPrimaryMainFrame(), page_ranges.fromMaybe(""),
+ web_contents_->GetPrimaryMainFrame(), page_ranges.value_or(""),
std::move(absl::get<printing::mojom::PrintPagesParamsPtr>(
print_pages_params)),
base::BindOnce(&PageHandler::OnPDFCreated,
diff --git a/chromium/chrome/browser/devtools/protocol/page_handler.h b/chromium/chrome/browser/devtools/protocol/page_handler.h
index 78b9f5b74e9..647ef4dcd7e 100644
--- a/chromium/chrome/browser/devtools/protocol/page_handler.h
+++ b/chromium/chrome/browser/devtools/protocol/page_handler.h
@@ -69,6 +69,7 @@ class PageHandler : public protocol::Page::Backend {
protocol::Maybe<protocol::String> footer_template,
protocol::Maybe<bool> prefer_css_page_size,
protocol::Maybe<protocol::String> transfer_mode,
+ protocol::Maybe<bool> generate_tagged_pdf,
std::unique_ptr<PrintToPDFCallback> callback) override;
void GetAppId(std::unique_ptr<GetAppIdCallback> callback) override;
diff --git a/chromium/chrome/browser/devtools/protocol/security_handler.cc b/chromium/chrome/browser/devtools/protocol/security_handler.cc
index a8f08349983..dbaf4ef09bc 100644
--- a/chromium/chrome/browser/devtools/protocol/security_handler.cc
+++ b/chromium/chrome/browser/devtools/protocol/security_handler.cc
@@ -11,6 +11,7 @@
#include "chrome/browser/ssl/security_state_tab_helper.h"
#include "components/security_state/content/content_utils.h"
#include "content/public/browser/web_contents.h"
+#include "net/base/net_errors.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/ssl/ssl_cipher_suite_names.h"
diff --git a/chromium/chrome/browser/devtools/protocol/system_info_handler.cc b/chromium/chrome/browser/devtools/protocol/system_info_handler.cc
index 8788073972c..bcbb1449b85 100644
--- a/chromium/chrome/browser/devtools/protocol/system_info_handler.cc
+++ b/chromium/chrome/browser/devtools/protocol/system_info_handler.cc
@@ -4,10 +4,10 @@
#include "chrome/browser/devtools/protocol/system_info_handler.h"
-#include "chrome/browser/dips/dips_features.h"
-#include "chrome/browser/dips/dips_utils.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/dips_utils.h"
SystemInfoHandler::SystemInfoHandler(protocol::UberDispatcher* dispatcher) {
protocol::SystemInfo::Dispatcher::wire(dispatcher, this);
@@ -19,10 +19,10 @@ protocol::Response SystemInfoHandler::GetFeatureState(
const std::string& in_featureState,
bool* featureEnabled) {
if (in_featureState == "DIPS") {
- *featureEnabled =
- base::FeatureList::IsEnabled(dips::kFeature) &&
- dips::kDeletionEnabled.Get() &&
- (dips::kTriggeringAction.Get() != DIPSTriggeringAction::kNone);
+ *featureEnabled = base::FeatureList::IsEnabled(features::kDIPS) &&
+ features::kDIPSDeletionEnabled.Get() &&
+ (features::kDIPSTriggeringAction.Get() !=
+ content::DIPSTriggeringAction::kNone);
return protocol::Response::Success();
}
diff --git a/chromium/chrome/browser/devtools/protocol/target_handler.cc b/chromium/chrome/browser/devtools/protocol/target_handler.cc
index 91c4748fa5b..d640ef1dd1b 100644
--- a/chromium/chrome/browser/devtools/protocol/target_handler.cc
+++ b/chromium/chrome/browser/devtools/protocol/target_handler.cc
@@ -81,8 +81,8 @@ protocol::Response TargetHandler::CreateTarget(
protocol::Maybe<bool> for_tab,
std::string* out_target_id) {
Profile* profile = nullptr;
- if (browser_context_id.isJust()) {
- std::string profile_id = browser_context_id.fromJust();
+ if (browser_context_id.has_value()) {
+ std::string profile_id = browser_context_id.value();
profile =
DevToolsBrowserContextManager::GetInstance().GetProfileById(profile_id);
if (!profile) {
@@ -94,8 +94,8 @@ protocol::Response TargetHandler::CreateTarget(
DCHECK(profile);
}
- bool create_new_window = new_window.fromMaybe(false);
- bool create_in_background = background.fromMaybe(false);
+ bool create_new_window = new_window.value_or(false);
+ bool create_in_background = background.value_or(false);
Browser* target_browser = nullptr;
// Must find target_browser if new_window not explicitly true.
@@ -111,7 +111,7 @@ protocol::Response TargetHandler::CreateTarget(
}
}
- bool explicit_old_window = !new_window.fromMaybe(true);
+ bool explicit_old_window = !new_window.value_or(true);
if (explicit_old_window && !target_browser) {
return protocol::Response::ServerError(
"Failed to open new tab - "
@@ -136,7 +136,7 @@ protocol::Response TargetHandler::CreateTarget(
if (!params.navigated_or_inserted_contents)
return protocol::Response::ServerError("Failed to open a new tab");
- if (for_tab.fromMaybe(false)) {
+ if (for_tab.value_or(false)) {
*out_target_id = content::DevToolsAgentHost::GetOrCreateForTab(
params.navigated_or_inserted_contents)
->GetId();
diff --git a/chromium/chrome/browser/dips/BUILD.gn b/chromium/chrome/browser/dips/BUILD.gn
index 949cc3033eb..21c8bb77c7d 100644
--- a/chromium/chrome/browser/dips/BUILD.gn
+++ b/chromium/chrome/browser/dips/BUILD.gn
@@ -35,7 +35,11 @@ source_set("unit_tests") {
bundle_data("golden_dbs_bundle_data") {
visibility = [ ":unit_tests" ]
testonly = true
- sources = [ "//chrome/test/data/dips/v1.sql" ]
+ sources = [
+ "//chrome/test/data/dips/v1.sql",
+ "//chrome/test/data/dips/v2.sql",
+ "//chrome/test/data/dips/v3.sql",
+ ]
outputs = [ "{{bundle_resources_dir}}/" +
"{{source_root_relative_dir}}/{{source_file_part}}" ]
}
diff --git a/chromium/chrome/browser/download/android/BUILD.gn b/chromium/chrome/browser/download/android/BUILD.gn
index 52609b0cb5f..fa8be30746a 100644
--- a/chromium/chrome/browser/download/android/BUILD.gn
+++ b/chromium/chrome/browser/download/android/BUILD.gn
@@ -193,6 +193,7 @@ robolectric_library("junit_tests") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
"//chrome/test/android:chrome_java_unit_test_support",
diff --git a/chromium/chrome/browser/download/internal/android/BUILD.gn b/chromium/chrome/browser/download/internal/android/BUILD.gn
index 857636020d4..09361f7e30a 100644
--- a/chromium/chrome/browser/download/internal/android/BUILD.gn
+++ b/chromium/chrome/browser/download/internal/android/BUILD.gn
@@ -231,9 +231,12 @@ android_library("javatests") {
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
+ "//ui/android:ui_java",
"//ui/android:ui_java_test_support",
+ "//url:gurl_java",
"//url:gurl_junit_test_support",
]
@@ -254,6 +257,7 @@ android_library("unit_device_javatests") {
":java_resources",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
"//chrome/browser/back_press/android:java",
"//chrome/browser/download/android:java",
@@ -264,6 +268,7 @@ android_library("unit_device_javatests") {
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/modaldialog/android:java",
"//components/browser_ui/util/android:java",
+ "//components/browser_ui/widget/android:java",
"//components/embedder_support/android:util_java",
"//components/feature_engagement:feature_engagement_java",
"//components/messages/android:java",
@@ -279,6 +284,7 @@ android_library("unit_device_javatests") {
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_core_core_java",
+ "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_monitor_java",
@@ -315,6 +321,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/download/android:java",
"//chrome/browser/profiles/android:java",
"//components/browser_ui/util/android:java",
diff --git a/chromium/chrome/browser/engagement/android/BUILD.gn b/chromium/chrome/browser/engagement/android/BUILD.gn
index 412ba038122..6f6130973f5 100644
--- a/chromium/chrome/browser/engagement/android/BUILD.gn
+++ b/chromium/chrome/browser/engagement/android/BUILD.gn
@@ -14,6 +14,7 @@ android_library("javatests") {
"//chrome/browser/tab:java",
"//chrome/test/android:chrome_java_integration_test_support",
"//components/site_engagement/content/android:java",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit:junit",
diff --git a/chromium/chrome/browser/enterprise/connectors/device_trust/attestation/ash/BUILD.gn b/chromium/chrome/browser/enterprise/connectors/device_trust/attestation/ash/BUILD.gn
index 99dce4b30af..1c0a8c757fb 100644
--- a/chromium/chrome/browser/enterprise/connectors/device_trust/attestation/ash/BUILD.gn
+++ b/chromium/chrome/browser/enterprise/connectors/device_trust/attestation/ash/BUILD.gn
@@ -7,9 +7,16 @@ import("//build/config/chromeos/ui_mode.gni")
assert(is_chromeos_ash)
source_set("ash") {
- sources = [ "ash_attestation_service.cc" ]
+ sources = [
+ "ash_attestation_cleanup_manager.cc",
+ "ash_attestation_service_impl.cc",
+ ]
- public = [ "ash_attestation_service.h" ]
+ public = [
+ "ash_attestation_cleanup_manager.h",
+ "ash_attestation_service.h",
+ "ash_attestation_service_impl.h",
+ ]
deps = [
"//chrome/browser/ash",
diff --git a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/BUILD.gn b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/BUILD.gn
index 3b79d11fca9..e49a6a2460e 100644
--- a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/BUILD.gn
+++ b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/BUILD.gn
@@ -5,30 +5,37 @@
source_set("browser") {
public = [
"device_trust_key_manager_impl.h",
+ "key_loader.h",
+ "key_loader_impl.h",
"key_rotation_launcher.h",
"metrics_utils.h",
]
sources = [
"device_trust_key_manager_impl.cc",
+ "key_loader.cc",
+ "key_loader_impl.cc",
"key_rotation_launcher.cc",
"key_rotation_launcher_impl.cc",
"key_rotation_launcher_impl.h",
+ "key_utils.cc",
+ "key_utils.h",
"metrics_utils.cc",
]
public_deps = [
"//base",
"//chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands",
- "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network",
"//components/enterprise",
"//components/policy/proto",
"//third_party/abseil-cpp:absl",
]
deps = [
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/common",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core:util",
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence",
"//components/policy/core/common",
"//components/prefs",
@@ -36,23 +43,28 @@ source_set("browser") {
"//services/network/public/cpp",
"//services/network/public/cpp:cpp_base",
]
+
+ if (is_win) {
+ deps += [ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network:win_key_network_delegate" ]
+ }
}
source_set("test_support") {
testonly = true
public = [
"mock_device_trust_key_manager.h",
+ "mock_key_loader.h",
"mock_key_rotation_launcher.h",
]
sources = [
"mock_device_trust_key_manager.cc",
+ "mock_key_loader.cc",
"mock_key_rotation_launcher.cc",
]
public_deps = [
":browser",
- "//chrome/browser/enterprise/connectors/device_trust/key_management/core",
"//components/enterprise",
"//components/policy/proto",
"//testing/gmock",
@@ -63,6 +75,7 @@ source_set("unit_tests") {
testonly = true
sources = [
"device_trust_key_manager_impl_unittest.cc",
+ "key_loader_unittest.cc",
"key_rotation_launcher_unittest.cc",
]
@@ -73,7 +86,10 @@ source_set("unit_tests") {
"//chrome/browser/enterprise/connectors/device_trust/common",
"//chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands",
"//chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands:test_support",
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/common",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core",
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network",
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network:test_support",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence:test_support",
"//components/enterprise",
diff --git a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn
index 1c1161c2e47..a88a584b1fc 100644
--- a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn
+++ b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn
@@ -126,6 +126,7 @@ source_set("unit_tests") {
deps += [
"//chrome/browser",
"//chrome/browser/enterprise/connectors/device_trust:prefs",
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/common",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/mac",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/mac:test_support",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/network:test_support",
diff --git a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/common/BUILD.gn b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/common/BUILD.gn
new file mode 100644
index 00000000000..6e82e7a59cb
--- /dev/null
+++ b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/common/BUILD.gn
@@ -0,0 +1,7 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("common") {
+ public = [ "key_types.h" ]
+}
diff --git a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/BUILD.gn b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/BUILD.gn
index 6a2e36550a3..de269313e69 100644
--- a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/BUILD.gn
+++ b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/BUILD.gn
@@ -49,6 +49,7 @@ source_set("unit_tests") {
":core",
":util",
"//base",
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/common",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence:test_support",
"//crypto",
"//crypto:test_support",
diff --git a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/BUILD.gn b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/BUILD.gn
index adfc9724c47..90ea9b30971 100644
--- a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/BUILD.gn
+++ b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/BUILD.gn
@@ -32,8 +32,6 @@ source_set("mac") {
"//third_party/boringssl",
]
- configs += [ "//build/config/compiler:enable_arc" ]
-
frameworks = [
"CoreFoundation.framework",
"CryptoTokenKit.framework",
@@ -79,8 +77,6 @@ source_set("unit_tests") {
"//testing/gtest",
]
- configs += [ "//build/config/compiler:enable_arc" ]
-
frameworks = [
"Foundation.framework",
"Security.framework",
diff --git a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/BUILD.gn b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/BUILD.gn
index c8e1b1b9772..a51053a951b 100644
--- a/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/BUILD.gn
+++ b/chromium/chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/BUILD.gn
@@ -17,6 +17,7 @@ source_set("persistence") {
deps = [ "//base" ]
public_deps = [
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/common",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core",
"//components/policy/proto",
"//crypto",
@@ -42,8 +43,6 @@ source_set("persistence") {
"//chrome/browser/enterprise/connectors/device_trust/key_management/core:constants",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/mac",
]
- configs += [ "//build/config/compiler:enable_arc" ]
-
friend = [ ":unit_tests" ]
}
@@ -55,6 +54,7 @@ source_set("persistence") {
deps += [
"//build:branding_buildflags",
"//chrome/browser/enterprise/connectors/device_trust/key_management/core:constants",
+ "//components/policy/core/common:policy_path_constants",
]
friend = [ ":unit_tests" ]
@@ -123,8 +123,6 @@ source_set("unit_tests") {
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/mac:test_support",
]
- configs += [ "//build/config/compiler:enable_arc" ]
-
frameworks = [
"Foundation.framework",
"Security.framework",
diff --git a/chromium/chrome/browser/enterprise/connectors/device_trust/test/BUILD.gn b/chromium/chrome/browser/enterprise/connectors/device_trust/test/BUILD.gn
index 1702172c82b..7a0f5b89ce4 100644
--- a/chromium/chrome/browser/enterprise/connectors/device_trust/test/BUILD.gn
+++ b/chromium/chrome/browser/enterprise/connectors/device_trust/test/BUILD.gn
@@ -44,7 +44,11 @@ source_set("test_support") {
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/network:network",
]
- deps += [ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence" ]
+ deps += [
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence",
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence:test_support",
+ "//chrome/browser/enterprise/connectors/device_trust/key_management/installer:elevated_rotation",
+ ]
}
if (is_win) {
@@ -61,7 +65,6 @@ source_set("test_support") {
deps += [
"//chrome/browser/enterprise/connectors/device_trust/key_management/core:constants",
- "//chrome/browser/enterprise/connectors/device_trust/key_management/installer:elevated_rotation",
"//chrome/browser/enterprise/connectors/device_trust/key_management/installer/management_service:rotate_util",
"//chrome/install_static:install_static_util",
"//chrome/installer/util:constants",
diff --git a/chromium/chrome/browser/enterprise/data_controls/BUILD.gn b/chromium/chrome/browser/enterprise/data_controls/BUILD.gn
index 677d0f192a0..2e09a86eb06 100644
--- a/chromium/chrome/browser/enterprise/data_controls/BUILD.gn
+++ b/chromium/chrome/browser/enterprise/data_controls/BUILD.gn
@@ -2,6 +2,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//components/enterprise/buildflags/buildflags.gni")
+
+assert(enterprise_data_controls)
+
source_set("data_controls") {
sources = [
"action_context.h",
@@ -15,6 +19,7 @@ source_set("data_controls") {
deps = [
"//base",
+ "//components/enterprise/data_controls:features",
"//components/keyed_service/core",
"//components/policy/core/browser",
"//components/prefs",
diff --git a/chromium/chrome/browser/enterprise/platform_auth/BUILD.gn b/chromium/chrome/browser/enterprise/platform_auth/BUILD.gn
index 27b4bab5705..85c55f6b1f8 100644
--- a/chromium/chrome/browser/enterprise/platform_auth/BUILD.gn
+++ b/chromium/chrome/browser/enterprise/platform_auth/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2022 The Chromium Authors. All rights reserved.
+# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/chromium/chrome/browser/error_reporting/BUILD.gn b/chromium/chrome/browser/error_reporting/BUILD.gn
index 8b041701409..1440bbe3c59 100644
--- a/chromium/chrome/browser/error_reporting/BUILD.gn
+++ b/chromium/chrome/browser/error_reporting/BUILD.gn
@@ -28,7 +28,7 @@ static_library("error_reporting") {
"//components/crash/content/browser/error_reporting",
"//components/crash/core/app",
"//components/feedback/redaction_tool",
- "//components/startup_metric_utils/browser",
+ "//components/startup_metric_utils",
"//components/upload_list",
"//components/variations",
"//content/public/browser",
diff --git a/chromium/chrome/browser/extensions/BUILD.gn b/chromium/chrome/browser/extensions/BUILD.gn
index 51ffd40dbf0..690a5ddf57d 100644
--- a/chromium/chrome/browser/extensions/BUILD.gn
+++ b/chromium/chrome/browser/extensions/BUILD.gn
@@ -185,8 +185,6 @@ static_library("extensions") {
"api/identity/identity_launch_web_auth_flow_function.h",
"api/identity/identity_mint_queue.cc",
"api/identity/identity_mint_queue.h",
- "api/identity/identity_private_api.cc",
- "api/identity/identity_private_api.h",
"api/identity/identity_remove_cached_auth_token_function.cc",
"api/identity/identity_remove_cached_auth_token_function.h",
"api/identity/identity_token_cache.cc",
@@ -290,6 +288,14 @@ static_library("extensions") {
"api/proxy/proxy_api_constants.h",
"api/proxy/proxy_api_helpers.cc",
"api/proxy/proxy_api_helpers.h",
+ "api/reading_list/reading_list_api.cc",
+ "api/reading_list/reading_list_api.h",
+ "api/reading_list/reading_list_api_constants.cc",
+ "api/reading_list/reading_list_api_constants.h",
+ "api/reading_list/reading_list_event_router.cc",
+ "api/reading_list/reading_list_event_router.h",
+ "api/reading_list/reading_list_util.cc",
+ "api/reading_list/reading_list_util.h",
"api/resources_private/resources_private_api.cc",
"api/resources_private/resources_private_api.h",
"api/runtime/chrome_runtime_api_delegate.cc",
@@ -566,6 +572,8 @@ static_library("extensions") {
"extension_system_impl.h",
"extension_tab_util.cc",
"extension_tab_util.h",
+ "extension_telemetry_service_verdict_handler.cc",
+ "extension_telemetry_service_verdict_handler.h",
"extension_ui_util.cc",
"extension_ui_util.h",
"extension_uninstall_dialog.cc",
@@ -735,6 +743,13 @@ static_library("extensions") {
]
}
+ if (is_chromeos_ash) {
+ sources += [
+ "file_handlers/web_file_handlers_permission_handler.cc",
+ "file_handlers/web_file_handlers_permission_handler.h",
+ ]
+ }
+
configs += [
"//build/config:precompiled_headers",
"//build/config/compiler:wexit_time_destructors",
@@ -811,7 +826,6 @@ static_library("extensions") {
"//chrome/browser/safe_browsing:metrics_collector",
"//chrome/browser/ui/tabs:tab_enums",
"//chrome/browser/web_applications",
- "//chromeos/components/kiosk",
"//components/cbor:cbor",
"//components/commerce/core:pref_names",
"//components/device_reauth",
@@ -865,6 +879,7 @@ static_library("extensions") {
"//components/language/core/common",
"//components/language/core/language_model",
"//components/live_caption:constants",
+ "//components/media_device_salt",
"//components/nacl/common:buildflags",
"//components/navigation_interception",
"//components/net_log",
@@ -875,6 +890,8 @@ static_library("extensions") {
"//components/password_manager/core/browser",
"//components/password_manager/core/browser:affiliation",
"//components/password_manager/core/browser:import_results",
+ "//components/password_manager/core/browser/features:password_features",
+ "//components/password_manager/core/browser/import:importer",
"//components/password_manager/core/browser/leak_detection",
"//components/payments/core",
"//components/performance_manager",
@@ -883,7 +900,9 @@ static_library("extensions") {
"//components/policy/core/browser",
"//components/pref_registry",
"//components/privacy_sandbox:privacy_sandbox_prefs",
+ "//components/privacy_sandbox:tracking_protection_prefs",
"//components/proxy_config",
+ "//components/reading_list/core",
"//components/resources",
"//components/safe_browsing:buildflags",
"//components/safe_browsing/content/browser/web_ui:web_ui",
@@ -892,7 +911,7 @@ static_library("extensions") {
"//components/safe_browsing/core/common:safe_browsing_prefs",
"//components/safe_browsing/core/common/proto:csd_proto",
"//components/search_engines",
- "//components/services/app_service/public/cpp:app_types",
+ "//components/services/app_service",
"//components/services/patch/content",
"//components/services/unzip/content",
"//components/services/unzip/public/cpp",
@@ -1170,11 +1189,11 @@ static_library("extensions") {
"//ash/webui/camera_app_ui:mojo_bindings",
"//ash/webui/file_manager/untrusted_resources:file_manager_untrusted_resources",
"//ash/webui/resources:media_app_bundle_resources_grit",
+ "//ash/webui/settings/public/constants:mojom",
"//chrome/browser/ash/crosapi",
"//chrome/browser/ash/crostini:crostini_installer_types_mojom",
"//chrome/browser/devtools",
"//chrome/browser/nearby_sharing/common",
- "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
"//chromeos/ash/components/attestation",
"//chromeos/ash/components/cryptohome",
"//chromeos/ash/components/dbus",
@@ -1193,6 +1212,7 @@ static_library("extensions") {
"//chromeos/ash/components/login/auth",
"//chromeos/ash/components/login/login_state",
"//chromeos/ash/components/network",
+ "//chromeos/ash/components/osauth/public",
"//chromeos/ash/components/proximity_auth",
"//chromeos/ash/components/settings",
"//chromeos/ash/components/system",
@@ -1286,7 +1306,6 @@ static_library("extensions") {
"system_display/display_info_provider_mac.cc",
"system_display/display_info_provider_mac.h",
]
- configs += [ "//build/config/compiler:enable_arc" ]
}
if (is_linux || is_chromeos_lacros) {
@@ -1407,7 +1426,6 @@ static_library("test_support") {
testonly = true
sources = [
- "api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h",
"api/passwords_private/test_passwords_private_delegate.cc",
"api/passwords_private/test_passwords_private_delegate.h",
"chrome_extension_test_notification_observer.cc",
diff --git a/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc b/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc
index 6fb95362c99..807a958ebbb 100644
--- a/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc
@@ -112,25 +112,8 @@ IN_PROC_BROWSER_TEST_P(AlarmsApiTest, IncognitoSpanning) {
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
-// Test that the histogram for counting the number of alarms for an extension
-// is working properly. The PRE step installs the alarms and we'll check the
-// histogram in the main part of the test.
-IN_PROC_BROWSER_TEST_P(AlarmsApiTest, PRE_Count) {
+IN_PROC_BROWSER_TEST_P(AlarmsApiTest, Count) {
EXPECT_TRUE(RunExtensionTest("alarms/count")) << message_;
}
-// TODO(crbug.com/1405713): Fix failing test on Mac builders.
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_Count DISABLED_Count
-#else
-#define MAYBE_Count Count
-#endif
-IN_PROC_BROWSER_TEST_P(AlarmsApiTest, MAYBE_Count) {
- // The histogram will be updated when the extension is loaded during
- // startup. This will happen before we enter the test, so just check
- // that the update is present.
- histogram_tester_->ExpectUniqueSample(
- "Extensions.AlarmManager.AlarmsLoadedCount", 100, 1);
-}
-
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc b/chromium/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
index c10ee262684..6aec2a92208 100644
--- a/chromium/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
+++ b/chromium/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
@@ -25,6 +25,7 @@
#include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h"
#include "chrome/browser/extensions/api/preference/preference_api.h"
#include "chrome/browser/extensions/api/processes/processes_api.h"
+#include "chrome/browser/extensions/api/reading_list/reading_list_event_router.h"
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
#include "chrome/browser/extensions/api/sessions/sessions_api.h"
#include "chrome/browser/extensions/api/settings_overrides/settings_overrides_api.h"
@@ -101,6 +102,7 @@ void EnsureApiBrowserContextKeyedServiceFactoriesBuilt() {
#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
extensions::PreferenceAPI::GetFactoryInstance();
extensions::ProcessesAPI::GetFactoryInstance();
+ extensions::ReadingListEventRouter::GetFactoryInstance();
extensions::SafeBrowsingPrivateEventRouterFactory::GetInstance();
extensions::SessionsAPI::GetFactoryInstance();
extensions::SettingsPrivateEventRouterFactory::GetInstance();
diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
index a956d2e7f5d..b619e9a0bde 100644
--- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
@@ -6,6 +6,7 @@
#include <stddef.h>
+#include <algorithm>
#include <utility>
#include "base/functional/bind.h"
@@ -22,12 +23,15 @@
#include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/autofill/core/browser/autofill_address_util.h"
+#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/browser_autofill_manager.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/data_model/iban.h"
#include "components/autofill/core/browser/form_data_importer.h"
+#include "components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h"
#include "components/autofill/core/browser/payments/local_card_migration_manager.h"
+#include "components/autofill/core/browser/payments/mandatory_reauth_manager.h"
#include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h"
#include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
#include "components/autofill/core/browser/personal_data_manager.h"
@@ -36,7 +40,6 @@
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/strings/grit/components_chromium_strings.h"
-#include "components/strings/grit/components_google_chrome_strings.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_function.h"
@@ -50,6 +53,11 @@
namespace autofill_private = extensions::api::autofill_private;
namespace addressinput = i18n::addressinput;
+using autofill::autofill_metrics::LogMandatoryReauthOptInOrOutUpdateEvent;
+using autofill::autofill_metrics::LogMandatoryReauthSettingsPageEditCardEvent;
+using autofill::autofill_metrics::MandatoryReauthAuthenticationFlowEvent;
+using autofill::autofill_metrics::MandatoryReauthOptInOrOutSource;
+
namespace {
static const char kSettingsOrigin[] = "Chrome settings";
@@ -67,89 +75,19 @@ constexpr char kFieldLengthKey[] = "isLongField";
constexpr char kFieldNameKey[] = "fieldName";
constexpr char kFieldRequired[] = "isRequired";
-// Field names for the address components.
-constexpr char kFullNameField[] = "FULL_NAME";
-constexpr char kCompanyNameField[] = "COMPANY_NAME";
-constexpr char kAddressLineField[] = "ADDRESS_LINES";
-constexpr char kDependentLocalityField[] = "ADDRESS_LEVEL_3";
-constexpr char kCityField[] = "ADDRESS_LEVEL_2";
-constexpr char kStateField[] = "ADDRESS_LEVEL_1";
-constexpr char kPostalCodeField[] = "POSTAL_CODE";
-constexpr char kSortingCodeField[] = "SORTING_CODE";
-constexpr char kCountryField[] = "COUNTY_CODE";
-
-// Converts an autofill::ServerFieldType to string format. Used in serilization
-// of field type info to be used in JavaScript code, and hence those values
-// shouldn't be modified.
-const char* GetStringFromAddressField(i18n::addressinput::AddressField type) {
- switch (type) {
- case i18n::addressinput::RECIPIENT:
- return kFullNameField;
- case i18n::addressinput::ORGANIZATION:
- return kCompanyNameField;
- case i18n::addressinput::STREET_ADDRESS:
- return kAddressLineField;
- case i18n::addressinput::DEPENDENT_LOCALITY:
- return kDependentLocalityField;
- case i18n::addressinput::LOCALITY:
- return kCityField;
- case i18n::addressinput::ADMIN_AREA:
- return kStateField;
- case i18n::addressinput::POSTAL_CODE:
- return kPostalCodeField;
- case i18n::addressinput::SORTING_CODE:
- return kSortingCodeField;
- case i18n::addressinput::COUNTRY:
- return kCountryField;
- default:
- NOTREACHED();
- return "";
- }
-}
-
// Serializes the AddressUiComponent a map from string to base::Value().
base::Value::Dict AddressUiComponentAsValueMap(
- const autofill::ExtendedAddressUiComponent& address_ui_component) {
+ const autofill::AutofillAddressUIComponent& address_ui_component) {
base::Value::Dict info;
info.Set(kFieldNameKey, address_ui_component.name);
- info.Set(kFieldTypeKey,
- GetStringFromAddressField(address_ui_component.field));
+ info.Set(kFieldTypeKey, FieldTypeToStringPiece(address_ui_component.field));
info.Set(kFieldLengthKey,
address_ui_component.length_hint ==
- i18n::addressinput::AddressUiComponent::HINT_LONG);
+ autofill::AutofillAddressUIComponent::HINT_LONG);
info.Set(kFieldRequired, address_ui_component.is_required);
return info;
}
-// Searches the |list| for the value at |index|. If this value is present in
-// any of the rest of the list, then the item (at |index|) is removed. The
-// comparison of phone number values is done on normalized versions of the phone
-// number values.
-void RemoveDuplicatePhoneNumberAtIndex(size_t index,
- const std::string& country_code,
- base::Value::List& list) {
- if (list.size() <= index) {
- NOTREACHED() << "List should have a value at index " << index;
- return;
- }
- const std::string& new_value = list[index].GetString();
-
- bool is_duplicate = false;
- std::string app_locale = g_browser_process->GetApplicationLocale();
- for (size_t i = 0; i < list.size() && !is_duplicate; ++i) {
- if (i == index)
- continue;
-
- const std::string& existing_value = list[i].GetString();
- is_duplicate = autofill::i18n::PhoneNumbersMatch(
- base::UTF8ToUTF16(new_value), base::UTF8ToUTF16(existing_value),
- country_code, app_locale);
- }
-
- if (is_duplicate)
- list.erase(list.begin() + index);
-}
-
autofill::AutofillManager* GetAutofillManager(
content::WebContents* web_contents) {
if (!web_contents) {
@@ -160,7 +98,7 @@ autofill::AutofillManager* GetAutofillManager(
->DriverForFrame(web_contents->GetPrimaryMainFrame());
if (!autofill_driver)
return nullptr;
- return autofill_driver->autofill_manager();
+ return &autofill_driver->GetAutofillManager();
}
autofill::AutofillProfile CreateNewAutofillProfile(
@@ -238,90 +176,33 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveAddressFunction::Run() {
if (!existing_profile)
return RespondNow(Error(kErrorDataUnavailable));
}
- autofill::AutofillProfile profile =
- existing_profile
- ? *existing_profile
- : CreateNewAutofillProfile(personal_data, address->country_code);
-
- if (address->full_names) {
- std::string full_name;
- if (!address->full_names->empty())
- full_name = address->full_names->at(0);
- profile.SetInfoWithVerificationStatus(
- autofill::AutofillType(autofill::NAME_FULL),
- base::UTF8ToUTF16(full_name), g_browser_process->GetApplicationLocale(),
- kUserVerified);
- }
-
- if (address->honorific) {
- profile.SetRawInfoWithVerificationStatus(
- autofill::NAME_HONORIFIC_PREFIX, base::UTF8ToUTF16(*address->honorific),
- kUserVerified);
- }
-
- if (address->company_name) {
- profile.SetRawInfoWithVerificationStatus(
- autofill::COMPANY_NAME, base::UTF8ToUTF16(*address->company_name),
- kUserVerified);
- }
-
- if (address->address_lines) {
- profile.SetRawInfoWithVerificationStatus(
- autofill::ADDRESS_HOME_STREET_ADDRESS,
- base::UTF8ToUTF16(*address->address_lines), kUserVerified);
- }
-
- if (address->address_level1) {
- profile.SetRawInfoWithVerificationStatus(
- autofill::ADDRESS_HOME_STATE,
- base::UTF8ToUTF16(*address->address_level1), kUserVerified);
- }
-
- if (address->address_level2) {
- profile.SetRawInfoWithVerificationStatus(
- autofill::ADDRESS_HOME_CITY,
- base::UTF8ToUTF16(*address->address_level2), kUserVerified);
- }
-
- if (address->address_level3) {
- profile.SetRawInfoWithVerificationStatus(
- autofill::ADDRESS_HOME_DEPENDENT_LOCALITY,
- base::UTF8ToUTF16(*address->address_level3), kUserVerified);
- }
-
- if (address->postal_code) {
- profile.SetRawInfoWithVerificationStatus(
- autofill::ADDRESS_HOME_ZIP, base::UTF8ToUTF16(*address->postal_code),
- kUserVerified);
- }
-
- if (address->sorting_code) {
- profile.SetRawInfoWithVerificationStatus(
- autofill::ADDRESS_HOME_SORTING_CODE,
- base::UTF8ToUTF16(*address->sorting_code), kUserVerified);
- }
-
- if (address->country_code) {
- profile.SetRawInfoWithVerificationStatus(
- autofill::ADDRESS_HOME_COUNTRY,
- base::UTF8ToUTF16(*address->country_code), kUserVerified);
+ absl::optional<base::StringPiece> country_code;
+ if (auto it = std::find_if(
+ address->fields.begin(), address->fields.end(),
+ [](const auto& field) {
+ return field.type ==
+ autofill_private::ServerFieldType::kAddressHomeCountry;
+ });
+ it != address->fields.end()) {
+ country_code = it->value;
}
-
- if (address->phone_numbers) {
- std::string phone;
- if (!address->phone_numbers->empty())
- phone = address->phone_numbers->at(0);
- profile.SetRawInfoWithVerificationStatus(autofill::PHONE_HOME_WHOLE_NUMBER,
- base::UTF8ToUTF16(phone),
- kUserVerified);
- }
-
- if (address->email_addresses) {
- std::string email;
- if (!address->email_addresses->empty())
- email = address->email_addresses->at(0);
- profile.SetRawInfoWithVerificationStatus(
- autofill::EMAIL_ADDRESS, base::UTF8ToUTF16(email), kUserVerified);
+ autofill::AutofillProfile profile =
+ existing_profile ? *existing_profile
+ : CreateNewAutofillProfile(personal_data, country_code);
+
+ // TODO(crbug.com/1441904): Fields not visible for the autofill profile's
+ // country must be reset.
+ for (const api::autofill_private::AddressField& field : address->fields) {
+ if (field.type == autofill_private::ServerFieldType::kNameFull) {
+ profile.SetInfoWithVerificationStatus(
+ autofill::AutofillType(autofill::NAME_FULL),
+ base::UTF8ToUTF16(field.value),
+ g_browser_process->GetApplicationLocale(), kUserVerified);
+ } else {
+ profile.SetRawInfoWithVerificationStatus(
+ autofill::TypeNameToFieldType(autofill_private::ToString(field.type)),
+ base::UTF8ToUTF16(field.value), kUserVerified);
+ }
}
if (address->language_code)
@@ -369,7 +250,7 @@ AutofillPrivateGetAddressComponentsFunction::Run() {
api::autofill_private::GetAddressComponents::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(parameters);
- std::vector<std::vector<autofill::ExtendedAddressUiComponent>> lines;
+ std::vector<std::vector<autofill::AutofillAddressUIComponent>> lines;
std::string language_code;
autofill::GetAddressComponents(
@@ -382,7 +263,7 @@ AutofillPrivateGetAddressComponentsFunction::Run() {
for (auto& line : lines) {
base::Value::List row_values;
- for (const autofill::ExtendedAddressUiComponent& component : line) {
+ for (const autofill::AutofillAddressUIComponent& component : line) {
row_values.Append(AddressUiComponentAsValueMap(component));
}
base::Value::Dict row;
@@ -508,7 +389,7 @@ ExtensionFunction::ResponseAction AutofillPrivateRemoveEntryFunction::Run() {
if (!personal_data || !personal_data->IsDataLoaded())
return RespondNow(Error(kErrorDataUnavailable));
- if (personal_data->GetIBANByGUID(parameters->guid)) {
+ if (personal_data->GetIbanByGUID(parameters->guid)) {
base::RecordAction(base::UserMetricsAction("AutofillIbanDeleted"));
}
@@ -518,30 +399,6 @@ ExtensionFunction::ResponseAction AutofillPrivateRemoveEntryFunction::Run() {
}
////////////////////////////////////////////////////////////////////////////////
-// AutofillPrivateValidatePhoneNumbersFunction
-
-ExtensionFunction::ResponseAction
-AutofillPrivateValidatePhoneNumbersFunction::Run() {
- absl::optional<api::autofill_private::ValidatePhoneNumbers::Params>
- parameters =
- api::autofill_private::ValidatePhoneNumbers::Params::Create(args());
- EXTENSION_FUNCTION_VALIDATE(parameters);
-
- api::autofill_private::ValidatePhoneParams& params = parameters->params;
-
- // Extract the phone numbers into a base::Value::List.
- base::Value::List phone_numbers;
- for (auto phone_number : params.phone_numbers) {
- phone_numbers.Append(phone_number);
- }
-
- RemoveDuplicatePhoneNumberAtIndex(params.index_of_new_number,
- params.country_code, phone_numbers);
-
- return RespondNow(WithArguments(std::move(phone_numbers)));
-}
-
-////////////////////////////////////////////////////////////////////////////////
// AutofillPrivateMaskCreditCardFunction
ExtensionFunction::ResponseAction AutofillPrivateMaskCreditCardFunction::Run() {
@@ -594,13 +451,14 @@ AutofillPrivateMigrateCreditCardsFunction::Run() {
// FormDataImporter.
autofill::AutofillManager* autofill_manager =
GetAutofillManager(GetSenderWebContents());
- if (!autofill_manager || !autofill_manager->client())
+ if (!autofill_manager) {
return RespondNow(Error(kErrorDataUnavailable));
+ }
// Get the FormDataImporter from AutofillClient. FormDataImporter owns
// LocalCardMigrationManager.
autofill::FormDataImporter* form_data_importer =
- autofill_manager->client()->GetFormDataImporter();
+ autofill_manager->client().GetFormDataImporter();
if (!form_data_importer)
return RespondNow(Error(kErrorDataUnavailable));
@@ -681,16 +539,16 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveIbanFunction::Run() {
// the Chrome payment settings page. Otherwise, leaving it blank creates a new
// IBAN.
std::string guid = iban_entry->guid ? *iban_entry->guid : "";
- const autofill::IBAN* existing_iban = nullptr;
+ const autofill::Iban* existing_iban = nullptr;
if (!guid.empty()) {
- existing_iban = personal_data->GetIBANByGUID(guid);
+ existing_iban = personal_data->GetIbanByGUID(guid);
if (!existing_iban)
return RespondNow(Error(kErrorDataUnavailable));
}
- autofill::IBAN iban =
+ autofill::Iban iban =
existing_iban
? *existing_iban
- : autofill::IBAN(base::Uuid::GenerateRandomV4().AsLowercaseString());
+ : autofill::Iban(base::Uuid::GenerateRandomV4().AsLowercaseString());
iban.SetRawInfo(autofill::IBAN_VALUE, base::UTF8ToUTF16(*iban_entry->value));
@@ -698,7 +556,7 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveIbanFunction::Run() {
iban.set_nickname(base::UTF8ToUTF16(*iban_entry->nickname));
if (guid.empty()) {
- personal_data->AddIBAN(iban);
+ personal_data->AddIban(iban);
base::RecordAction(base::UserMetricsAction("AutofillIbanAdded"));
if (!iban.nickname().empty()) {
base::RecordAction(
@@ -708,7 +566,7 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveIbanFunction::Run() {
}
if (existing_iban->Compare(iban) != 0) {
- personal_data->UpdateIBAN(iban);
+ personal_data->UpdateIban(iban);
base::RecordAction(base::UserMetricsAction("AutofillIbanEdited"));
// Record when nickname is updated.
if (existing_iban->nickname() != iban.nickname()) {
@@ -744,21 +602,7 @@ ExtensionFunction::ResponseAction AutofillPrivateIsValidIbanFunction::Run() {
api::autofill_private::IsValidIban::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(parameters);
return RespondNow(WithArguments(
- autofill::IBAN::IsValid(base::UTF8ToUTF16(parameters->iban_value))));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// AutofillPrivateGetUpiIdListFunction
-
-ExtensionFunction::ResponseAction AutofillPrivateGetUpiIdListFunction::Run() {
- autofill::PersonalDataManager* personal_data =
- autofill::PersonalDataManagerFactory::GetForProfile(
- Profile::FromBrowserContext(browser_context()));
- DCHECK(personal_data && personal_data->IsDataLoaded());
-
- return RespondNow(
- ArgumentList(api::autofill_private::GetUpiIdList::Results::Create(
- personal_data->GetUpiIds())));
+ autofill::Iban::IsValid(base::UTF8ToUTF16(parameters->iban_value))));
}
////////////////////////////////////////////////////////////////////////////////
@@ -783,17 +627,16 @@ ExtensionFunction::ResponseAction AutofillPrivateAddVirtualCardFunction::Run() {
autofill::AutofillManager* autofill_manager =
GetAutofillManager(GetSenderWebContents());
- if (!autofill_manager || !autofill_manager->client() ||
- !autofill_manager->client()->GetFormDataImporter() ||
+ if (!autofill_manager || !autofill_manager->client().GetFormDataImporter() ||
!autofill_manager->client()
- ->GetFormDataImporter()
+ .GetFormDataImporter()
->GetVirtualCardEnrollmentManager()) {
return RespondNow(Error(kErrorDataUnavailable));
}
autofill::VirtualCardEnrollmentManager* virtual_card_enrollment_manager =
autofill_manager->client()
- ->GetFormDataImporter()
+ .GetFormDataImporter()
->GetVirtualCardEnrollmentManager();
virtual_card_enrollment_manager->InitVirtualCardEnroll(
@@ -824,17 +667,16 @@ AutofillPrivateRemoveVirtualCardFunction::Run() {
autofill::AutofillManager* autofill_manager =
GetAutofillManager(GetSenderWebContents());
- if (!autofill_manager || !autofill_manager->client() ||
- !autofill_manager->client()->GetFormDataImporter() ||
+ if (!autofill_manager || !autofill_manager->client().GetFormDataImporter() ||
!autofill_manager->client()
- ->GetFormDataImporter()
+ .GetFormDataImporter()
->GetVirtualCardEnrollmentManager()) {
return RespondNow(Error(kErrorDataUnavailable));
}
autofill::VirtualCardEnrollmentManager* virtual_card_enrollment_manager =
autofill_manager->client()
- ->GetFormDataImporter()
+ .GetFormDataImporter()
->GetVirtualCardEnrollmentManager();
virtual_card_enrollment_manager->Unenroll(
@@ -856,50 +698,75 @@ AutofillPrivateAuthenticateUserAndFlipMandatoryAuthToggleFunction::Run() {
return RespondNow(Error(kErrorDeviceAuthUnavailable));
}
- // If `device_authenticator` is not available, then don't do anything.
- scoped_refptr<device_reauth::DeviceAuthenticator> device_authenticator =
- client->GetDeviceAuthenticator();
- if (!device_authenticator) {
- return RespondNow(Error(kErrorDeviceAuthUnavailable));
- }
-
- // `device_authenticator` is a scoped_refptr, so we need to keep it alive
- // until the callback that uses it is complete.
- base::OnceClosure bind_device_authenticator =
- base::DoNothingWithBoundArgs(device_authenticator);
const std::u16string message =
l10n_util::GetStringUTF16(IDS_PAYMENTS_AUTOFILL_MANDATORY_REAUTH_PROMPT);
+ // If `personal_data_manager` is not available or `IsDataLoaded` is false,
+ // then don't do anything.
+ autofill::PersonalDataManager* personal_data_manager =
+ client->GetPersonalDataManager();
+ if (!personal_data_manager || !personal_data_manager->IsDataLoaded()) {
+ return RespondNow(Error(kErrorDataUnavailable));
+ }
+
// We will be modifying the pref `kAutofillPaymentMethodsMandatoryReauth`
// asynchronously. The pref value directly correlates to the mandatory auth
// toggle.
- autofill_util::AuthenticateUser(
- device_authenticator, message,
+ // We are also logging the start of the auth flow and
+ // `!personal_data_manager->IsPaymentMethodsMandatoryReauthEnabled()` denotes
+ // if the user is either opting in or out.
+ base::RecordAction(base::UserMetricsAction(
+ "PaymentsUserAuthTriggeredForMandatoryAuthToggle"));
+ LogMandatoryReauthOptInOrOutUpdateEvent(
+ MandatoryReauthOptInOrOutSource::kSettingsPage,
+ /*opt_in=*/
+ !personal_data_manager->IsPaymentMethodsMandatoryReauthEnabled(),
+ MandatoryReauthAuthenticationFlowEvent::kFlowStarted);
+ client->GetOrCreatePaymentsMandatoryReauthManager()->AuthenticateWithMessage(
+ message,
base::BindOnce(
&AutofillPrivateAuthenticateUserAndFlipMandatoryAuthToggleFunction::
UpdateMandatoryAuthTogglePref,
- this)
- .Then(base::IgnoreArgs(std::move(bind_device_authenticator))));
- base::RecordAction(base::UserMetricsAction(
- "PaymentsUserAuthTriggeredForMandatoryAuthToggle"));
+ this));
+
return RespondNow(NoArguments());
#else
return RespondNow(Error(kErrorDeviceAuthUnavailable));
#endif // BUILDFLAG (IS_MAC) || BUILDFLAG(IS_WIN)
}
-// Update the Mandatory auth toggle pref after a successful user auth.
+// Update the Mandatory auth toggle pref and log whether the auth was successful
+// or not.
void AutofillPrivateAuthenticateUserAndFlipMandatoryAuthToggleFunction::
UpdateMandatoryAuthTogglePref(bool reauth_succeeded) {
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
- if (reauth_succeeded && browser_context()) {
- PrefService* prefs =
- Profile::FromBrowserContext(browser_context())->GetPrefs();
- autofill::prefs::SetPaymentMethodsMandatoryReauthEnabled(
- prefs, !prefs->GetBoolean(
- autofill::prefs::kAutofillPaymentMethodsMandatoryReauth));
+ content::WebContents* sender_web_contents = GetSenderWebContents();
+ if (!sender_web_contents) {
+ return;
+ }
+ autofill::ContentAutofillClient* client =
+ autofill::ContentAutofillClient::FromWebContents(sender_web_contents);
+ CHECK(client);
+ autofill::PersonalDataManager* personal_data_manager =
+ client->GetPersonalDataManager();
+ // This function is not called in incognito mode and therefore a
+ // PersonalDataManager should always exist.
+ CHECK(personal_data_manager);
+
+ // `opt_in` bool denotes whether the user is trying to opt in or out of the
+ // mandatory reauth feature. If the mandatory reauth toggle on the settings is
+ // currently enabled, then the `opt_in` bool will be false because the user is
+ // opting-out, otherwise the `opt_in` bool will be true.
+ const bool opt_in =
+ !personal_data_manager->IsPaymentMethodsMandatoryReauthEnabled();
+ LogMandatoryReauthOptInOrOutUpdateEvent(
+ MandatoryReauthOptInOrOutSource::kSettingsPage, opt_in,
+ reauth_succeeded ? MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded
+ : MandatoryReauthAuthenticationFlowEvent::kFlowFailed);
+ if (reauth_succeeded) {
base::RecordAction(base::UserMetricsAction(
"PaymentsUserAuthSuccessfulForMandatoryAuthToggle"));
+ personal_data_manager->SetPaymentMethodsMandatoryReauthEnabled(opt_in);
}
#endif
}
@@ -924,44 +791,40 @@ AutofillPrivateAuthenticateUserToEditLocalCardFunction::Run() {
return RespondNow(Error(kErrorDataUnavailable));
}
if (personal_data_manager->IsPaymentMethodsMandatoryReauthEnabled()) {
- // If `device_authenticator` is not available, then don't do anything.
- scoped_refptr<device_reauth::DeviceAuthenticator> device_authenticator =
- client->GetDeviceAuthenticator();
- if (!device_authenticator) {
- return RespondNow(Error(kErrorDeviceAuthUnavailable));
- }
-
- // `device_authenticator` is a scoped_refptr, so we need to keep it alive
- // until the callback that uses it is complete.
- base::OnceClosure bind_device_authenticator =
- base::DoNothingWithBoundArgs(device_authenticator);
const std::u16string message = l10n_util::GetStringUTF16(
IDS_PAYMENTS_AUTOFILL_EDIT_CARD_MANDATORY_REAUTH_PROMPT);
base::RecordAction(base::UserMetricsAction(
"PaymentsUserAuthTriggeredToShowEditLocalCardDialog"));
+ LogMandatoryReauthSettingsPageEditCardEvent(
+ MandatoryReauthAuthenticationFlowEvent::kFlowStarted);
// Based on the result of the auth, we will be asynchronously returning if
// the user can edit the local card.
- autofill_util::AuthenticateUser(
- device_authenticator, message,
- base::BindOnce(&AutofillPrivateAuthenticateUserToEditLocalCardFunction::
- CanShowEditDialogForLocalCard,
- this)
- .Then(base::IgnoreArgs(std::move(bind_device_authenticator))));
-
- // Due to async nature of AuthenticateWithMessage() on device authenticator
- // we use the below check to make sure we have a `Respond` captured. If we
- // didn't have this check, then we would show the edit card dialog box even
- // before the user successfully completes the auth.
+ client->GetOrCreatePaymentsMandatoryReauthManager()
+ ->AuthenticateWithMessage(
+ message,
+ base::BindOnce(
+ &AutofillPrivateAuthenticateUserToEditLocalCardFunction::
+ CanShowEditDialogForLocalCard,
+ this));
+
+ // Due to async nature of AuthenticateWithMessage() on mandatory re-auth
+ // manager we use the below check to make sure we have a `Respond` captured.
+ // If we didn't have this check, then we would show the edit card dialog box
+ // even before the user successfully completes the auth.
return did_respond() ? AlreadyResponded() : RespondLater();
}
#endif
return RespondNow(WithArguments(true));
}
-// Return the auth result for showing the edit card for local card.
+// Return the auth result for showing the edit card dialog for local card. We
+// also log whether the auth was successful or not.
void AutofillPrivateAuthenticateUserToEditLocalCardFunction::
CanShowEditDialogForLocalCard(bool can_show) {
+ LogMandatoryReauthSettingsPageEditCardEvent(
+ can_show ? MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded
+ : MandatoryReauthAuthenticationFlowEvent::kFlowFailed);
if (can_show) {
base::RecordAction(base::UserMetricsAction(
"PaymentsUserAuthSuccessfulToShowEditLocalCardDialog"));
@@ -969,4 +832,20 @@ void AutofillPrivateAuthenticateUserToEditLocalCardFunction::
Respond(WithArguments(can_show));
}
+////////////////////////////////////////////////////////////////////////////////
+// AutofillPrivateCheckIfDeviceAuthAvailableFunction
+
+ExtensionFunction::ResponseAction
+AutofillPrivateCheckIfDeviceAuthAvailableFunction::Run() {
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+ autofill::ContentAutofillClient* client =
+ autofill::ContentAutofillClient::FromWebContents(GetSenderWebContents());
+ if (client) {
+ return RespondNow(WithArguments(
+ autofill::IsDeviceAuthAvailable(client->GetDeviceAuthenticator())));
+ }
+#endif // BUILDFLAG (IS_MAC) || BUILDFLAG(IS_WIN)
+ return RespondNow(Error(kErrorDeviceAuthUnavailable));
+}
+
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h
index 8e067366973..73315366dee 100644
--- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h
+++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h
@@ -129,23 +129,6 @@ class AutofillPrivateRemoveEntryFunction : public ExtensionFunction {
ResponseAction Run() override;
};
-class AutofillPrivateValidatePhoneNumbersFunction : public ExtensionFunction {
- public:
- AutofillPrivateValidatePhoneNumbersFunction() = default;
- AutofillPrivateValidatePhoneNumbersFunction(
- const AutofillPrivateValidatePhoneNumbersFunction&) = delete;
- AutofillPrivateValidatePhoneNumbersFunction& operator=(
- const AutofillPrivateValidatePhoneNumbersFunction&) = delete;
- DECLARE_EXTENSION_FUNCTION("autofillPrivate.validatePhoneNumbers",
- AUTOFILLPRIVATE_VALIDATEPHONENUMBERS)
-
- protected:
- ~AutofillPrivateValidatePhoneNumbersFunction() override = default;
-
- // ExtensionFunction overrides.
- ResponseAction Run() override;
-};
-
class AutofillPrivateMaskCreditCardFunction : public ExtensionFunction {
public:
AutofillPrivateMaskCreditCardFunction() = default;
@@ -286,23 +269,6 @@ class AutofillPrivateIsValidIbanFunction : public ExtensionFunction {
ResponseAction Run() override;
};
-class AutofillPrivateGetUpiIdListFunction : public ExtensionFunction {
- public:
- AutofillPrivateGetUpiIdListFunction() = default;
- AutofillPrivateGetUpiIdListFunction(
- const AutofillPrivateGetUpiIdListFunction&) = delete;
- AutofillPrivateGetUpiIdListFunction& operator=(
- const AutofillPrivateGetUpiIdListFunction&) = delete;
- DECLARE_EXTENSION_FUNCTION("autofillPrivate.getUpiIdList",
- AUTOFILLPRIVATE_GETUPIIDLIST)
-
- protected:
- ~AutofillPrivateGetUpiIdListFunction() override = default;
-
- // ExtensionFunction overrides.
- ResponseAction Run() override;
-};
-
class AutofillPrivateAddVirtualCardFunction : public ExtensionFunction {
public:
AutofillPrivateAddVirtualCardFunction() = default;
@@ -383,6 +349,24 @@ class AutofillPrivateAuthenticateUserToEditLocalCardFunction
void CanShowEditDialogForLocalCard(bool can_show);
};
+class AutofillPrivateCheckIfDeviceAuthAvailableFunction
+ : public ExtensionFunction {
+ public:
+ AutofillPrivateCheckIfDeviceAuthAvailableFunction() = default;
+ AutofillPrivateCheckIfDeviceAuthAvailableFunction(
+ const AutofillPrivateCheckIfDeviceAuthAvailableFunction&) = delete;
+ AutofillPrivateCheckIfDeviceAuthAvailableFunction& operator=(
+ const AutofillPrivateCheckIfDeviceAuthAvailableFunction&) = delete;
+ DECLARE_EXTENSION_FUNCTION("autofillPrivate.checkIfDeviceAuthAvailable",
+ AUTOFILLPRIVATE_CHECKIFDEVICEAUTHAVAILABLE)
+
+ protected:
+ ~AutofillPrivateCheckIfDeviceAuthAvailableFunction() override = default;
+
+ // ExtensionFunction overrides.
+ ResponseAction Run() override;
+};
+
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_API_H_
diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api_unittest.cc
new file mode 100644
index 00000000000..58101af4364
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api_unittest.cc
@@ -0,0 +1,140 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/autofill_private/autofill_private_api.h"
+
+#include <vector>
+
+#include "chrome/browser/autofill/autofill_uitest_util.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/ui/autofill/chrome_autofill_client.h"
+#include "components/autofill/content/browser/test_autofill_client_injector.h"
+#include "components/autofill/content/browser/test_content_autofill_client.h"
+#include "components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h"
+#include "components/autofill/core/common/autofill_prefs.h"
+#include "components/device_reauth/mock_device_authenticator.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/test/browser_test.h"
+
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+using autofill::autofill_metrics::MandatoryReauthAuthenticationFlowEvent;
+
+// There are 2 boolean params set in the test suites.
+// The first param can be retrieved via `IsFeatureTurnedOn()` which determines
+// if the toggle is currently turned on or off. The second param can be
+// retrieved via `IsUserAuthSuccessful()` which determines if the user auth was
+// successful or not.
+class MandatoryReauthSettingsPageMetricsTest
+ : public extensions::ExtensionApiTest,
+ public testing::WithParamInterface<std::tuple<bool, bool>> {
+ public:
+ MandatoryReauthSettingsPageMetricsTest() = default;
+ MandatoryReauthSettingsPageMetricsTest(
+ const MandatoryReauthSettingsPageMetricsTest&) = delete;
+ MandatoryReauthSettingsPageMetricsTest& operator=(
+ const MandatoryReauthSettingsPageMetricsTest&) = delete;
+ ~MandatoryReauthSettingsPageMetricsTest() override = default;
+
+ void SetUpOnMainThread() override {
+ ExtensionApiTest::SetUpOnMainThread();
+ autofill_client()->GetPersonalDataManager()->SetPrefService(
+ autofill_client()->GetPrefs());
+ autofill_client()
+ ->GetPersonalDataManager()
+ ->SetPaymentMethodsMandatoryReauthEnabled(IsFeatureTurnedOn());
+ }
+
+ bool IsFeatureTurnedOn() const { return std::get<0>(GetParam()); }
+
+ bool IsUserAuthSuccessful() const { return std::get<1>(GetParam()); }
+
+ protected:
+ bool RunAutofillSubtest(const std::string& subtest) {
+ autofill::WaitForPersonalDataManagerToBeLoaded(profile());
+
+ const std::string extension_url = "main.html?" + subtest;
+ return RunExtensionTest("autofill_private",
+ {.extension_url = extension_url.c_str()},
+ {.load_as_component = true});
+ }
+
+ autofill::TestContentAutofillClient* autofill_client() {
+ return test_autofill_client_injector_
+ [browser()->tab_strip_model()->GetActiveWebContents()];
+ }
+
+ private:
+ autofill::TestAutofillClientInjector<autofill::TestContentAutofillClient>
+ test_autofill_client_injector_;
+};
+
+// This tests the logging for mandatory reauth opt-in / opt-out flows when
+// triggered from the settings page.
+IN_PROC_BROWSER_TEST_P(MandatoryReauthSettingsPageMetricsTest,
+ SettingsPageMandatoryReauthToggleSwitching) {
+ base::HistogramTester histogram_tester;
+
+ ON_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>(
+ autofill_client()->GetOrCreatePaymentsMandatoryReauthManager()),
+ AuthenticateWithMessage)
+ .WillByDefault(
+ testing::WithArg<1>([auth_success = IsUserAuthSuccessful()](
+ base::OnceCallback<void(bool)> callback) {
+ std::move(callback).Run(auth_success);
+ }));
+
+ RunAutofillSubtest("authenticateUserAndFlipMandatoryAuthToggle");
+
+ std::string histogram_name = base::StrCat(
+ {"Autofill.PaymentMethods.MandatoryReauth.OptChangeEvent.SettingsPage.",
+ IsFeatureTurnedOn() ? "OptOut" : "OptIn"});
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(histogram_name),
+ testing::ElementsAre(
+ base::Bucket(MandatoryReauthAuthenticationFlowEvent::kFlowStarted, 1),
+ base::Bucket(
+ IsUserAuthSuccessful()
+ ? MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded
+ : MandatoryReauthAuthenticationFlowEvent::kFlowFailed,
+ 1)));
+}
+
+IN_PROC_BROWSER_TEST_P(MandatoryReauthSettingsPageMetricsTest,
+ SettingsPageMandatoryReauthEditLocalCard) {
+ base::HistogramTester histogram_tester;
+
+ ON_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>(
+ autofill_client()->GetOrCreatePaymentsMandatoryReauthManager()),
+ AuthenticateWithMessage)
+ .WillByDefault(
+ testing::WithArg<1>([auth_success = IsUserAuthSuccessful()](
+ base::OnceCallback<void(bool)> callback) {
+ std::move(callback).Run(auth_success);
+ }));
+
+ RunAutofillSubtest("authenticateUserToEditLocalCard");
+
+ std::string histogram_name =
+ "Autofill.PaymentMethods.MandatoryReauth.AuthEvent.SettingsPage.EditCard";
+
+ std::vector<base::Bucket> expected_histogram_buckets;
+ if (IsFeatureTurnedOn()) {
+ expected_histogram_buckets = {
+ base::Bucket(MandatoryReauthAuthenticationFlowEvent::kFlowStarted, 1),
+ base::Bucket(
+ IsUserAuthSuccessful()
+ ? MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded
+ : MandatoryReauthAuthenticationFlowEvent::kFlowFailed,
+ 1)};
+ }
+
+ EXPECT_EQ(histogram_tester.GetAllSamples(histogram_name),
+ expected_histogram_buckets);
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+ MandatoryReauthSettingsPageMetricsTest,
+ testing::Combine(testing::Bool(), testing::Bool()));
+#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc
index cff47530b0d..8aaeda25f32 100644
--- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc
@@ -6,8 +6,8 @@
#include <memory>
-#include "base/allocator/partition_allocator/pointers/raw_ptr.h"
#include "base/command_line.h"
+#include "base/memory/raw_ptr.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/values.h"
#include "build/build_config.h"
@@ -18,10 +18,10 @@
#include "components/autofill/content/browser/test_autofill_client_injector.h"
#include "components/autofill/content/browser/test_content_autofill_client.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/payments/test/mock_mandatory_reauth_manager.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/autofill/core/common/autofill_prefs.h"
-#include "components/device_reauth/mock_device_authenticator.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
@@ -45,6 +45,8 @@ class AutofillPrivateApiTest : public ExtensionApiTest {
void SetUpOnMainThread() override {
ExtensionApiTest::SetUpOnMainThread();
content::RunAllPendingInMessageLoop();
+ autofill_client()->GetPersonalDataManager()->SetPrefService(
+ autofill_client()->GetPrefs());
}
protected:
@@ -62,9 +64,6 @@ class AutofillPrivateApiTest : public ExtensionApiTest {
[browser()->tab_strip_model()->GetActiveWebContents()];
}
- std::unique_ptr<autofill::TestPersonalDataManager>
- test_personal_data_manager_;
-
private:
autofill::TestAutofillClientInjector<autofill::TestContentAutofillClient>
test_autofill_client_injector_;
@@ -86,10 +85,6 @@ IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, RemoveEntry) {
EXPECT_TRUE(RunAutofillSubtest("removeEntry")) << message_;
}
-IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, ValidatePhoneNumbers) {
- EXPECT_TRUE(RunAutofillSubtest("validatePhoneNumbers")) << message_;
-}
-
IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, AddAndUpdateAddress) {
EXPECT_TRUE(RunAutofillSubtest("addAndUpdateAddress")) << message_;
}
@@ -153,19 +148,20 @@ IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, isValidIban) {
IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest,
authenticateUserAndFlipMandatoryAuthToggle) {
base::UserActionTester user_action_tester;
- auto mock_device_authenticator = autofill_client()->GetDeviceAuthenticator();
+ auto* mock_mandatory_reauth_manager =
+ autofill_client()->GetOrCreatePaymentsMandatoryReauthManager();
- ON_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>(
- mock_device_authenticator.get()),
+ ON_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>(
+ mock_mandatory_reauth_manager),
AuthenticateWithMessage)
.WillByDefault(
testing::WithArg<1>([](base::OnceCallback<void(bool)> callback) {
std::move(callback).Run(true);
}));
- EXPECT_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>(
- mock_device_authenticator.get()),
- AuthenticateWithMessage(testing::_, testing::_))
+ EXPECT_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>(
+ mock_mandatory_reauth_manager),
+ AuthenticateWithMessage)
.Times(1);
EXPECT_TRUE(RunAutofillSubtest("authenticateUserAndFlipMandatoryAuthToggle"))
<< message_;
@@ -182,19 +178,20 @@ IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest,
autofill_client()
->GetPersonalDataManager()
->SetPaymentMethodsMandatoryReauthEnabled(true);
- auto mock_device_authenticator = autofill_client()->GetDeviceAuthenticator();
+ auto* mock_mandatory_reauth_manager =
+ autofill_client()->GetOrCreatePaymentsMandatoryReauthManager();
- ON_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>(
- mock_device_authenticator.get()),
+ ON_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>(
+ mock_mandatory_reauth_manager),
AuthenticateWithMessage)
.WillByDefault(
testing::WithArg<1>([](base::OnceCallback<void(bool)> callback) {
std::move(callback).Run(true);
}));
- EXPECT_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>(
- mock_device_authenticator.get()),
- AuthenticateWithMessage(testing::_, testing::_))
+ EXPECT_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>(
+ mock_mandatory_reauth_manager),
+ AuthenticateWithMessage)
.Times(1);
EXPECT_TRUE(RunAutofillSubtest("authenticateUserToEditLocalCard"))
<< message_;
diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc
index 9380de9af1b..44fd4d2a245 100644
--- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc
+++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc
@@ -100,9 +100,4 @@ void AutofillPrivateEventRouter::BroadcastCurrentData() {
event_router_->BroadcastEvent(std::move(extension_event));
}
-AutofillPrivateEventRouter* AutofillPrivateEventRouter::Create(
- content::BrowserContext* context) {
- return new AutofillPrivateEventRouter(context);
-}
-
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h
index d8e01b94c14..7685c65d86a 100644
--- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h
+++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h
@@ -27,16 +27,14 @@ class AutofillPrivateEventRouter :
public EventRouter::Observer,
public autofill::PersonalDataManagerObserver {
public:
- static AutofillPrivateEventRouter* Create(
- content::BrowserContext* browser_context);
+ // Uses AutofillPrivateEventRouterFactory instead.
+ explicit AutofillPrivateEventRouter(content::BrowserContext* context);
AutofillPrivateEventRouter(const AutofillPrivateEventRouter&) = delete;
AutofillPrivateEventRouter& operator=(const AutofillPrivateEventRouter&) =
delete;
~AutofillPrivateEventRouter() override = default;
protected:
- explicit AutofillPrivateEventRouter(content::BrowserContext* context);
-
// KeyedService overrides:
void Shutdown() override;
diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc
index 79d934c0ee2..674ecfec9b2 100644
--- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc
+++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc
@@ -40,10 +40,11 @@ AutofillPrivateEventRouterFactory::AutofillPrivateEventRouterFactory()
DependsOn(autofill::PersonalDataManagerFactory::GetInstance());
}
-KeyedService* AutofillPrivateEventRouterFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+AutofillPrivateEventRouterFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
// TODO(1426498): pass router's dependencies directly instead of context.
- return AutofillPrivateEventRouter::Create(context);
+ return std::make_unique<AutofillPrivateEventRouter>(context);
}
bool AutofillPrivateEventRouterFactory::
diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h
index 45ba793c6ad..3068c41f1ed 100644
--- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h
+++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h
@@ -41,7 +41,7 @@ class AutofillPrivateEventRouterFactory : public ProfileKeyedServiceFactory {
~AutofillPrivateEventRouterFactory() override = default;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc
index b06f9f98d19..a630230b9e7 100644
--- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc
+++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc
@@ -93,26 +93,32 @@ autofill_private::AddressEntry ProfileToAddressEntry(
// Add all address fields to the entry.
address.guid = profile.guid();
- address.full_names = GetList(profile, autofill::NAME_FULL);
- address.honorific =
- GetStringFromProfile(profile, autofill::NAME_HONORIFIC_PREFIX);
- address.company_name = GetStringFromProfile(profile, autofill::COMPANY_NAME);
- address.address_lines =
- GetStringFromProfile(profile, autofill::ADDRESS_HOME_STREET_ADDRESS);
- address.address_level1 =
- GetStringFromProfile(profile, autofill::ADDRESS_HOME_STATE);
- address.address_level2 =
- GetStringFromProfile(profile, autofill::ADDRESS_HOME_CITY);
- address.address_level3 =
- GetStringFromProfile(profile, autofill::ADDRESS_HOME_DEPENDENT_LOCALITY);
- address.postal_code =
- GetStringFromProfile(profile, autofill::ADDRESS_HOME_ZIP);
- address.sorting_code =
- GetStringFromProfile(profile, autofill::ADDRESS_HOME_SORTING_CODE);
- address.country_code =
- GetStringFromProfile(profile, autofill::ADDRESS_HOME_COUNTRY);
- address.phone_numbers = GetList(profile, autofill::PHONE_HOME_WHOLE_NUMBER);
- address.email_addresses = GetList(profile, autofill::EMAIL_ADDRESS);
+
+ // TODO(crbug.com/1441904): provide all available fields instead of the hard
+ // coded list of fields.
+ std::vector<autofill::ServerFieldType> field_types = {
+ autofill::NAME_FULL,
+ autofill::NAME_HONORIFIC_PREFIX,
+ autofill::COMPANY_NAME,
+ autofill::ADDRESS_HOME_STREET_ADDRESS,
+ autofill::ADDRESS_HOME_STATE,
+ autofill::ADDRESS_HOME_CITY,
+ autofill::ADDRESS_HOME_DEPENDENT_LOCALITY,
+ autofill::ADDRESS_HOME_ZIP,
+ autofill::ADDRESS_HOME_SORTING_CODE,
+ autofill::ADDRESS_HOME_COUNTRY,
+ autofill::PHONE_HOME_WHOLE_NUMBER,
+ autofill::EMAIL_ADDRESS};
+
+ base::ranges::transform(
+ field_types, back_inserter(address.fields), [&profile](auto field_type) {
+ autofill_private::AddressField field;
+ field.type = autofill_private::ParseServerFieldType(
+ FieldTypeToStringPiece(field_type));
+ field.value = GetStringFromProfile(profile, field_type);
+ return field;
+ });
+
address.language_code = profile.language_code();
// Parse |label| so that it can be used to create address metadata.
@@ -200,9 +206,10 @@ autofill_private::CreditCardEntry CreditCardToCreditCardEntry(
autofill_private::CreditCardEntry card;
// Add all credit card fields to the entry.
- card.guid = credit_card.record_type() == autofill::CreditCard::LOCAL_CARD
- ? credit_card.guid()
- : credit_card.server_id();
+ card.guid =
+ credit_card.record_type() == autofill::CreditCard::RecordType::kLocalCard
+ ? credit_card.guid()
+ : credit_card.server_id();
card.name = base::UTF16ToUTF8(
credit_card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
card.card_number =
@@ -232,9 +239,9 @@ autofill_private::CreditCardEntry CreditCardToCreditCardEntry(
card.metadata->summary_label = base::UTF16ToUTF8(label_pieces.first);
card.metadata->summary_sublabel = base::UTF16ToUTF8(label_pieces.second);
card.metadata->is_local =
- credit_card.record_type() == autofill::CreditCard::LOCAL_CARD;
- card.metadata->is_cached =
- credit_card.record_type() == autofill::CreditCard::FULL_SERVER_CARD;
+ credit_card.record_type() == autofill::CreditCard::RecordType::kLocalCard;
+ card.metadata->is_cached = credit_card.record_type() ==
+ autofill::CreditCard::RecordType::kFullServerCard;
// IsValid() checks if both card number and expiration date are valid.
// IsServerCard() checks whether there is a duplicated server card in
// |personal_data|.
@@ -242,19 +249,19 @@ autofill_private::CreditCardEntry CreditCardToCreditCardEntry(
credit_card.IsValid() && !personal_data.IsServerCard(&credit_card);
card.metadata->is_virtual_card_enrollment_eligible =
credit_card.virtual_card_enrollment_state() ==
- autofill::CreditCard::VirtualCardEnrollmentState::ENROLLED ||
+ autofill::CreditCard::VirtualCardEnrollmentState::kEnrolled ||
credit_card.virtual_card_enrollment_state() ==
autofill::CreditCard::VirtualCardEnrollmentState::
- UNENROLLED_AND_ELIGIBLE;
+ kUnenrolledAndEligible;
card.metadata->is_virtual_card_enrolled =
credit_card.virtual_card_enrollment_state() ==
- autofill::CreditCard::VirtualCardEnrollmentState::ENROLLED;
+ autofill::CreditCard::VirtualCardEnrollmentState::kEnrolled;
return card;
}
autofill_private::IbanEntry IbanToIbanEntry(
- const autofill::IBAN& iban,
+ const autofill::Iban& iban,
const autofill::PersonalDataManager& personal_data) {
autofill_private::IbanEntry iban_entry;
@@ -326,8 +333,9 @@ CreditCardEntryList GenerateCreditCardList(
IbanEntryList GenerateIbanList(
const autofill::PersonalDataManager& personal_data) {
IbanEntryList list;
- for (const autofill::IBAN* iban : personal_data.GetLocalIBANs())
+ for (const autofill::Iban* iban : personal_data.GetLocalIbans()) {
list.push_back(IbanToIbanEntry(*iban, personal_data));
+ }
return list;
}
@@ -343,7 +351,7 @@ absl::optional<api::autofill_private::AccountInfo> GetAccountInfo(
api::autofill_private::AccountInfo api_account;
api_account.email = account->email;
api_account.is_sync_enabled_for_autofill_profiles =
- personal_data.IsSyncEnabledFor(syncer::UserSelectableType::kAutofill);
+ personal_data.IsSyncFeatureEnabledForAutofill();
api_account.is_eligible_for_address_account_storage =
personal_data.IsEligibleForAddressAccountStorage();
return std::move(api_account);
diff --git a/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc b/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc
index 61db738439f..e7c8aa0b93d 100644
--- a/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -49,6 +49,7 @@
#include "ash/public/cpp/accelerators.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
+#include "ui/accessibility/ax_action_handler_registry.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/display/test/display_manager_test_api.h" // nogncheck
@@ -56,13 +57,20 @@
namespace extensions {
-namespace {
-static const char kDomain[] = "a.com";
-static const char kSitesDir[] = "automation/sites";
-static const char kGotTree[] = "got_tree";
-} // anonymous namespace
-
class AutomationApiTest : public ExtensionApiTest {
+ public:
+ void SetUpOnMainThread() override {
+ ExtensionApiTest::SetUpOnMainThread();
+ host_resolver()->AddRule("*", "127.0.0.1");
+ }
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ extensions::ExtensionApiTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitchASCII(
+ extensions::switches::kAllowlistedExtensionID,
+ "ddchlicdkolnonkihahngkmmmjnjlkkf");
+ }
+
protected:
GURL GetURLForPath(const std::string& host, const std::string& path) {
std::string port = base::NumberToString(embedded_test_server()->port());
@@ -75,6 +83,7 @@ class AutomationApiTest : public ExtensionApiTest {
}
void StartEmbeddedTestServer() {
+ static const char kSitesDir[] = "automation/sites";
base::FilePath test_data;
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data));
embedded_test_server()->ServeFilesFromDirectory(
@@ -82,19 +91,6 @@ class AutomationApiTest : public ExtensionApiTest {
ASSERT_TRUE(ExtensionApiTest::StartEmbeddedTestServer());
}
- public:
- void SetUpOnMainThread() override {
- ExtensionApiTest::SetUpOnMainThread();
- host_resolver()->AddRule("*", "127.0.0.1");
- }
-
- void SetUpCommandLine(base::CommandLine* command_line) override {
- extensions::ExtensionApiTest::SetUpCommandLine(command_line);
- command_line->AppendSwitchASCII(
- extensions::switches::kAllowlistedExtensionID,
- "ddchlicdkolnonkihahngkmmmjnjlkkf");
- }
-
base::test::ScopedFeatureList scoped_feature_list_;
};
@@ -108,6 +104,13 @@ class AutomationApiCanvasTest : public AutomationApiTest {
}
};
+#if defined(USE_AURA)
+
+namespace {
+static const char kDomain[] = "a.com";
+static const char kGotTree[] = "got_tree";
+} // anonymous namespace
+
IN_PROC_BROWSER_TEST_F(AutomationApiTest, TestRendererAccessibilityEnabled) {
StartEmbeddedTestServer();
const GURL url = GetURLForPath(kDomain, "/index.html");
@@ -170,19 +173,6 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, ImageLabels) {
EXPECT_EQ(expected_mode, accessibility_mode);
}
-// Flaky on Mac: crbug.com/1248445
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_GetTreeByTabId DISABLED_GetTreeByTabId
-#else
-#define MAYBE_GetTreeByTabId GetTreeByTabId
-#endif
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_GetTreeByTabId) {
- StartEmbeddedTestServer();
- ASSERT_TRUE(RunExtensionTest("automation/tests/tabs",
- {.extension_url = "tab_id.html"}))
- << message_;
-}
-
IN_PROC_BROWSER_TEST_F(AutomationApiTest, Events) {
StartEmbeddedTestServer();
ASSERT_TRUE(RunExtensionTest("automation/tests/tabs",
@@ -247,50 +237,6 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, TableProperties) {
// Flaky on Mac and Windows: crbug.com/1235249
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-#define MAYBE_TabsAutomationBooleanPermissions \
- DISABLED_TabsAutomationBooleanPermissions
-#else
-#define MAYBE_TabsAutomationBooleanPermissions TabsAutomationBooleanPermissions
-#endif
-IN_PROC_BROWSER_TEST_F(AutomationApiTest,
- MAYBE_TabsAutomationBooleanPermissions) {
- StartEmbeddedTestServer();
- ASSERT_TRUE(RunExtensionTest("automation/tests/tabs_automation_boolean",
- {.extension_url = "permissions.html"}))
- << message_;
-}
-
-// Flaky on Mac and Windows: crbug.com/1235249
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-#define MAYBE_TabsAutomationBooleanActions \
- DISABLED_TabsAutomationBooleanActions
-#else
-#define MAYBE_TabsAutomationBooleanActions TabsAutomationBooleanActions
-#endif
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_TabsAutomationBooleanActions) {
- StartEmbeddedTestServer();
- ASSERT_TRUE(RunExtensionTest("automation/tests/tabs_automation_boolean",
- {.extension_url = "actions.html"}))
- << message_;
-}
-
-// Flaky on Mac and Windows: crbug.com/1202710
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-#define MAYBE_TabsAutomationHostsPermissions \
- DISABLED_TabsAutomationHostsPermissions
-#else
-#define MAYBE_TabsAutomationHostsPermissions TabsAutomationHostsPermissions
-#endif
-IN_PROC_BROWSER_TEST_F(AutomationApiTest,
- MAYBE_TabsAutomationHostsPermissions) {
- StartEmbeddedTestServer();
- ASSERT_TRUE(RunExtensionTest("automation/tests/tabs_automation_hosts",
- {.extension_url = "permissions.html"}))
- << message_;
-}
-
-// Flaky on Mac and Windows: crbug.com/1235249
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
#define MAYBE_CloseTab DISABLED_CloseTab
#else
#define MAYBE_CloseTab CloseTab
@@ -302,13 +248,6 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_CloseTab) {
<< message_;
}
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, QuerySelector) {
- StartEmbeddedTestServer();
- ASSERT_TRUE(RunExtensionTest("automation/tests/tabs",
- {.extension_url = "queryselector.html"}))
- << message_;
-}
-
IN_PROC_BROWSER_TEST_F(AutomationApiTest, Find) {
StartEmbeddedTestServer();
ASSERT_TRUE(
@@ -418,12 +357,6 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, EnumValidity) {
<< message_;
}
-#if defined(USE_AURA)
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopNotRequested) {
- ASSERT_TRUE(RunExtensionTest("automation/tests/tabs",
- {.extension_url = "desktop_not_requested.html"}))
- << message_;
-}
#endif // defined(USE_AURA)
#if !defined(USE_AURA)
@@ -704,6 +637,47 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_AddRemoveEventListeners) {
{.extension_url = "add_remove_event_listeners.html"}))
<< message_;
}
+
+class AutomationApiTestWithMockedSourceRenderer
+ : public AutomationApiTest,
+ public ui::AXActionHandlerObserver {
+ protected:
+ // This method is used to intercept AXActions dispatched from extensions.
+ // Because `DispatchActionResult`, from the automation API, is only used in
+ // specific source renderers (e.g. arc++), we mock the behavior here so we can
+ // test that the behavior in the automation api works correctly.
+ void InterceptAXActions() {
+ ui::AXActionHandlerRegistry* registry =
+ ui::AXActionHandlerRegistry ::GetInstance();
+ ASSERT_TRUE(registry);
+ registry->AddObserver(this);
+ }
+
+ private:
+ // ui::AXActionHandlerObserver :
+ void PerformAction(const ui::AXActionData& action_data) override {
+ extensions::AutomationEventRouter* router =
+ extensions::AutomationEventRouter::GetInstance();
+ ASSERT_TRUE(router);
+ EXPECT_EQ(action_data.action, ax::mojom::Action::kScrollBackward);
+ router->DispatchActionResult(action_data, /*result=*/true);
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(AutomationApiTestWithMockedSourceRenderer,
+ ActionResult) {
+ StartEmbeddedTestServer();
+
+ // Intercept AXActions for this test in order to test the behavior of
+ // DispatchActionResult. Here, we mock the action logic to always return true
+ // to return to the extension test that the action was handled and that the
+ // result is true. This will make sure that the passing of messages between
+ // processes is correct.
+ InterceptAXActions();
+ ASSERT_TRUE(RunExtensionTest("automation/tests/desktop",
+ {.extension_url = "action_result.html"}))
+ << message_;
+}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_CHROMEOS)
diff --git a/chromium/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc b/chromium/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc
index 1b18b129b2e..1190aa6f7b3 100644
--- a/chromium/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc
@@ -20,6 +20,7 @@
#include "extensions/common/extension.h"
#include "extensions/common/manifest_handlers/automation.h"
#include "extensions/common/permissions/permissions_data.h"
+#include "ui/accessibility/ax_tree_id.h"
#if defined(USE_AURA)
#include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
diff --git a/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc b/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc
index 576e97007ce..bbcf1379af1 100644
--- a/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc
+++ b/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc
@@ -68,7 +68,8 @@ class BluetoothLowEnergyApiTestChromeOs : public PlatformAppBrowserTest {
ash::KioskAppManager* manager() const { return ash::KioskAppManager::Get(); }
- raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_;
+ raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh>
+ fake_user_manager_;
std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
ash::ScopedCrosSettingsTestHelper settings_helper_;
diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
index c6f74309f73..3b7a1df74d6 100644
--- a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
+++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
@@ -284,10 +284,8 @@ void BookmarkEventRouter::BookmarkNodeRemoved(
void BookmarkEventRouter::BookmarkAllUserNodesRemoved(
BookmarkModel* model,
const std::set<GURL>& removed_urls) {
- NOTREACHED();
- // TODO(shashishekhar) Currently this notification is only used on Android,
- // which does not support extensions. If Desktop needs to support this, add
- // a new event to the extensions api.
+ // TODO(crbug.com/1468324): This used to be used only on Android, but that's
+ // no longer the case. We need to implement a new event to handle this.
}
void BookmarkEventRouter::BookmarkNodeChanged(BookmarkModel* model,
diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc b/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc
index 0ded9284474..9fb90a0b518 100644
--- a/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc
+++ b/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc
@@ -124,8 +124,9 @@ void BrailleControllerImpl::WriteDots(const std::vector<uint8_t>& cells,
unsigned int row_limit = std::min(rows, cells_rows);
unsigned int col_limit = std::min(columns, cells_cols);
for (unsigned int row = 0; row < row_limit; row++) {
- for (unsigned int col = 0; col < col_limit; col++) {
- sized_cells[row * columns + col] = cells[row * cells_cols + col];
+ for (unsigned int col = 0;
+ col < col_limit && (row * columns + col) < cells.size(); col++) {
+ sized_cells[row * columns + col] = cells[row * columns + col];
}
}
diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc b/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc
index 31034fa1f0d..202e006b80a 100644
--- a/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc
@@ -171,9 +171,6 @@ ExtensionFunction::ResponseAction
BrailleDisplayPrivateWriteDotsFunction::Run() {
params_ = WriteDots::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params_);
- EXTENSION_FUNCTION_VALIDATE(
- params_->cells.size() >=
- static_cast<size_t>(params_->columns * params_->rows));
bool did_post_task = content::GetIOThreadTaskRunner({})->PostTaskAndReply(
FROM_HERE,
diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc b/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc
index c447af6d75e..262764c9880 100644
--- a/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc
@@ -141,7 +141,8 @@ class MockBrlapiConnection : public BrlapiConnection {
}
}
- raw_ptr<MockBrlapiConnectionData, ExperimentalAsh> data_;
+ raw_ptr<MockBrlapiConnectionData, LeakedDanglingUntriaged | ExperimentalAsh>
+ data_;
OnDataReadyCallback on_data_ready_;
};
@@ -189,16 +190,59 @@ IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, WriteDots) {
ASSERT_TRUE(RunExtensionTest("braille_display_private/write_dots", {},
{.load_as_component = true}))
<< message_;
- ASSERT_EQ(3U, connection_data_.written_content.size());
- const std::string expected_content(
- connection_data_.display_columns * connection_data_.display_rows, '\0');
- for (size_t i = 0; i < connection_data_.written_content.size(); ++i) {
- ASSERT_EQ(std::string(connection_data_.display_columns *
- connection_data_.display_rows,
- static_cast<char>(i)),
- connection_data_.written_content[i])
- << "String " << i << " doesn't match";
- }
+ ASSERT_EQ(4U, connection_data_.written_content.size());
+
+ // testWriteEmptyCells.
+ EXPECT_EQ(std::string(11, 0), connection_data_.written_content[0]);
+
+ // testWriteOversizedCells.
+ EXPECT_EQ(std::string(11, 1), connection_data_.written_content[1]);
+ EXPECT_EQ(std::string(11, 2), connection_data_.written_content[2]);
+
+ // testWriteUndersizedCellsNoCrash.
+ EXPECT_EQ(std::string(9, 3) + std::string(2, 0),
+ connection_data_.written_content[3]);
+}
+
+IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, WriteDotsMultiLine) {
+ connection_data_.display_columns = 20;
+ connection_data_.display_rows = 7;
+ connection_data_.cell_size = 6;
+ ASSERT_TRUE(RunExtensionTest("braille_display_private/write_dots_multi_line",
+ {}, {.load_as_component = true}))
+ << message_;
+ ASSERT_EQ(6U, connection_data_.written_content.size());
+
+ // testWriteEmptyCells.
+ EXPECT_EQ(std::string(140, 0), connection_data_.written_content[0]);
+ EXPECT_EQ(std::string(140, 0), connection_data_.written_content[1]);
+ EXPECT_EQ(std::string(140, 0), connection_data_.written_content[2]);
+
+ // testWriteOversizedCells.
+
+ // The test passes a grid of cells 19x9 on a display of 20x7. (cols x rows).
+ // Thus, the last two rows get truncated, and the last column is unfilled
+ // (0s).
+ std::string grid;
+ grid += std::string(19, 1) + std::string(1, 0);
+ grid += std::string(19, 1) + std::string(1, 0);
+ grid += std::string(19, 1) + std::string(1, 0);
+ grid += std::string(19, 1) + std::string(1, 0);
+ grid += std::string(19, 1) + std::string(1, 0);
+ grid += std::string(19, 1) + std::string(1, 0);
+ grid += std::string(19, 1) + std::string(1, 0);
+ EXPECT_EQ(grid, connection_data_.written_content[3]);
+
+ // This one is 21x8, so only the last row is truncated.
+ EXPECT_EQ(std::string(140, 2), connection_data_.written_content[4]);
+
+ // testWriteUndersizedCellsNoCrash.
+
+ // 10x2.
+ std::string grid2 = std::string(10, 3) + std::string(10, 0) +
+ std::string(10, 3) + std::string(10, 0) +
+ std::string(100, 0);
+ EXPECT_EQ(grid2, connection_data_.written_content[5]);
}
IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, KeyEvents) {
diff --git a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
index b3e797e594e..4097a526437 100644
--- a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
+++ b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
@@ -96,8 +96,9 @@ static_assert((kFilterableDataTypes &
uint64_t MaskForKey(const char* key) {
if (strcmp(key, extension_browsing_data_api_constants::kCacheKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_CACHE;
- if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0)
+ if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0) {
return content::BrowsingDataRemover::DATA_TYPE_COOKIES;
+ }
if (strcmp(key, extension_browsing_data_api_constants::kDownloadsKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS;
if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0)
@@ -311,18 +312,16 @@ ExtensionFunction::ResponseAction BrowsingDataRemoverFunction::Run() {
if (origins) {
OriginParsingResult result = ParseOrigins(*origins);
- if (result.has_value()) {
- origins_ = std::move(*result);
- } else {
+ if (!result.has_value()) {
return RespondNow(std::move(result.error()));
}
+ origins_ = std::move(*result);
} else if (exclude_origins) {
OriginParsingResult result = ParseOrigins(*exclude_origins);
- if (result.has_value()) {
- origins_ = std::move(*result);
- } else {
+ if (!result.has_value()) {
return RespondNow(std::move(result.error()));
}
+ origins_ = std::move(*result);
}
mode_ = origins ? content::BrowsingDataFilterBuilder::Mode::kDelete
: content::BrowsingDataFilterBuilder::Mode::kPreserve;
diff --git a/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc b/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
index 9e4879b642d..08cd5f764d7 100644
--- a/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
@@ -289,7 +289,7 @@ class CertificateProviderApiTest : public extensions::ExtensionApiTest {
protected:
testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
- raw_ptr<chromeos::CertificateProviderService, DanglingUntriaged>
+ raw_ptr<chromeos::CertificateProviderService, AcrossTasksDanglingUntriaged>
cert_provider_service_ = nullptr;
policy::PolicyMap policy_map_;
@@ -490,9 +490,10 @@ class CertificateProviderApiMockedExtensionTest
return certificate_data;
}
- raw_ptr<content::WebContents, DanglingUntriaged> extension_contents_ =
- nullptr;
- raw_ptr<const extensions::Extension, DanglingUntriaged> extension_ = nullptr;
+ raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged>
+ extension_contents_ = nullptr;
+ raw_ptr<const extensions::Extension, AcrossTasksDanglingUntriaged>
+ extension_ = nullptr;
base::FilePath extension_path_;
};
@@ -598,7 +599,8 @@ class CertificateProviderRequestPinTest : public CertificateProviderApiTest {
extension_ = LoadExtension(extension_path);
}
- raw_ptr<const extensions::Extension, DanglingUntriaged> extension_ = nullptr;
+ raw_ptr<const extensions::Extension, AcrossTasksDanglingUntriaged>
+ extension_ = nullptr;
std::unique_ptr<ExtensionTestMessageListener> command_request_listener_;
};
diff --git a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc
index 76cfedc631b..4f11c0749d7 100644
--- a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc
+++ b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <utility>
+#include "base/check.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
@@ -36,8 +37,12 @@
#include "chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_guest_delegate.h"
#include "chrome/browser/guest_view/web_view/chrome_web_view_guest_delegate.h"
#include "chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h"
+#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/instant_service.h"
#include "chrome/browser/search/instant_service_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/webui/devtools_ui.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/url_constants.h"
@@ -63,6 +68,8 @@
#include "pdf/buildflags.h"
#include "printing/buildflags/buildflags.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "ui/base/page_transition_types.h"
+#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -84,18 +91,11 @@
#include "chrome/browser/extensions/clipboard_extension_helper_chromeos.h"
#endif
-#if BUILDFLAG(ENABLE_PDF)
-#include "chrome/browser/ui/pdf/chrome_pdf_web_contents_helper_client.h"
-#include "components/pdf/browser/pdf_web_contents_helper.h"
-#endif
-
#if BUILDFLAG(ENABLE_PRINTING)
#include "chrome/browser/printing/printing_init.h"
#endif
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-// TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build
-// flag to #if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/supervised_user/supervised_user_extensions_delegate_impl.h"
#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
#endif
@@ -126,10 +126,6 @@ void ChromeExtensionsAPIClient::AttachWebContentsHelpers(
#if BUILDFLAG(ENABLE_PRINTING)
printing::InitializePrintingForWebContents(web_contents);
#endif
-#if BUILDFLAG(ENABLE_PDF)
- pdf::PDFWebContentsHelper::CreateForWebContentsWithClient(
- web_contents, std::make_unique<ChromePDFWebContentsHelperClient>());
-#endif
}
bool ChromeExtensionsAPIClient::ShouldHideResponseHeader(
@@ -193,17 +189,19 @@ void ChromeExtensionsAPIClient::NotifyWebRequestWithheld(
// Track down the ExtensionActionRunner and the extension. Since this is
// asynchronous, we could hit a null anywhere along the path.
- content::RenderFrameHost* rfh =
+ content::RenderFrameHost* render_frame_host =
content::RenderFrameHost::FromID(render_process_id, render_frame_id);
- if (!rfh)
+ if (!render_frame_host) {
return;
+ }
// We don't count subframes and prerendering blocked actions as yet, since
// there's no way to surface this to the user. Ignore these (which is also
// what we do for content scripts).
- if (!rfh->IsInPrimaryMainFrame())
+ if (!render_frame_host->IsInPrimaryMainFrame()) {
return;
+ }
content::WebContents* web_contents =
- content::WebContents::FromRenderFrameHost(rfh);
+ content::WebContents::FromRenderFrameHost(render_frame_host);
if (!web_contents)
return;
extensions::ExtensionActionRunner* runner =
@@ -229,7 +227,7 @@ void ChromeExtensionsAPIClient::NotifyWebRequestWithheld(
if (!extension->permissions_data()
->withheld_permissions()
.explicit_hosts()
- .MatchesURL(rfh->GetLastCommittedURL())) {
+ .MatchesURL(render_frame_host->GetLastCommittedURL())) {
return;
}
@@ -287,6 +285,20 @@ void ChromeExtensionsAPIClient::ClearActionCount(
}
}
+void ChromeExtensionsAPIClient::OpenFileUrl(
+ const GURL& file_url,
+ content::BrowserContext* browser_context) {
+ CHECK(file_url.is_valid());
+ CHECK(file_url.SchemeIsFile());
+ Profile* profile = Profile::FromBrowserContext(browser_context);
+ NavigateParams navigate_params(profile, file_url,
+ ui::PAGE_TRANSITION_FROM_API);
+ navigate_params.disposition = WindowOpenDisposition::CURRENT_TAB;
+ navigate_params.browser =
+ chrome::FindTabbedBrowser(profile, /*match_original_profiles=*/false);
+ Navigate(&navigate_params);
+}
+
AppViewGuestDelegate* ChromeExtensionsAPIClient::CreateAppViewGuestDelegate()
const {
return new ChromeAppViewGuestDelegate();
@@ -299,9 +311,8 @@ ChromeExtensionsAPIClient::CreateExtensionOptionsGuestDelegate(
}
std::unique_ptr<guest_view::GuestViewManagerDelegate>
-ChromeExtensionsAPIClient::CreateGuestViewManagerDelegate(
- content::BrowserContext* context) const {
- return std::make_unique<ChromeGuestViewManagerDelegate>(context);
+ChromeExtensionsAPIClient::CreateGuestViewManagerDelegate() const {
+ return std::make_unique<ChromeGuestViewManagerDelegate>();
}
std::unique_ptr<MimeHandlerViewGuestDelegate>
diff --git a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h
index e13b68a8713..4d47b0b1e1e 100644
--- a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h
+++ b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h
@@ -49,12 +49,13 @@ class ChromeExtensionsAPIClient : public ExtensionsAPIClient {
bool clear_badge_text) override;
void ClearActionCount(content::BrowserContext* context,
const Extension& extension) override;
+ void OpenFileUrl(const GURL& file_url,
+ content::BrowserContext* browser_context) override;
AppViewGuestDelegate* CreateAppViewGuestDelegate() const override;
ExtensionOptionsGuestDelegate* CreateExtensionOptionsGuestDelegate(
ExtensionOptionsGuest* guest) const override;
std::unique_ptr<guest_view::GuestViewManagerDelegate>
- CreateGuestViewManagerDelegate(
- content::BrowserContext* context) const override;
+ CreateGuestViewManagerDelegate() const override;
std::unique_ptr<MimeHandlerViewGuestDelegate>
CreateMimeHandlerViewGuestDelegate(
MimeHandlerViewGuest* guest) const override;
diff --git a/chromium/chrome/browser/extensions/api/commands/command_service.cc b/chromium/chrome/browser/extensions/api/commands/command_service.cc
index f57c3d1897f..294b6ce11fa 100644
--- a/chromium/chrome/browser/extensions/api/commands/command_service.cc
+++ b/chromium/chrome/browser/extensions/api/commands/command_service.cc
@@ -29,7 +29,6 @@
#include "extensions/browser/extension_function_registry.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_system.h"
-#include "extensions/browser/notification_types.h"
#include "extensions/common/api/commands/commands_handler.h"
#include "extensions/common/command.h"
#include "extensions/common/feature_switch.h"
diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_api.cc b/chromium/chrome/browser/extensions/api/content_settings/content_settings_api.cc
index af7c3a8a01f..471ff11a732 100644
--- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_api.cc
+++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_api.cc
@@ -33,8 +33,8 @@
#include "extensions/browser/api/content_settings/content_settings_helpers.h"
#include "extensions/browser/api/content_settings/content_settings_service.h"
#include "extensions/browser/api/content_settings/content_settings_store.h"
-#include "extensions/browser/extension_prefs_scope.h"
#include "extensions/browser/extension_util.h"
+#include "extensions/common/api/extension_types.h"
#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
@@ -47,6 +47,8 @@ namespace pref_helpers = extensions::preference_helpers;
namespace {
+using extensions::api::types::ChromeSettingScope;
+
bool RemoveContentType(base::Value::List& args,
ContentSettingsType* content_type) {
if (args.empty() || !args[0].is_string())
@@ -89,11 +91,11 @@ ContentSettingsContentSettingClearFunction::Run() {
return RespondNow(Error(kUnknownErrorDoNotUse));
}
- ExtensionPrefsScope scope = kExtensionPrefsScopeRegular;
+ ChromeSettingScope scope = ChromeSettingScope::kRegular;
bool incognito = false;
if (params->details.scope ==
api::content_settings::Scope::kIncognitoSessionOnly) {
- scope = kExtensionPrefsScopeIncognitoSessionOnly;
+ scope = ChromeSettingScope::kIncognitoSessionOnly;
incognito = true;
}
@@ -148,7 +150,7 @@ ContentSettingsContentSettingGetFunction::Run() {
return RespondNow(Error(extension_misc::kIncognitoErrorMessage));
HostContentSettingsMap* map;
- content_settings::CookieSettings* cookie_settings;
+ scoped_refptr<content_settings::CookieSettings> cookie_settings;
Profile* profile = Profile::FromBrowserContext(browser_context());
if (incognito) {
if (!profile->HasPrimaryOTRProfile()) {
@@ -158,13 +160,11 @@ ContentSettingsContentSettingGetFunction::Run() {
}
map = HostContentSettingsMapFactory::GetForProfile(
profile->GetPrimaryOTRProfile(/*create_if_needed=*/true));
- cookie_settings =
- CookieSettingsFactory::GetForProfile(
- profile->GetPrimaryOTRProfile(/*create_if_needed=*/true))
- .get();
+ cookie_settings = CookieSettingsFactory::GetForProfile(
+ profile->GetPrimaryOTRProfile(/*create_if_needed=*/true));
} else {
map = HostContentSettingsMapFactory::GetForProfile(profile);
- cookie_settings = CookieSettingsFactory::GetForProfile(profile).get();
+ cookie_settings = CookieSettingsFactory::GetForProfile(profile);
}
// TODO(crbug.com/1386190): Consider whether the following check should
@@ -284,11 +284,11 @@ ContentSettingsContentSettingSetFunction::Run() {
return RespondNow(Error(kUnsupportedEmbeddedException));
}
- ExtensionPrefsScope scope = kExtensionPrefsScopeRegular;
+ ChromeSettingScope scope = ChromeSettingScope::kRegular;
bool incognito = false;
if (params->details.scope ==
api::content_settings::Scope::kIncognitoSessionOnly) {
- scope = kExtensionPrefsScopeIncognitoSessionOnly;
+ scope = ChromeSettingScope::kIncognitoSessionOnly;
incognito = true;
}
@@ -307,7 +307,7 @@ ContentSettingsContentSettingSetFunction::Run() {
return RespondNow(Error(kIncognitoContextError));
}
- if (scope == kExtensionPrefsScopeIncognitoSessionOnly &&
+ if (scope == ChromeSettingScope::kIncognitoSessionOnly &&
!Profile::FromBrowserContext(browser_context())->HasPrimaryOTRProfile()) {
return RespondNow(Error(extension_misc::kIncognitoSessionOnlyErrorMessage));
}
diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc b/chromium/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
index 57dc41aa8f0..3a931619898 100644
--- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
@@ -33,7 +33,6 @@
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_manager.h"
-#include "components/permissions/permission_result.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/notification_service.h"
#include "content/public/common/content_switches.h"
@@ -245,7 +244,7 @@ class ExtensionContentSettingsApiTest : public ExtensionApiTest {
}
private:
- raw_ptr<Profile, DanglingUntriaged> profile_ = nullptr;
+ raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_ = nullptr;
std::unique_ptr<ScopedKeepAlive> keep_alive_;
std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_;
};
diff --git a/chromium/chrome/browser/extensions/api/context_menus/extension_context_menu_browsertest.cc b/chromium/chrome/browser/extensions/api/context_menus/extension_context_menu_browsertest.cc
index 14a3e245196..cc29fa3e320 100644
--- a/chromium/chrome/browser/extensions/api/context_menus/extension_context_menu_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/context_menus/extension_context_menu_browsertest.cc
@@ -154,13 +154,13 @@ class ExtensionContextMenuBrowserTest
// This creates a test menu for a page with |page_url| and |link_url|, looks
// for an extension item with the given |label|, and returns true if the item
// was found.
- bool MenuHasItemWithLabel(const GURL& page_url,
+ bool MenuHasItemWithLabel(const GURL& frame_url,
const GURL& link_url,
- const GURL& frame_url,
+ bool is_subframe,
const std::string& label) {
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), page_url, link_url,
- frame_url));
+ TestRenderViewContextMenu::Create(GetWebContents(), frame_url, link_url,
+ is_subframe));
return MenuHasExtensionItemWithLabel(menu.get(), label);
}
@@ -329,8 +329,7 @@ class ExtensionContextMenuLazyTest
// Create and build our test context menu.
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
- GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), page_url));
// Look for the extension item in the menu, and make sure it's |enabled|.
int command_id = ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0);
@@ -368,8 +367,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, Simple) {
// Create and build our test context menu.
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
- GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), page_url));
// Look for the extension item in the menu, and execute it.
int command_id = ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0);
@@ -431,8 +429,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuPersistentTest, UpdateOnclick) {
// Create and build our test context menu.
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
- GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), page_url));
// Look for the extension item in the menu, and execute it.
MenuItem::Id id(false, MenuItem::ExtensionKey(extension->id()));
@@ -448,8 +445,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuPersistentTest, UpdateOnclick) {
ASSERT_TRUE(listener_update2.WaitUntilSatisfied());
// Rebuild the context menu and click on the second extension item.
- menu = TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
- GURL());
+ menu = TestRenderViewContextMenu::Create(GetWebContents(), page_url);
id.string_uid = "id2";
ASSERT_TRUE(FindCommandId(menu.get(), id, &command_id));
menu->ExecuteCommand(command_id, 0);
@@ -489,8 +485,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest,
// Create and build our test context menu.
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
- GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), page_url));
VerifyRadioItemSelectionState(menu.get(), extension->id(), "radio1", true);
VerifyRadioItemSelectionState(menu.get(), extension->id(), "radio2", false);
@@ -538,8 +533,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest,
// Create and build our test context menu.
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
- GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), page_url));
VerifyRadioItemSelectionState(menu.get(), extension->id(), "radio1", true);
VerifyRadioItemSelectionState(menu.get(), extension->id(), "radio2", false);
@@ -575,17 +569,17 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, Patterns) {
// Check that a document url that should match the items' patterns appears.
GURL google_url("http://www.google.com");
- ASSERT_TRUE(MenuHasItemWithLabel(google_url, GURL(), GURL(),
+ ASSERT_TRUE(MenuHasItemWithLabel(google_url, GURL(), false,
std::string("test_item1")));
- ASSERT_TRUE(MenuHasItemWithLabel(google_url, GURL(), GURL(),
+ ASSERT_TRUE(MenuHasItemWithLabel(google_url, GURL(), false,
std::string("test_item2")));
// Now check with a non-matching url.
GURL test_url("http://www.test.com");
- ASSERT_FALSE(MenuHasItemWithLabel(test_url, GURL(), GURL(),
- std::string("test_item1")));
- ASSERT_FALSE(MenuHasItemWithLabel(test_url, GURL(), GURL(),
- std::string("test_item2")));
+ ASSERT_FALSE(
+ MenuHasItemWithLabel(test_url, GURL(), false, std::string("test_item1")));
+ ASSERT_FALSE(
+ MenuHasItemWithLabel(test_url, GURL(), false, std::string("test_item2")));
}
// Tests registering an item with a very long title that should get truncated in
@@ -608,7 +602,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, LongTitle) {
// truncated.
GURL url("http://foo.com/");
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), url));
std::u16string label;
ASSERT_TRUE(GetItemLabel(menu.get(), item->id(), &label));
@@ -654,7 +648,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, TopLevel) {
GURL url("http://foo.com/");
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), url));
size_t index = 0;
MenuModel* model = nullptr;
@@ -742,7 +736,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuPersistentTest, Separators) {
GURL url("http://www.google.com/");
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), url));
// The top-level item should be an "automagic parent" with the extension's
// name.
@@ -767,8 +761,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuPersistentTest, Separators) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL(extension->GetResourceURL("test2.html"))));
EXPECT_TRUE(listener2.WaitUntilSatisfied());
- menu =
- TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL());
+ menu = TestRenderViewContextMenu::Create(GetWebContents(), url);
ASSERT_TRUE(menu->GetMenuModelAndItemIndex(
ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0),
&model,
@@ -791,14 +784,14 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, TargetURLs) {
// No target url - the item should not appear.
ASSERT_FALSE(
- MenuHasItemWithLabel(google_url, GURL(), GURL(), std::string("item1")));
+ MenuHasItemWithLabel(google_url, GURL(), false, std::string("item1")));
// A matching target url - the item should appear.
- ASSERT_TRUE(MenuHasItemWithLabel(google_url, google_url, GURL(),
+ ASSERT_TRUE(MenuHasItemWithLabel(google_url, google_url, false,
std::string("item1")));
// A non-matching target url - the item should not appear.
- ASSERT_FALSE(MenuHasItemWithLabel(google_url, non_google_url, GURL(),
+ ASSERT_FALSE(MenuHasItemWithLabel(google_url, non_google_url, false,
std::string("item1")));
}
@@ -832,13 +825,11 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuSWTest, IncognitoSplit) {
// Create and build our test context menu.
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
- GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), page_url));
WebContents* incognito_web_contents =
browser_incognito->tab_strip_model()->GetActiveWebContents();
std::unique_ptr<TestRenderViewContextMenu> menu_incognito(
- TestRenderViewContextMenu::Create(incognito_web_contents, page_url,
- GURL(), GURL()));
+ TestRenderViewContextMenu::Create(incognito_web_contents, page_url));
// Look for the extension item in the menu, and execute it.
int command_id = ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0);
@@ -864,18 +855,16 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, Frames) {
ASSERT_TRUE(listener.WaitUntilSatisfied());
GURL page_url("http://www.google.com");
- GURL no_frame_url;
- GURL frame_url("http://www.google.com");
-
- ASSERT_TRUE(MenuHasItemWithLabel(page_url, GURL(), no_frame_url,
- std::string("Page item")));
- ASSERT_FALSE(MenuHasItemWithLabel(page_url, GURL(), no_frame_url,
- std::string("Frame item")));
-
- ASSERT_TRUE(MenuHasItemWithLabel(page_url, GURL(), frame_url,
- std::string("Page item")));
- ASSERT_TRUE(MenuHasItemWithLabel(page_url, GURL(), frame_url,
- std::string("Frame item")));
+
+ ASSERT_TRUE(
+ MenuHasItemWithLabel(page_url, GURL(), false, std::string("Page item")));
+ ASSERT_FALSE(
+ MenuHasItemWithLabel(page_url, GURL(), false, std::string("Frame item")));
+
+ ASSERT_TRUE(
+ MenuHasItemWithLabel(page_url, GURL(), true, std::string("Page item")));
+ ASSERT_TRUE(
+ MenuHasItemWithLabel(page_url, GURL(), true, std::string("Frame item")));
}
// Tests that info.frameId is correctly set when the context menu is invoked.
@@ -932,18 +921,17 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, EventPage) {
host_helper.WaitForHostDestroyed();
// Test that menu items appear while the page is unloaded.
- ASSERT_TRUE(MenuHasItemWithLabel(
- about_blank, GURL(), GURL(), std::string("Item 1")));
- ASSERT_TRUE(MenuHasItemWithLabel(
- about_blank, GURL(), GURL(), std::string("Checkbox 1")));
+ ASSERT_TRUE(
+ MenuHasItemWithLabel(about_blank, GURL(), false, std::string("Item 1")));
+ ASSERT_TRUE(MenuHasItemWithLabel(about_blank, GURL(), false,
+ std::string("Checkbox 1")));
// Test that checked menu items retain their checkedness.
extensions::ExtensionHostTestHelper checkbox_checked(profile());
host_helper.RestrictToType(
extensions::mojom::ViewType::kExtensionBackgroundPage);
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), about_blank, GURL(),
- GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), about_blank));
MenuItem::Id id(false, MenuItem::ExtensionKey(extension->id()));
id.string_uid = "checkbox1";
@@ -1003,8 +991,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, UpdateCheckboxes) {
// Create and build our test context menu.
std::unique_ptr<TestRenderViewContextMenu> menu(
- TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
- GURL()));
+ TestRenderViewContextMenu::Create(GetWebContents(), page_url));
VerifyRadioItemSelectionState(menu.get(), extension->id(), "checkbox1",
false);
diff --git a/chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc b/chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc
index 5f76f554f0f..f30d5c0283a 100644
--- a/chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc
@@ -92,7 +92,7 @@ class CrashReportPrivateApiTest : public ExtensionApiTest {
const absl::optional<MockCrashEndpoint::Report>& last_report() {
return crash_endpoint_->last_report();
}
- raw_ptr<const Extension, ExperimentalAsh> extension_;
+ raw_ptr<const Extension, DanglingUntriaged | ExperimentalAsh> extension_;
std::unique_ptr<MockCrashEndpoint> crash_endpoint_;
std::unique_ptr<ScopedMockChromeJsErrorReportProcessor> processor_;
};
@@ -327,7 +327,7 @@ IN_PROC_BROWSER_TEST_P(CrashReportPrivateCalledFromSwaTest,
ASSERT_TRUE(embedded_test_server()->Started());
// Create and launch a test web app, opens in an app window.
GURL start_url = embedded_test_server()->GetURL("/test_app.html");
- auto web_app_info = std::make_unique<WebAppInstallInfo>();
+ auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
web_app_info->start_url = start_url;
web_app::AppId app_id =
web_app::test::InstallWebApp(profile(), std::move(web_app_info));
diff --git a/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc b/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc
index ee4f1e0a2d5..09694b76bb9 100644
--- a/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -61,6 +61,7 @@
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/switches.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/origin.h"
using content::DevToolsAgentHost;
@@ -163,15 +164,15 @@ bool ExtensionMayAttachToRenderFrameHost(
bool result = true;
render_frame_host->ForEachRenderFrameHostWithAction(
[&extension, extension_profile, error,
- &result](content::RenderFrameHost* rfh) {
- // If |rfh| is attached to an inner MimeHandlerViewGuest skip it.
- // This is done to fix crbug.com/1293856 because an extension cannot
- // inspect another extension.
- if (MimeHandlerViewGuest::FromRenderFrameHost(rfh)) {
+ &result](content::RenderFrameHost* render_frame_host) {
+ // If |render_frame_host| is attached to an inner MimeHandlerViewGuest
+ // skip it. This is done to fix crbug.com/1293856 because an extension
+ // cannot inspect another extension.
+ if (MimeHandlerViewGuest::FromRenderFrameHost(render_frame_host)) {
return content::RenderFrameHost::FrameIterationAction::kSkipChildren;
}
- if (rfh->GetWebUI()) {
+ if (render_frame_host->GetWebUI()) {
*error = debugger_api_constants::kRestrictedError;
result = false;
return content::RenderFrameHost::FrameIterationAction::kStop;
@@ -180,12 +181,12 @@ bool ExtensionMayAttachToRenderFrameHost(
// We check both the last committed URL and the SiteURL because this
// method may be called in the middle of a navigation where the SiteURL
// has been updated but navigation hasn't committed yet.
- if (!ExtensionMayAttachToURLOrInnerURL(extension, extension_profile,
- rfh->GetLastCommittedURL(),
- error) ||
+ if (!ExtensionMayAttachToURLOrInnerURL(
+ extension, extension_profile,
+ render_frame_host->GetLastCommittedURL(), error) ||
!ExtensionMayAttachToURLOrInnerURL(
extension, extension_profile,
- rfh->GetSiteInstance()->GetSiteURL(), error)) {
+ render_frame_host->GetSiteInstance()->GetSiteURL(), error)) {
result = false;
return content::RenderFrameHost::FrameIterationAction::kStop;
}
@@ -256,10 +257,12 @@ base::LazyInstance<AttachedClientHosts>::Leaky g_attached_client_hosts =
class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient,
public ExtensionRegistryObserver {
public:
- ExtensionDevToolsClientHost(Profile* profile,
- DevToolsAgentHost* agent_host,
- scoped_refptr<const Extension> extension,
- const Debuggee& debuggee);
+ ExtensionDevToolsClientHost(
+ Profile* profile,
+ DevToolsAgentHost* agent_host,
+ scoped_refptr<const Extension> extension,
+ absl::optional<WorkerId> extension_service_worker_id,
+ const Debuggee& debuggee);
ExtensionDevToolsClientHost(const ExtensionDevToolsClientHost&) = delete;
ExtensionDevToolsClientHost& operator=(const ExtensionDevToolsClientHost&) =
@@ -309,6 +312,10 @@ class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient,
raw_ptr<Profile> profile_;
scoped_refptr<DevToolsAgentHost> agent_host_;
scoped_refptr<const Extension> extension_;
+ // The WorkerId of the extension service worker that called attach() for this
+ // client host, if any.
+ const absl::optional<WorkerId> extension_service_worker_id_;
+
Debuggee debuggee_;
base::CallbackListSubscription on_app_terminating_subscription_;
int last_request_id_ = 0;
@@ -317,6 +324,11 @@ class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient,
api::debugger::DetachReason detach_reason_ =
api::debugger::DetachReason::kTargetClosed;
+ // A service worker keepalive used to keep the associated worker alive while
+ // this client is attached. Only used if `extension_service_worker_id_` has a
+ // value.
+ absl::optional<base::Uuid> service_worker_keepalive_;
+
// Listen to extension unloaded notification.
base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
extension_registry_observation_{this};
@@ -326,10 +338,12 @@ ExtensionDevToolsClientHost::ExtensionDevToolsClientHost(
Profile* profile,
DevToolsAgentHost* agent_host,
scoped_refptr<const Extension> extension,
+ absl::optional<WorkerId> extension_service_worker_id,
const Debuggee& debuggee)
: profile_(profile),
agent_host_(agent_host),
- extension_(std::move(extension)) {
+ extension_(std::move(extension)),
+ extension_service_worker_id_(std::move(extension_service_worker_id)) {
CopyDebuggee(&debuggee_, debuggee);
g_attached_client_hosts.Get().insert(this);
@@ -366,12 +380,38 @@ bool ExtensionDevToolsClientHost::Attach() {
extension_id(), extension_->name(),
base::BindOnce(&ExtensionDevToolsClientHost::InfoBarDestroyed,
base::Unretained(this)));
+ if (extension_service_worker_id_) {
+ ProcessManager* process_manager = ProcessManager::Get(profile_);
+ CHECK(process_manager);
+ // The service worker should definitely be registered at this point.
+ CHECK(process_manager->HasServiceWorker(*extension_service_worker_id_));
+ service_worker_keepalive_ =
+ process_manager->IncrementServiceWorkerKeepaliveCount(
+ *extension_service_worker_id_,
+ content::ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout,
+ Activity::DEBUGGER, /*extra_data=*/std::string());
+ }
+
return true;
}
ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
ExtensionDevToolsInfoBarDelegate::NotifyExtensionDetached(extension_id());
g_attached_client_hosts.Get().erase(this);
+
+ // Decrement the associated worker keepalive, if any.
+ if (service_worker_keepalive_) {
+ CHECK(extension_service_worker_id_);
+ ProcessManager* process_manager = ProcessManager::Get(profile_);
+ CHECK(process_manager);
+ // The worker may have terminated for other reasons. Only decrement the
+ // keepalive if it's still around.
+ if (process_manager->HasServiceWorker(*extension_service_worker_id_)) {
+ process_manager->DecrementServiceWorkerKeepaliveCount(
+ *extension_service_worker_id_, *service_worker_keepalive_,
+ Activity::DEBUGGER, /*extra_data=*/std::string());
+ }
+ }
}
// DevToolsAgentHostClient implementation.
@@ -677,7 +717,7 @@ ExtensionFunction::ResponseAction DebuggerAttachFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
auto host = std::make_unique<ExtensionDevToolsClientHost>(
- profile, agent_host_.get(), extension(), debuggee_);
+ profile, agent_host_.get(), extension(), worker_id(), debuggee_);
if (!host->Attach()) {
return RespondNow(Error(debugger_api_constants::kRestrictedError));
diff --git a/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc b/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc
index e0062b9b410..46b52a820b7 100644
--- a/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc
@@ -402,7 +402,9 @@ IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) {
EXPECT_EQ(1u, manager3->infobar_count());
// Closing tab should not affect anything.
- ASSERT_TRUE(another_browser->tab_strip_model()->CloseWebContentsAt(1, 0));
+ EXPECT_EQ(2, another_browser->tab_strip_model()->count());
+ another_browser->tab_strip_model()->CloseWebContentsAt(1, 0);
+ EXPECT_EQ(1, another_browser->tab_strip_model()->count());
manager3 = nullptr;
EXPECT_EQ(1u, manager1->infobar_count());
EXPECT_EQ(1u, manager2->infobar_count());
diff --git a/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar_delegate.h b/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar_delegate.h
index c00912f79d1..48fb3aad373 100644
--- a/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar_delegate.h
+++ b/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar_delegate.h
@@ -13,6 +13,7 @@
#include "base/memory/raw_ptr.h"
#include "base/timer/timer.h"
#include "components/infobars/core/confirm_infobar_delegate.h"
+#include "extensions/common/extension_id.h"
class GlobalConfirmInfoBar;
@@ -57,7 +58,7 @@ class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate {
base::CallbackListSubscription RegisterDestroyedCallback(
base::OnceClosure destroyed_callback);
- const std::string extension_id_;
+ const ExtensionId extension_id_;
const std::u16string extension_name_;
// infobar_ is set after attaching an extension and is deleted 5 seconds after
// detaching the extension. |infobar_| owns this object and is therefore
diff --git a/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc b/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc
index bc4971bc0f1..cdd579ef54a 100644
--- a/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc
@@ -158,7 +158,7 @@ class SetIconAPIPrerenderingTest : public SetIconAPITest {
private:
void SetUp() override {
- prerender_helper_.SetUp(embedded_test_server());
+ prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
ExtensionApiTest::SetUp();
}
diff --git a/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 26857eb016e..849d6bdb456 100644
--- a/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -154,10 +154,12 @@ base::Value::List VectorToList(const std::vector<T>& values) {
}
// Returns true if |window.scriptExecuted| is true for the given frame.
-bool WasFrameWithScriptLoaded(content::RenderFrameHost* rfh) {
- if (!rfh)
+bool WasFrameWithScriptLoaded(content::RenderFrameHost* render_frame_host) {
+ if (!render_frame_host) {
return false;
- return content::EvalJs(rfh, "!!window.scriptExecuted").ExtractBool();
+ }
+ return content::EvalJs(render_frame_host, "!!window.scriptExecuted")
+ .ExtractBool();
}
// Helper to wait for ruleset load in response to extension load.
@@ -2380,11 +2382,11 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
rules_1, "extension_1", {URLPattern::kAllUrlsPattern}));
- const std::string extension_id_1 = last_loaded_extension_id();
+ const ExtensionId extension_id_1 = last_loaded_extension_id();
ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
rules_2, "extension_2", {URLPattern::kAllUrlsPattern}));
- const std::string extension_id_2 = last_loaded_extension_id();
+ const ExtensionId extension_id_2 = last_loaded_extension_id();
auto get_manifest_url = [](const ExtensionId& extension_id) {
return GURL(base::StringPrintf("%s://%s/manifest.json",
@@ -6649,7 +6651,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, FledgeAuctionScripts) {
navigator.joinAdInterestGroup({
name: 'cars',
owner: $1,
- biddingLogicUrl: $2,
+ biddingLogicURL: $2,
userBiddingSignals: [],
ads: [{
renderURL: 'https://example.com/render',
@@ -6682,7 +6684,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, FledgeAuctionScripts) {
(async function() {
let config = await navigator.runAdAuction({
seller: $1,
- decisionLogicUrl: $2,
+ decisionLogicURL: $2,
interestGroupBuyers: [$1],
});
document.querySelector('fencedframe').config =
@@ -6780,21 +6782,22 @@ class DeclarativeNetRequestBackForwardCacheBrowserTest
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
// 1) Navigate to A.
- content::RenderFrameHost* rfh_a =
+ content::RenderFrameHost* render_frame_host_a =
ui_test_utils::NavigateToURL(browser(), url_a);
- auto delete_observer_rfh_a =
- std::make_unique<content::RenderFrameDeletedObserver>(rfh_a);
+ auto delete_observer_render_frame_host_a =
+ std::make_unique<content::RenderFrameDeletedObserver>(
+ render_frame_host_a);
// 2) Navigate to B.
- content::RenderFrameHost* rfh_b =
+ content::RenderFrameHost* render_frame_host_b =
ui_test_utils::NavigateToURL(browser(), url_b);
- // Ensure that |rfh_a| is in the cache.
- EXPECT_FALSE(delete_observer_rfh_a->deleted());
- EXPECT_NE(rfh_a, rfh_b);
- EXPECT_EQ(rfh_a->GetLifecycleState(),
+ // Ensure that |render_frame_host_a| is in the cache.
+ EXPECT_FALSE(delete_observer_render_frame_host_a->deleted());
+ EXPECT_NE(render_frame_host_a, render_frame_host_b);
+ EXPECT_EQ(render_frame_host_a->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kInBackForwardCache);
- return delete_observer_rfh_a;
+ return delete_observer_render_frame_host_a;
}
private:
@@ -6811,16 +6814,17 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest,
rule.condition->url_filter = std::string("script.js");
ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
- auto bfcache_rfh_delete_observer = NavigateForBackForwardCache();
+ auto bfcache_render_frame_host_delete_observer =
+ NavigateForBackForwardCache();
const ExtensionId extension_id = last_loaded_extension_id();
// Add dynamic rule.
rule.condition->url_filter = std::string("dynamic.com");
ASSERT_NO_FATAL_FAILURE(AddDynamicRules(extension_id, {rule}));
- // Expect that |rfh_a| is destroyed as the cache would get cleared due to
- // addition of new rule.
- bfcache_rfh_delete_observer->WaitUntilDeleted();
+ // Expect that |render_frame_host_a| is destroyed as the cache would get
+ // cleared due to addition of new rule.
+ bfcache_render_frame_host_delete_observer->WaitUntilDeleted();
}
// Ensure that Back Forward is cleared on updating session rules.
@@ -6833,16 +6837,17 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest,
rule.condition->url_filter = std::string("script.js");
ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
- auto bfcache_rfh_delete_observer = NavigateForBackForwardCache();
+ auto bfcache_render_frame_host_delete_observer =
+ NavigateForBackForwardCache();
const ExtensionId extension_id = last_loaded_extension_id();
// Add session-scoped rule to block requests to "session.example".
rule.condition->url_filter = std::string("session.example");
ASSERT_NO_FATAL_FAILURE(UpdateSessionRules(extension_id, {}, {rule}));
- // Expect that |rfh_a| is destroyed as the cache would get cleared due to
- // addition of new rule.
- bfcache_rfh_delete_observer->WaitUntilDeleted();
+ // Expect that |render_frame_host_a| is destroyed as the cache would get
+ // cleared due to addition of new rule.
+ bfcache_render_frame_host_delete_observer->WaitUntilDeleted();
}
// Ensure that Back Forward is cleared on updating enabled rulesets.
@@ -6858,16 +6863,17 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest,
ASSERT_NO_FATAL_FAILURE(
LoadExtensionWithRulesets(rulesets, "test_extension", {} /* hosts */));
- auto bfcache_rfh_delete_observer = NavigateForBackForwardCache();
+ auto bfcache_render_frame_host_delete_observer =
+ NavigateForBackForwardCache();
const ExtensionId extension_id = last_loaded_extension_id();
// Enable |ruleset_2|.
ASSERT_NO_FATAL_FAILURE(
UpdateEnabledRulesets(last_loaded_extension_id(), {}, {"ruleset_2"}));
- // Expect that |rfh_a| is destroyed as the cache would get cleared due to
- // addition of new ruleset.
- bfcache_rfh_delete_observer->WaitUntilDeleted();
+ // Expect that |render_frame_host_a| is destroyed as the cache would get
+ // cleared due to addition of new ruleset.
+ bfcache_render_frame_host_delete_observer->WaitUntilDeleted();
}
// Ensure that Back Forward is cleared on new extension.
@@ -6875,16 +6881,17 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest,
BackForwardCacheClearedOnAddExtension) {
set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
- auto bfcache_rfh_delete_observer = NavigateForBackForwardCache();
+ auto bfcache_render_frame_host_delete_observer =
+ NavigateForBackForwardCache();
// Now block requests to script.js.
TestRule rule = CreateGenericRule();
rule.condition->url_filter = std::string("script.js");
ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
- // Expect that |rfh_a| is destroyed as the cache would get cleared due to
- // addition of new rule.
- bfcache_rfh_delete_observer->WaitUntilDeleted();
+ // Expect that |render_frame_host_a| is destroyed as the cache would get
+ // cleared due to addition of new rule.
+ bfcache_render_frame_host_delete_observer->WaitUntilDeleted();
}
INSTANTIATE_TEST_SUITE_P(All,
diff --git a/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc b/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
index 7f08f2e1020..0d03680a22f 100644
--- a/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
@@ -184,7 +184,8 @@ TEST_P(RulesetManagerTest, MultipleRulesets) {
ASSERT_EQ(0u, manager()->GetMatcherCountForTest());
- std::string extension_id_one, extension_id_two;
+ ExtensionId extension_id_one;
+ ExtensionId extension_id_two;
size_t expected_matcher_count = 0;
// Add the required rulesets.
diff --git a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index 362165b3186..61b0f3825cf 100644
--- a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -52,8 +52,12 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/browser/ui/extensions/extensions_dialogs.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
+#include "chrome/browser/web_applications/extension_status_utils.h"
#include "chrome/common/extensions/api/developer_private.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/pref_names.h"
@@ -87,7 +91,6 @@
#include "extensions/browser/extension_util.h"
#include "extensions/browser/file_highlighter.h"
#include "extensions/browser/management_policy.h"
-#include "extensions/browser/notification_types.h"
#include "extensions/browser/path_util.h"
#include "extensions/browser/permissions_manager.h"
#include "extensions/browser/process_manager_factory.h"
@@ -159,12 +162,19 @@ const char kCannotRepairPolicyExtension[] =
"Cannot repair a policy-installed extension.";
const char kCannotChangeHostPermissions[] =
"Cannot change host permissions for the given extension.";
+const char kCannotSetPinnedWithoutAction[] =
+ "Cannot set pinned action state for an extension with no action.";
const char kInvalidHost[] = "Invalid host.";
const char kInvalidLazyBackgroundPageParameter[] =
"isServiceWorker can not be set for lazy background page based extensions.";
const char kInvalidRenderProcessId[] =
"render_process_id can be set to -1 for only lazy background page based or "
"service-worker based extensions.";
+const char kFailToUninstallEnterpriseOrComponentExtensions[] =
+ "Cannot uninstall the enterprise or component extensions in your list.";
+const char kFailToUninstallNoneExistentExtensions[] =
+ "Cannot uninstall non-existent extensions in your list.";
+const char kUserCancelledError[] = "User cancelled uninstall";
const char kUnpackedAppsFolder[] = "apps_target";
const char kManifestFile[] = "manifest.json";
@@ -361,18 +371,6 @@ void ProcessSitesForRuntimeHostPermissions(
}
}
-// Returns the current set of granted host permissions for the extension. Note
-// that permissions that are specified but withheld will not be returned.
-std::unique_ptr<const PermissionSet> GetExtensionGrantedPermissions(
- content::BrowserContext* context,
- const scoped_refptr<const Extension>& extension) {
- ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
- const PermissionsManager* manager = PermissionsManager::Get(context);
- return manager->HasWithheldHostPermissions(*extension)
- ? prefs->GetRuntimeGrantedPermissions(extension->id())
- : prefs->GetGrantedPermissions(extension->id());
-}
-
// Updates num_extensions counts in `site_groups` for `granted_hosts` from one
// extension.
void UpdateSiteGroupCountsForExtensionHosts(
@@ -509,7 +507,8 @@ std::unique_ptr<developer::ProfileInfo> DeveloperPrivateAPI::CreateProfileInfo(
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
supervised_user::SupervisedUserService* service =
SupervisedUserServiceFactory::GetForProfile(profile);
- info->is_child_account = service->AreExtensionsPermissionsEnabled();
+ info->is_child_account =
+ service && service->AreExtensionsPermissionsEnabled();
#else
info->is_child_account = false;
#endif
@@ -542,6 +541,7 @@ void BrowserContextKeyedAPIFactory<
DependsOn(EventRouterFactory::GetInstance());
DependsOn(ExtensionSystemFactory::GetInstance());
DependsOn(PermissionsManager::GetFactory());
+ DependsOn(ToolbarActionsModelFactory::GetInstance());
}
// static
@@ -569,6 +569,7 @@ DeveloperPrivateEventRouter::DeveloperPrivateEventRouter(Profile* profile)
extension_allowlist_observer_.Observe(
ExtensionSystem::Get(profile)->extension_service()->allowlist());
permissions_manager_observation_.Observe(PermissionsManager::Get(profile));
+ toolbar_actions_model_observation_.Observe(ToolbarActionsModel::Get(profile));
pref_change_registrar_.Init(profile->GetPrefs());
// The unretained is safe, since the PrefChangeRegistrar unregisters the
// callback on destruction.
@@ -637,6 +638,12 @@ void DeveloperPrivateEventRouter::OnErrorAdded(const ExtensionError* error) {
error->extension_id());
}
+void DeveloperPrivateEventRouter::OnExtensionConfigurationChanged(
+ const std::string& extension_id) {
+ BroadcastItemStateChanged(developer::EVENT_TYPE_CONFIGURATION_CHANGED,
+ extension_id);
+}
+
void DeveloperPrivateEventRouter::OnErrorsRemoved(
const std::set<std::string>& removed_ids) {
for (const std::string& id : removed_ids) {
@@ -748,6 +755,21 @@ void DeveloperPrivateEventRouter::OnExtensionPermissionsUpdated(
extension.id());
}
+void DeveloperPrivateEventRouter::OnToolbarPinnedActionsChanged() {
+ // Currently, only enabled extensions are considered since they are the only
+ // ones that have extension actions.
+ // TODO(crbug.com/1477884): Since pinned info is stored as a pref, include
+ // disabled extensions in this event as well.
+ const ExtensionSet& extensions =
+ ExtensionRegistry::Get(profile_)->enabled_extensions();
+ for (const auto& extension : extensions) {
+ if (ui_util::ShouldDisplayInExtensionSettings(*extension)) {
+ BroadcastItemStateChanged(developer::EVENT_TYPE_PINNED_ACTIONS_CHANGED,
+ extension->id());
+ }
+ }
+}
+
void DeveloperPrivateEventRouter::OnProfilePrefChanged() {
base::Value::List args;
args.Append(DeveloperPrivateAPI::CreateProfileInfo(profile_)->ToValue());
@@ -1162,6 +1184,26 @@ DeveloperPrivateUpdateExtensionConfigurationFunction::Run() {
ExtensionPrefs::Get(browser_context())
->SetBooleanPref(extension->id(), kPrefAcknowledgeSafetyCheckWarning,
*update.acknowledge_safety_check_warning);
+ DeveloperPrivateEventRouter* event_router =
+ DeveloperPrivateAPI::Get(browser_context())
+ ->developer_private_event_router();
+ if (event_router) {
+ event_router->OnExtensionConfigurationChanged(extension->id());
+ }
+ }
+ if (update.pinned_to_toolbar) {
+ ToolbarActionsModel* toolbar_actions_model = ToolbarActionsModel::Get(
+ Profile::FromBrowserContext(browser_context()));
+ if (!toolbar_actions_model->HasAction(extension->id())) {
+ return RespondNow(Error(kCannotSetPinnedWithoutAction));
+ }
+
+ bool is_action_pinned =
+ toolbar_actions_model->IsActionPinned(extension->id());
+ if (is_action_pinned != *update.pinned_to_toolbar) {
+ toolbar_actions_model->SetActionVisibility(extension->id(),
+ !is_action_pinned);
+ }
}
return RespondNow(NoArguments());
@@ -1635,7 +1677,6 @@ ExtensionFunction::ResponseAction DeveloperPrivateLoadDirectoryFunction::Run() {
if (directory_url.is_valid() &&
directory_url.type() != storage::kFileSystemTypeLocal &&
- directory_url.type() != storage::kFileSystemTypeRestrictedLocal &&
directory_url.type() != storage::kFileSystemTypeDragged) {
return LoadByFileSystemAPI(directory_url);
}
@@ -2006,11 +2047,14 @@ DeveloperPrivateOpenDevToolsFunction::Run() {
// NOTE(devlin): Even though the properties use "render_view_id", this
// actually refers to a render frame.
- content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
- properties.render_process_id, properties.render_view_id);
+ content::RenderFrameHost* render_frame_host =
+ content::RenderFrameHost::FromID(properties.render_process_id,
+ properties.render_view_id);
content::WebContents* web_contents =
- rfh ? content::WebContents::FromRenderFrameHost(rfh) : nullptr;
+ render_frame_host
+ ? content::WebContents::FromRenderFrameHost(render_frame_host)
+ : nullptr;
// It's possible that the render frame was closed since we last updated the
// links. Handle this gracefully.
if (!web_contents)
@@ -2410,23 +2454,19 @@ DeveloperPrivateGetUserAndExtensionSitesByEtldFunction::Run() {
std::vector<scoped_refptr<const Extension>> extensions_to_check;
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
+ PermissionsManager* permissions_manager =
+ PermissionsManager::Get(browser_context());
// Note: we are only counting enabled extensions as the returned extension
// counts will reflect how many extensions can actually run on each site at
// the current moment.
for (const auto& extension : registry->enabled_extensions()) {
- // TODO(crbug.com/1331137): Some extensions can access certain sites even if
- // the user cannot modify their permissions. These also need to be added to
- // another list so the frontend knows that their site access cannot be
- // modified.
- PermissionsManager* manager = PermissionsManager::Get(browser_context());
- if (!ui_util::ShouldDisplayInExtensionSettings(*extension) ||
- !manager->CanAffectExtension(*extension)) {
+ if (!ui_util::ShouldDisplayInExtensionSettings(*extension)) {
continue;
}
std::unique_ptr<const PermissionSet> granted_permissions =
- GetExtensionGrantedPermissions(browser_context(), extension);
+ permissions_manager->GetExtensionGrantedPermissions(*extension);
std::vector<URLPattern> distinct_hosts =
ExtensionInfoGenerator::GetDistinctHosts(
granted_permissions->effective_hosts());
@@ -2445,7 +2485,7 @@ DeveloperPrivateGetUserAndExtensionSitesByEtldFunction::Run() {
// counts are accurate.
for (const auto& extension : extensions_to_check) {
std::unique_ptr<const PermissionSet> granted_permissions =
- GetExtensionGrantedPermissions(browser_context(), extension);
+ permissions_manager->GetExtensionGrantedPermissions(*extension);
UpdateSiteGroupCountsForExtensionHosts(
&site_groups, &match_subdomains_count,
granted_permissions->effective_hosts());
@@ -2492,20 +2532,22 @@ DeveloperPrivateGetMatchingExtensionsForSiteFunction::Run() {
if (parsed_site.Parse(params->site) != URLPattern::ParseResult::kSuccess)
return RespondNow(Error("Invalid site: " + params->site));
+ constexpr bool kIncludeApiPermissions = false;
+
std::vector<developer::MatchingExtensionInfo> matching_extensions;
URLPatternSet site_pattern({parsed_site});
- const ExtensionSet all_extensions =
- ExtensionRegistry::Get(browser_context())
- ->GenerateInstalledExtensionsSet(
- ExtensionRegistry::ENABLED | ExtensionRegistry::DISABLED |
- ExtensionRegistry::TERMINATED | ExtensionRegistry::BLOCKLISTED);
- for (const auto& extension : all_extensions) {
+ const ExtensionSet& enabled_extensions =
+ ExtensionRegistry::Get(browser_context())->enabled_extensions();
+ PermissionsManager* permissions_manager =
+ PermissionsManager::Get(browser_context());
+ for (const auto& extension : enabled_extensions) {
+ std::unique_ptr<const PermissionSet> granted_permissions =
+ permissions_manager->GetExtensionGrantedPermissions(*extension);
const URLPatternSet& extension_withheld_sites =
extension->permissions_data()->withheld_permissions().effective_hosts();
const URLPatternSet granted_intersection =
URLPatternSet::CreateIntersection(
- site_pattern,
- extension->permissions_data()->GetEffectiveHostPermissions(),
+ site_pattern, granted_permissions->effective_hosts(),
URLPatternSet::IntersectionBehavior::kDetailed);
const URLPatternSet withheld_intersection =
URLPatternSet::CreateIntersection(
@@ -2519,16 +2561,25 @@ DeveloperPrivateGetMatchingExtensionsForSiteFunction::Run() {
// have access to any sites that match `site_pattern`.
developer::HostAccess host_access = developer::HOST_ACCESS_ON_CLICK;
+ // TODO(crbug.com/1472899): Add a version of CanUserSelectSiteAccess to
+ // PermissionsManager which takes in a URLPattern.
+ bool can_request_all_sites =
+ granted_permissions->ShouldWarnAllHosts(kIncludeApiPermissions) ||
+ extension->permissions_data()
+ ->withheld_permissions()
+ .ShouldWarnAllHosts(kIncludeApiPermissions);
+
// If the extension has access to at least one site that matches
- // `site_pattern`, return ON_ALL_SITES or ON_SPECIFIC_SITES depending on
- // if the extension has any withheld sites.
+ // `site_pattern`, return ON_ALL_SITES if the extension can request all
+ // sites and has no withheld sites, or ON_SPECIFIC_SITES otherwise.
if (!granted_intersection.is_empty()) {
- host_access = extension_withheld_sites.is_empty()
+ host_access = can_request_all_sites && extension_withheld_sites.is_empty()
? developer::HOST_ACCESS_ON_ALL_SITES
: developer::HOST_ACCESS_ON_SPECIFIC_SITES;
}
developer::MatchingExtensionInfo matching_info;
+ matching_info.can_request_all_sites = can_request_all_sites;
matching_info.site_access = host_access;
matching_info.id = extension->id();
matching_extensions.push_back(std::move(matching_info));
@@ -2627,6 +2678,85 @@ void DeveloperPrivateUpdateSiteAccessFunction::OnSiteSettingsUpdated() {
Respond(NoArguments());
}
+DeveloperPrivateRemoveMultipleExtensionsFunction::
+ DeveloperPrivateRemoveMultipleExtensionsFunction() = default;
+DeveloperPrivateRemoveMultipleExtensionsFunction::
+ ~DeveloperPrivateRemoveMultipleExtensionsFunction() = default;
+
+ExtensionFunction::ResponseAction
+DeveloperPrivateRemoveMultipleExtensionsFunction::Run() {
+ absl::optional<developer::RemoveMultipleExtensions::Params> params =
+ developer::RemoveMultipleExtensions::Params::Create(args());
+ EXTENSION_FUNCTION_VALIDATE(params);
+ profile_ = Profile::FromBrowserContext(browser_context());
+ extension_ids_ = std::move(params->extension_ids);
+
+ // Verify the input extension list.
+ for (const auto& extension_id : extension_ids_) {
+ CHECK(profile_);
+ const Extension* current_extension =
+ ExtensionRegistry::Get(profile_)->GetExtensionById(
+ extension_id, ExtensionRegistry::EVERYTHING);
+ if (!current_extension) {
+ // Return early if the extension is a non-existent extension.
+ return RespondNow(Error(kFailToUninstallNoneExistentExtensions));
+ }
+ // If enterprise or component extensions are found, do nothing and respond
+ // with an error.
+ if (Manifest::IsComponentLocation(current_extension->location()) ||
+ Manifest::IsPolicyLocation(current_extension->location())) {
+ return RespondNow(Error(kFailToUninstallEnterpriseOrComponentExtensions));
+ }
+ }
+
+ if (accept_bubble_for_testing_.has_value()) {
+ if (*accept_bubble_for_testing_) {
+ OnDialogAccepted();
+ return AlreadyResponded();
+ }
+ return RespondNow(NoArguments());
+ }
+
+ Browser* browser = chrome::FindBrowserWithWebContents(GetSenderWebContents());
+ CHECK(browser);
+
+ ShowExtensionMultipleUninstallDialog(
+ browser->profile(), browser->window()->GetNativeWindow(), extension_ids_,
+ base::BindOnce(
+ &DeveloperPrivateRemoveMultipleExtensionsFunction::OnDialogAccepted,
+ this),
+ base::BindOnce(
+ &DeveloperPrivateRemoveMultipleExtensionsFunction::OnDialogCancelled,
+ this));
+ return RespondLater();
+}
+
+void DeveloperPrivateRemoveMultipleExtensionsFunction::OnDialogCancelled() {
+ // Let the consumer end know that the Close button was clicked.
+ Respond(Error(kUserCancelledError));
+}
+
+void DeveloperPrivateRemoveMultipleExtensionsFunction::OnDialogAccepted() {
+ for (const auto& extension_id : extension_ids_) {
+ if (!browser_context()) {
+ return;
+ }
+ const Extension* current_extension =
+ ExtensionRegistry::Get(profile_)->GetExtensionById(
+ extension_id, ExtensionRegistry::EVERYTHING);
+ // Extensions can be uninstalled externally while the dialog is open. Only
+ // uninstall extensions that are still existent.
+ if (!current_extension) {
+ continue;
+ }
+ // If an extension fails to be uninstalled, it will not pause the
+ // uninstall of the other extensions on the list.
+ ExtensionSystem::Get(profile_)->extension_service()->UninstallExtension(
+ extension_id, UNINSTALL_REASON_USER_INITIATED, nullptr);
+ }
+ Respond(NoArguments());
+}
+
} // namespace api
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h
index 3936d9649eb..c826ae40970 100644
--- a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h
+++ b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h
@@ -20,6 +20,7 @@
#include "chrome/browser/extensions/extension_uninstall_dialog.h"
#include "chrome/browser/extensions/load_error_reporter.h"
#include "chrome/browser/extensions/pack_extension_job.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/common/extensions/api/developer_private.h"
#include "chrome/common/extensions/webstore_install_result.h"
#include "components/prefs/pref_change_registrar.h"
@@ -53,7 +54,7 @@ class ExtensionInfoGenerator;
// A key that indicates whether the safety check warning for this
// extension has been acknowledged because the user has chosen to keep
// it in a past review.
-constexpr PrefMap kPrefAcknowledgeSafetyCheckWarning = {
+inline constexpr PrefMap kPrefAcknowledgeSafetyCheckWarning = {
"ack_safety_check_warning", PrefType::kBool, PrefScope::kExtensionSpecific};
namespace api {
@@ -77,7 +78,8 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver,
public ExtensionAllowlist::Observer,
public ExtensionManagement::Observer,
public WarningService::Observer,
- public PermissionsManager::Observer {
+ public PermissionsManager::Observer,
+ public ToolbarActionsModel::Observer {
public:
explicit DeveloperPrivateEventRouter(Profile* profile);
@@ -91,6 +93,10 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver,
void AddExtensionId(const std::string& extension_id);
void RemoveExtensionId(const std::string& extension_id);
+ // Called when the configuration (such as user preferences) for an extension
+ // has changed in a way that may affect the chrome://extensions UI.
+ void OnExtensionConfigurationChanged(const std::string& extension_id);
+
private:
// ExtensionRegistryObserver:
void OnExtensionLoaded(content::BrowserContext* browser_context,
@@ -154,6 +160,15 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver,
const PermissionSet& permissions,
PermissionsManager::UpdateReason reason) override;
+ // ToolbarActionsModel::Observer:
+ void OnToolbarActionAdded(const ToolbarActionsModel::ActionId& id) override {}
+ void OnToolbarActionRemoved(
+ const ToolbarActionsModel::ActionId& id) override {}
+ void OnToolbarActionUpdated(
+ const ToolbarActionsModel::ActionId& id) override {}
+ void OnToolbarModelInitialized() override {}
+ void OnToolbarPinnedActionsChanged() override;
+
// Handles a profile preference change.
void OnProfilePrefChanged();
@@ -186,6 +201,8 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver,
extension_allowlist_observer_{this};
base::ScopedObservation<PermissionsManager, PermissionsManager::Observer>
permissions_manager_observation_{this};
+ base::ScopedObservation<ToolbarActionsModel, ToolbarActionsModel::Observer>
+ toolbar_actions_model_observation_{this};
raw_ptr<Profile> profile_;
@@ -940,6 +957,44 @@ class DeveloperPrivateUpdateSiteAccessFunction
void OnSiteSettingsUpdated();
};
+class DeveloperPrivateRemoveMultipleExtensionsFunction
+ : public DeveloperPrivateAPIFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("developerPrivate.removeMultipleExtensions",
+ DEVELOPERPRIVATE_REMOVEMULTIPLEEXTENSIONS)
+ DeveloperPrivateRemoveMultipleExtensionsFunction();
+
+ DeveloperPrivateRemoveMultipleExtensionsFunction(
+ const DeveloperPrivateRemoveMultipleExtensionsFunction&) = delete;
+ DeveloperPrivateRemoveMultipleExtensionsFunction& operator=(
+ const DeveloperPrivateRemoveMultipleExtensionsFunction&) = delete;
+
+ void accept_bubble_for_testing(bool accept_bubble) {
+ accept_bubble_for_testing_ = accept_bubble;
+ }
+
+ private:
+ ~DeveloperPrivateRemoveMultipleExtensionsFunction() override;
+
+ // ExtensionFunction:
+ ResponseAction Run() override;
+
+ // A callback function to run when the user accepts the action dialog.
+ void OnDialogAccepted();
+
+ // A callback function to run when the user cancels the action dialog.
+ void OnDialogCancelled();
+
+ // The IDs of the extensions to be uninstalled.
+ std::vector<ExtensionId> extension_ids_;
+
+ raw_ptr<Profile> profile_;
+
+ // If true, immediately accept the blocked action dialog by running the
+ // callback.
+ absl::optional<bool> accept_bubble_for_testing_;
+};
+
} // namespace api
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
index c53ab8189ee..fe1672f097c 100644
--- a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
@@ -22,8 +22,10 @@
#include "base/test/gtest_util.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
+#include "chrome/browser/extensions/api/developer_private/extension_info_generator.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/error_console/error_console.h"
+#include "chrome/browser/extensions/extension_action_test_util.h"
#include "chrome/browser/extensions/extension_management.h"
#include "chrome/browser/extensions/extension_management_test_util.h"
#include "chrome/browser/extensions/extension_service.h"
@@ -34,6 +36,7 @@
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/extensions/site_permissions_helper.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/common/extensions/api/developer_private.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/test_browser_window.h"
@@ -82,6 +85,7 @@ namespace extensions {
namespace {
const char kGoodCrx[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
+const char kGoogleOnlyCrx[] = "jjlcocfpfbknlbgijblaapbcpbdglkhf";
constexpr char kInvalidHost[] = "invalid host";
constexpr char kInvalidHostError[] = "Invalid host.";
@@ -226,13 +230,17 @@ void GetMatchingExtensionsForSite(
auto MatchMatchingExtensionInfo(
const std::string& extension_id,
- const api::developer_private::HostAccess& host_access) {
+ const api::developer_private::HostAccess& host_access,
+ bool can_request_all_sites) {
return testing::AllOf(
testing::Field(&api::developer_private::MatchingExtensionInfo::id,
extension_id),
testing::Field(
&api::developer_private::MatchingExtensionInfo::site_access,
- host_access));
+ host_access),
+ testing::Field(
+ &api::developer_private::MatchingExtensionInfo::can_request_all_sites,
+ can_request_all_sites));
}
api::developer_private::ExtensionSiteAccessUpdate CreateSiteAccessUpdate(
@@ -418,18 +426,27 @@ void DeveloperPrivateApiUnitTest::TestExtensionPrefSetting(
function->set_source_context_type(Feature::WEBUI_CONTEXT);
EXPECT_TRUE(RunFunction(function, args)) << key;
EXPECT_TRUE(has_pref.Run()) << key;
+ }
+
+ {
+ base::Value::Dict parameters;
+ parameters.Set("extensionId", extension_id);
+ parameters.Set(key, false);
+
+ base::Value::List args;
+ args.Append(std::move(parameters));
ExtensionFunction::ScopedUserGestureForTests scoped_user_gesture;
- function = base::MakeRefCounted<
+ auto function = base::MakeRefCounted<
api::DeveloperPrivateUpdateExtensionConfigurationFunction>();
EXPECT_TRUE(RunFunction(function, args)) << key;
- EXPECT_TRUE(has_pref.Run()) << key;
+ EXPECT_FALSE(has_pref.Run()) << key;
}
{
base::Value::Dict parameters;
parameters.Set("extensionId", extension_id);
- parameters.Set(key, false);
+ parameters.Set(key, true);
base::Value::List args;
args.Append(std::move(parameters));
@@ -438,7 +455,7 @@ void DeveloperPrivateApiUnitTest::TestExtensionPrefSetting(
auto function = base::MakeRefCounted<
api::DeveloperPrivateUpdateExtensionConfigurationFunction>();
EXPECT_TRUE(RunFunction(function, args)) << key;
- EXPECT_FALSE(has_pref.Run()) << key;
+ EXPECT_TRUE(has_pref.Run()) << key;
}
}
@@ -516,6 +533,7 @@ void DeveloperPrivateApiUnitTest::SetUp() {
ExtensionServiceInitParams init_params;
init_params.profile_is_supervised = ProfileIsSupervised();
InitializeExtensionService(init_params);
+ extension_action_test_util::CreateToolbarModelForProfile(profile());
browser_window_ = std::make_unique<TestBrowserWindow>();
Browser::CreateParams params(profile(), true);
@@ -556,6 +574,19 @@ TEST_F(DeveloperPrivateApiUnitTest,
ScriptingPermissionsModifier(profile(), base::WrapRefCounted(extension))
.SetWithholdHostPermissions(true);
+ // Test pinning to toolbar first as this needs the extension to be enabled.
+ // The other pref settings tested below may disable the extension so it will
+ // not have an action in the toolbar.
+ auto pinned_to_toolbar = [&]() {
+ ToolbarActionsModel* toolbar_actions_model =
+ ToolbarActionsModel::Get(profile());
+ return toolbar_actions_model->HasAction(id) &&
+ toolbar_actions_model->IsActionPinned(id);
+ };
+ TestExtensionPrefSetting(base::BindLambdaForTesting(pinned_to_toolbar),
+ "pinnedToToolbar", id,
+ /*expected_default_value=*/false);
+
TestExtensionPrefSetting(
base::BindRepeating(&HasPrefsPermission, &util::IsIncognitoEnabled,
profile(), id),
@@ -2012,8 +2043,16 @@ TEST_P(DeveloperPrivateApiZipFileUnitTest, InstallDroppedFileZip) {
// Expect extension install directory to be immediate subdir of expected
// unpacked install directory. E.g. /a/b/c/d == /a/b/c + /d.
- EXPECT_EQ(extension->path(), expected_extension_install_directory_.Append(
- extension->path().BaseName()));
+ //
+ // Make sure we're comparing absolute paths to avoid failures like
+ // https://crbug.com/1453671 on macOS 14.
+ base::FilePath absolute_extension_path =
+ base::MakeAbsoluteFilePath(extension->path());
+ base::FilePath absolute_expected_extension_install_directory =
+ base::MakeAbsoluteFilePath(expected_extension_install_directory_.Append(
+ extension->path().BaseName()));
+ EXPECT_EQ(absolute_extension_path,
+ absolute_expected_extension_install_directory);
// Expect extension install directory to exist and be named with the right
// prefix.
@@ -2481,6 +2520,50 @@ TEST_F(DeveloperPrivateApiUnitTest,
}])");
}
+// Test that host permissions from policy installed extensions are included in
+// `getUserAndExtensionSitesByEtld` calls.
+TEST_F(
+ DeveloperPrivateApiUnitTest,
+ DeveloperPrivateGetUserAndExtensionSitesByEtld_PolicyControlledExtensions) {
+ std::string extension_id(kGoogleOnlyCrx);
+
+ // Set up a mock provider with a policy extension.
+ std::unique_ptr<MockExternalProvider> mock_provider =
+ std::make_unique<MockExternalProvider>(
+ service(), mojom::ManifestLocation::kExternalPolicyDownload);
+ MockExternalProvider* mock_provider_ptr = mock_provider.get();
+ AddMockExternalProvider(std::move(mock_provider));
+
+ // google_only.crx contains only a manifest.json file that requests
+ // *://www.google.com/* as a permission.
+ mock_provider_ptr->UpdateOrAddExtension(
+ extension_id, "1", data_dir().AppendASCII("google_only.crx"));
+ // Reloading extensions should find our externally registered extension
+ // and install it.
+ {
+ TestExtensionRegistryObserver observer(registry());
+ service()->CheckForExternalUpdates();
+ EXPECT_EQ(extension_id, observer.WaitForExtensionLoaded()->id());
+ }
+
+ auto function = base::MakeRefCounted<
+ api::DeveloperPrivateGetUserAndExtensionSitesByEtldFunction>();
+ EXPECT_TRUE(RunFunction(function, base::Value::List()))
+ << function->GetError();
+ const base::Value::List* results = function->GetResultListForTest();
+ ASSERT_EQ(1u, results->size());
+
+ EXPECT_THAT((*results)[0], base::test::IsJson(R"([{
+ "etldPlusOne": "google.com",
+ "numExtensions": 1,
+ "sites": [{
+ "siteSet": "EXTENSION_SPECIFIED",
+ "numExtensions": 1,
+ "site": "www.google.com",
+ }]
+ }])"));
+}
+
TEST_F(DeveloperPrivateApiUnitTest,
DeveloperPrivateGetMatchingExtensionsForSite) {
namespace developer = api::developer_private;
@@ -2504,21 +2587,19 @@ TEST_F(DeveloperPrivateApiUnitTest,
// "http://images.google.com/" should only match with `extension_2`.
EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo(
extension_2->id(),
- developer::HostAccess::HOST_ACCESS_ON_ALL_SITES)));
+ developer::HostAccess::HOST_ACCESS_ON_SPECIFIC_SITES,
+ /*can_request_all_sites=*/false)));
service()->DisableExtension(extension_2->id(),
disable_reason::DISABLE_USER_ACTION);
GetMatchingExtensionsForSite(profile(), "*://*.google.com/", &infos);
- // "*://*.google.com/" should only match with both `extension_1` and
- // `extension_2`.
- EXPECT_THAT(infos, testing::UnorderedElementsAre(
- MatchMatchingExtensionInfo(
- extension_1->id(),
- developer::HostAccess::HOST_ACCESS_ON_ALL_SITES),
- MatchMatchingExtensionInfo(
- extension_2->id(),
- developer::HostAccess::HOST_ACCESS_ON_ALL_SITES)));
+ // "*://*.google.com/" should match with `extension_1` but not `extension_2`
+ // since it is disabled.
+ EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo(
+ extension_1->id(),
+ developer::HostAccess::HOST_ACCESS_ON_SPECIFIC_SITES,
+ /*can_request_all_sites=*/false)));
}
// Test that the host access returned by GetMatchingExtensionsForSite reflects
@@ -2537,7 +2618,8 @@ TEST_F(DeveloperPrivateApiUnitTest,
EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo(
extension->id(),
- developer::HostAccess::HOST_ACCESS_ON_ALL_SITES)));
+ developer::HostAccess::HOST_ACCESS_ON_ALL_SITES,
+ /*can_request_all_sites=*/true)));
EXPECT_FALSE(PermissionsManager::Get(browser()->profile())
->HasWithheldHostPermissions(*extension));
@@ -2545,23 +2627,25 @@ TEST_F(DeveloperPrivateApiUnitTest,
modifier.SetWithholdHostPermissions(true);
GetMatchingExtensionsForSite(profile(), "http://example.com/", &infos);
- EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo(
- extension->id(),
- developer::HostAccess::HOST_ACCESS_ON_CLICK)));
+ EXPECT_THAT(infos,
+ testing::UnorderedElementsAre(MatchMatchingExtensionInfo(
+ extension->id(), developer::HostAccess::HOST_ACCESS_ON_CLICK,
+ /*can_request_all_sites=*/true)));
RunAddHostPermission(profile(), *extension, "*://*.google.com/*",
/*should_succeed=*/true, nullptr);
GetMatchingExtensionsForSite(profile(), "http://google.com/", &infos);
- EXPECT_THAT(infos,
- testing::UnorderedElementsAre(MatchMatchingExtensionInfo(
- extension->id(),
- developer::HostAccess::HOST_ACCESS_ON_SPECIFIC_SITES)));
-
- GetMatchingExtensionsForSite(profile(), "http://example.com/", &infos);
EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo(
extension->id(),
- developer::HostAccess::HOST_ACCESS_ON_CLICK)));
+ developer::HostAccess::HOST_ACCESS_ON_SPECIFIC_SITES,
+ /*can_request_all_sites=*/true)));
+
+ GetMatchingExtensionsForSite(profile(), "http://example.com/", &infos);
+ EXPECT_THAT(infos,
+ testing::UnorderedElementsAre(MatchMatchingExtensionInfo(
+ extension->id(), developer::HostAccess::HOST_ACCESS_ON_CLICK,
+ /*can_request_all_sites=*/true)));
}
// Tests the UpdateSiteAccess function when called on an extension with no
@@ -2741,6 +2825,163 @@ TEST_F(DeveloperPrivateApiUnitTest,
permissions_manager->HasGrantedHostPermission(*extension_2, kGoogleCom));
}
+// Test uninstalling multiple extensions.
+TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateRemoveMultipleExtensions) {
+ scoped_refptr<const Extension> extension_1 =
+ ExtensionBuilder("test_1").Build();
+ scoped_refptr<const Extension> extension_2 =
+ ExtensionBuilder("test_2").Build();
+ service()->AddExtension(extension_1.get());
+ service()->AddExtension(extension_2.get());
+ EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_1->id()));
+ EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_2->id()));
+
+ base::Value::List extension_ids_entries;
+ extension_ids_entries.reserve(2u);
+ extension_ids_entries.Append(extension_1->id());
+ extension_ids_entries.Append(extension_2->id());
+ std::string extension_ids_arg;
+ EXPECT_TRUE(
+ base::JSONWriter::Write(extension_ids_entries, &extension_ids_arg));
+ std::string args = base::StringPrintf(R"([%s])", extension_ids_arg.c_str());
+
+ auto function = base::MakeRefCounted<
+ api::DeveloperPrivateRemoveMultipleExtensionsFunction>();
+
+ // Accept the multiple extension uninstallation bubble by default in unit
+ // tests.
+ function->accept_bubble_for_testing(true);
+
+ // Run the private api to remove the installed extensions.
+ api_test_utils::RunFunction(function.get(), args, profile());
+
+ EXPECT_FALSE(registry()->enabled_extensions().Contains(extension_1->id()));
+ EXPECT_FALSE(registry()->enabled_extensions().Contains(extension_2->id()));
+ EXPECT_EQ(registry()->enabled_extensions().size(), 0u);
+}
+
+TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateRemoveComponentExtensions) {
+ // Create a component extension and a regular extension, then try to remove
+ // them.
+ scoped_refptr<const Extension> component_extension =
+ ExtensionBuilder("component_extension")
+ .SetLocation(mojom::ManifestLocation::kComponent)
+ .Build();
+ scoped_refptr<const Extension> test_extension =
+ ExtensionBuilder("test_extension").Build();
+ service()->AddExtension(component_extension.get());
+ service()->AddExtension(test_extension.get());
+
+ EXPECT_EQ(registry()->enabled_extensions().size(), 2u);
+
+ // Create a list of extensions with a component extension in it.
+ base::Value::List extensions_list;
+ extensions_list.reserve(2u);
+ extensions_list.Append(component_extension->id());
+ extensions_list.Append(test_extension->id());
+ std::string args;
+ EXPECT_TRUE(base::JSONWriter::Write(extensions_list, &args));
+ std::string component_args = base::StringPrintf(R"([%s])", args.c_str());
+ auto function = base::MakeRefCounted<
+ api::DeveloperPrivateRemoveMultipleExtensionsFunction>();
+
+ // Accept the multiple extension uninstallation bubble by default in unit
+ // tests.
+ function->accept_bubble_for_testing(true);
+ // Verify the error message for uninstalling component and enterprise
+ // extensions.
+ EXPECT_EQ(
+ "Cannot uninstall the enterprise or component extensions in your list.",
+ api_test_utils::RunFunctionAndReturnError(function.get(), component_args,
+ profile()));
+
+ // Because there is a component extension in the list, the uninstallation is
+ // canceled. The number of extensions remains the same.
+ EXPECT_EQ(registry()->enabled_extensions().size(), 2u);
+}
+
+TEST_F(DeveloperPrivateApiUnitTest,
+ DeveloperPrivateRemoveEnterpriseExtensions) {
+ // Create an enterprise extension and a regular extension, then try to remove
+ // them.
+ scoped_refptr<const Extension> enterprise_extension =
+ ExtensionBuilder("enterprise_extension")
+ .SetLocation(mojom::ManifestLocation::kExternalPolicy)
+ .Build();
+ scoped_refptr<const Extension> test_extension =
+ ExtensionBuilder("test_extension").Build();
+ service()->AddExtension(enterprise_extension.get());
+ service()->AddExtension(test_extension.get());
+
+ EXPECT_EQ(registry()->enabled_extensions().size(), 2u);
+
+ // Create a list of extensions with an enterprise extension in it.
+ base::Value::List extensions_list;
+ extensions_list.reserve(2u);
+ extensions_list.Append(enterprise_extension->id());
+ extensions_list.Append(test_extension->id());
+ std::string args;
+ EXPECT_TRUE(base::JSONWriter::Write(extensions_list, &args));
+ std::string enterprise_args = base::StringPrintf(R"([%s])", args.c_str());
+ auto function = base::MakeRefCounted<
+ api::DeveloperPrivateRemoveMultipleExtensionsFunction>();
+
+ // Accept the multiple extension uninstallation bubble by default in unit
+ // tests.
+ function->accept_bubble_for_testing(true);
+ // Verify the error message for uninstalling component and enterprise
+ // extensions.
+ EXPECT_EQ(
+ "Cannot uninstall the enterprise or component extensions in your list.",
+ api_test_utils::RunFunctionAndReturnError(function.get(), enterprise_args,
+ profile()));
+
+ // Because there is an enterprise extension in the list, the uninstallation is
+ // canceled. The number of extensions remains the same.
+ EXPECT_EQ(registry()->enabled_extensions().size(), 2u);
+}
+
+// Test that an event is dispatched when the list of pinned extension actions
+// has changed.
+TEST_F(DeveloperPrivateApiUnitTest,
+ ExtensionUpdatedEventOnPinnedActionsChange) {
+ // We need to call DeveloperPrivateAPI::Get() in order to instantiate the
+ // keyed service, since it's not created by default in unit tests.
+ DeveloperPrivateAPI::Get(profile());
+ EventRouter* event_router = EventRouter::Get(profile());
+
+ // The DeveloperPrivateEventRouter will only dispatch events if there's at
+ // least one listener to dispatch to. Create one.
+ const char* kEventName =
+ api::developer_private::OnItemStateChanged::kEventName;
+ event_router->AddEventListener(kEventName, render_process_host(),
+ crx_file::id_util::GenerateId("listener"));
+
+ TestEventRouterObserver test_observer(event_router);
+
+ scoped_refptr<const Extension> extension = ExtensionBuilder("test").Build();
+ service()->AddExtension(extension.get());
+ EXPECT_TRUE(registry()->enabled_extensions().Contains(extension->id()));
+
+ // The event router fetches icons from a blocking thread when sending the
+ // update event; allow it to finish before verifying the event was dispatched.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(WasItemChangedEventDispatched(
+ test_observer, extension->id(),
+ api::developer_private::EVENT_TYPE_PINNED_ACTIONS_CHANGED));
+
+ ToolbarActionsModel* toolbar_actions_model =
+ ToolbarActionsModel::Get(profile());
+
+ toolbar_actions_model->SetActionVisibility(
+ extension->id(), !toolbar_actions_model->IsActionPinned(extension->id()));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(WasItemChangedEventDispatched(
+ test_observer, extension->id(),
+ api::developer_private::EVENT_TYPE_PINNED_ACTIONS_CHANGED));
+}
+
class DeveloperPrivateApiAllowlistUnitTest
: public DeveloperPrivateApiUnitTest {
public:
diff --git a/chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc b/chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc
index fabe6549dda..2ac4fa20175 100644
--- a/chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc
@@ -130,16 +130,26 @@ IN_PROC_BROWSER_TEST_F(DeveloperPrivateApiTest, InspectEmbeddedOptionsPage) {
profile());
// Verify that dev tools opened.
- content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
- view.render_process_id, view.render_view_id);
- ASSERT_TRUE(rfh);
- content::WebContents* wc = content::WebContents::FromRenderFrameHost(rfh);
+ content::RenderFrameHost* render_frame_host =
+ content::RenderFrameHost::FromID(view.render_process_id,
+ view.render_view_id);
+ ASSERT_TRUE(render_frame_host);
+ content::WebContents* wc =
+ content::WebContents::FromRenderFrameHost(render_frame_host);
ASSERT_TRUE(wc);
EXPECT_TRUE(DevToolsWindow::GetInstanceForInspectedWebContents(wc));
}
+// TODO(https://crbug.com/1457154): Test is flaky on MSan builders.
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_InspectInactiveServiceWorkerBackground \
+ DISABLED_InspectInactiveServiceWorkerBackground
+#else
+#define MAYBE_InspectInactiveServiceWorkerBackground \
+ InspectInactiveServiceWorkerBackground
+#endif
IN_PROC_BROWSER_TEST_F(DeveloperPrivateApiTest,
- InspectInactiveServiceWorkerBackground) {
+ MAYBE_InspectInactiveServiceWorkerBackground) {
ResultCatcher result_catcher;
// Load an extension that is service worker-based.
const Extension* extension =
diff --git a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
index d1051e2fa6f..74a1d670662 100644
--- a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
+++ b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
@@ -17,6 +17,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/extensions/api/commands/command_service.h"
+#include "chrome/browser/extensions/api/developer_private/developer_private_api.h"
#include "chrome/browser/extensions/api/developer_private/inspectable_views_finder.h"
#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
#include "chrome/browser/extensions/error_console/error_console.h"
@@ -26,12 +27,12 @@
#include "chrome/browser/extensions/shared_module_service.h"
#include "chrome/browser/extensions/site_permissions_helper.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
-#include "chrome/grit/google_chrome_strings.h"
#include "components/supervised_user/core/common/pref_names.h"
#include "content/public/browser/render_frame_host.h"
#include "extensions/browser/blocklist_extension_prefs.h"
@@ -55,11 +56,13 @@
#include "extensions/common/manifest_handlers/icons_handler.h"
#include "extensions/common/manifest_handlers/offline_enabled_info.h"
#include "extensions/common/manifest_handlers/options_page_info.h"
+#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/manifest_url_handlers.h"
#include "extensions/common/permissions/permission_message_provider.h"
#include "extensions/common/permissions/permission_message_util.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/grit/extensions_browser_resources.h"
+#include "extensions/strings/grit/extensions_strings.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
@@ -142,15 +145,15 @@ developer::RuntimeError ConstructRuntimeError(const RuntimeError& error) {
developer::RuntimeError result;
PopulateErrorBase(error, &result);
switch (error.level()) {
- case logging::LOG_VERBOSE:
- case logging::LOG_INFO:
+ case logging::LOGGING_VERBOSE:
+ case logging::LOGGING_INFO:
result.severity = developer::ERROR_LEVEL_LOG;
break;
- case logging::LOG_WARNING:
+ case logging::LOGGING_WARNING:
result.severity = developer::ERROR_LEVEL_WARN;
break;
- case logging::LOG_FATAL:
- case logging::LOG_ERROR:
+ case logging::LOGGING_FATAL:
+ case logging::LOGGING_ERROR:
result.severity = developer::ERROR_LEVEL_ERROR;
break;
default:
@@ -311,6 +314,19 @@ developer::RuntimeHostPermissions CreateRuntimeHostPermissionsInfo(
return runtime_host_permissions;
}
+// Returns if the extension can access site data. This checks for host
+// permissions, activeTab and API permissions that will surface a warning for
+// all hosts access.
+bool CanAccessSiteData(PermissionsManager* permissions_manager,
+ const Extension& extension) {
+ return permissions_manager->ExtensionRequestsHostPermissionsOrActiveTab(
+ extension) ||
+ PermissionsParser::GetRequiredPermissions(&extension)
+ .ShouldWarnAllHosts() ||
+ PermissionsParser::GetOptionalPermissions(&extension)
+ .ShouldWarnAllHosts();
+}
+
// Populates the |permissions| data for the given |extension|.
void AddPermissionsInfo(content::BrowserContext* browser_context,
const Extension& extension,
@@ -331,6 +347,10 @@ void AddPermissionsInfo(content::BrowserContext* browser_context,
PermissionsManager* permissions_manager =
PermissionsManager::Get(browser_context);
+
+ permissions->can_access_site_data =
+ CanAccessSiteData(permissions_manager, extension);
+
bool enable_runtime_host_permissions =
permissions_manager->CanAffectExtension(extension);
@@ -401,6 +421,8 @@ void ExtensionInfoGenerator::CreateExtensionInfo(
state = developer::EXTENSION_STATE_DISABLED;
else if ((ext = registry->terminated_extensions().GetByID(id)) != nullptr)
state = developer::EXTENSION_STATE_TERMINATED;
+ else if ((ext = registry->blocklisted_extensions().GetByID(id)) != nullptr)
+ state = developer::EXTENSION_STATE_BLACKLISTED;
if (ext && ui_util::ShouldDisplayInExtensionSettings(*ext))
CreateExtensionInfoHelper(*ext, state);
@@ -539,7 +561,8 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper(
absl::optional<CWSInfoService::CWSInfo> cws_info =
cws_info_service_->GetCWSInfo(extension);
if (cws_info.has_value()) {
- info->safety_check_text = CreateSafetyCheckDisplayString(*cws_info);
+ info->safety_check_text =
+ CreateSafetyCheckDisplayString(*cws_info, state);
}
}
@@ -639,6 +662,11 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper(
info->incognito_access.is_active =
util::IsIncognitoEnabled(extension.id(), browser_context_);
+ // Safety check warning acknowledge status.
+ extension_prefs_->ReadPrefAsBoolean(
+ extension.id(), extensions::kPrefAcknowledgeSafetyCheckWarning,
+ &info->acknowledge_safety_check_warning);
+
// Install warnings, but only if unpacked, the error console isn't enabled
// (otherwise it shows these), and we're in developer mode (normal users don't
// need to see these).
@@ -763,6 +791,18 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper(
SitePermissionsHelper(profile).ShowAccessRequestsInToolbar(
extension.id());
+ // Pinned to toolbar.
+ // TODO(crbug.com/1477884): Currently this information is only shown for
+ // enabled extensions as only enabled extensions can have actions. However,
+ // this information can be found in prefs, so disabled extensiosn can be
+ // included as well.
+ ToolbarActionsModel* toolbar_actions_model =
+ ToolbarActionsModel::Get(profile);
+ if (toolbar_actions_model->HasAction(extension.id())) {
+ info->pinned_to_toolbar =
+ toolbar_actions_model->IsActionPinned(extension.id());
+ }
+
// The icon.
ExtensionResource icon =
IconsInfo::GetIconResource(&extension,
@@ -786,8 +826,8 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper(
developer::SafetyCheckStrings
ExtensionInfoGenerator::CreateSafetyCheckDisplayString(
- CWSInfoService::CWSInfo& cws_info) {
- // TODO(crbug.com/1432194): Add panel_page_string logic.
+ const CWSInfoService::CWSInfo& cws_info,
+ developer::ExtensionState state) {
developer::SafetyCheckStrings display_strings;
std::string detail_page_string;
std::string panel_page_string;
@@ -796,10 +836,16 @@ ExtensionInfoGenerator::CreateSafetyCheckDisplayString(
case CWSInfoService::CWSViolationType::kMalware:
detail_page_string =
l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_MALWARE);
+ panel_page_string = l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_MALWARE);
break;
case CWSInfoService::CWSViolationType::kPolicy:
detail_page_string = l10n_util::GetStringUTF8(
IDS_SAFETY_CHECK_EXTENSIONS_POLICY_VIOLATION);
+ panel_page_string = state == developer::EXTENSION_STATE_ENABLED
+ ? l10n_util::GetStringUTF8(
+ IDS_EXTENSIONS_SC_POLICY_VIOLATION_ON)
+ : l10n_util::GetStringUTF8(
+ IDS_EXTENSIONS_SC_POLICY_VIOLATION_OFF);
break;
case CWSInfoService::CWSViolationType::kNone:
case CWSInfoService::CWSViolationType::kMinorPolicy:
@@ -807,6 +853,10 @@ ExtensionInfoGenerator::CreateSafetyCheckDisplayString(
if (cws_info.unpublished_long_ago) {
detail_page_string =
l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_UNPUBLISHED);
+ panel_page_string =
+ state == developer::EXTENSION_STATE_ENABLED
+ ? l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_UNPUBLISHED_ON)
+ : l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_UNPUBLISHED_OFF);
}
break;
}
diff --git a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.h b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.h
index 619b73d07cf..d0e2d6ae5e1 100644
--- a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.h
+++ b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.h
@@ -74,17 +74,18 @@ class ExtensionInfoGenerator {
static std::vector<URLPattern> GetDistinctHosts(
const URLPatternSet& patterns);
+ // Construct the needed strings for the safety check on the
+ // extensions page.
+ static api::developer_private::SafetyCheckStrings
+ CreateSafetyCheckDisplayString(const CWSInfoService::CWSInfo& cws_info,
+ api::developer_private::ExtensionState state);
+
private:
// Creates an ExtensionInfo for the given |extension| and |state|, and
// asynchronously adds it to the |list|.
void CreateExtensionInfoHelper(const Extension& extension,
api::developer_private::ExtensionState state);
- // Construct the needed strings for the safety check on the
- // extensions page.
- static api::developer_private::SafetyCheckStrings
- CreateSafetyCheckDisplayString(CWSInfoService::CWSInfo& cws_info);
-
// Callback for the asynchronous image loading.
void OnImageLoaded(
std::unique_ptr<api::developer_private::ExtensionInfo> info,
diff --git a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
index 74dd9d5eee7..e577afbff5f 100644
--- a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
@@ -24,6 +24,7 @@
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/cws_info_service.h"
#include "chrome/browser/extensions/error_console/error_console.h"
+#include "chrome/browser/extensions/extension_action_test_util.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_service_test_with_install.h"
#include "chrome/browser/extensions/extension_util.h"
@@ -31,10 +32,11 @@
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/common/extensions/api/developer_private.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/google_chrome_strings.h"
+#include "chrome/grit/generated_resources.h"
#include "components/crx_file/id_util.h"
#include "components/supervised_user/core/common/buildflags.h"
#include "extensions/browser/extension_registry.h"
@@ -124,6 +126,7 @@ class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestWithInstall {
void SetUp() override {
ExtensionServiceTestWithInstall::SetUp();
InitializeExtensionService(GetExtensionServiceInitParams());
+ extension_action_test_util::CreateToolbarModelForProfile(profile());
}
// Returns the initialization parameters for the extension service.
@@ -141,12 +144,6 @@ class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestWithInstall {
std::move(quit_closure_).Run();
}
- api::developer_private::SafetyCheckStrings CreateSafetyCheckDisplayStringTest(
- CWSInfoService::CWSInfo& cws_info) {
- return ExtensionInfoGenerator(browser_context())
- .CreateSafetyCheckDisplayString(cws_info);
- }
-
std::unique_ptr<developer::ExtensionInfo> GenerateExtensionInfo(
const std::string& extension_id) {
std::unique_ptr<developer::ExtensionInfo> info;
@@ -211,6 +208,10 @@ class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestWithInstall {
const base::FilePath& extension_path,
mojom::ManifestLocation location) {
ChromeTestExtensionLoader loader(browser_context());
+
+ // Unit tests are single process and as such, attempting to wait for an
+ // extension renderer process will cause the test to time out.
+ loader.set_wait_for_renderers(false);
loader.set_location(location);
loader.set_creation_flags(Extension::REQUIRE_KEY);
scoped_refptr<const Extension> extension =
@@ -298,13 +299,13 @@ TEST_F(ExtensionInfoGeneratorUnitTest, BasicInfoTest) {
error_console->ReportError(std::make_unique<RuntimeError>(
extension->id(), false, u"source", u"message",
StackTrace(1, StackFrame(1, 1, u"source", u"function")), kContextUrl,
- logging::LOG_ERROR, 1, 1));
+ logging::LOGGING_ERROR, 1, 1));
error_console->ReportError(std::make_unique<ManifestError>(
extension->id(), u"message", u"key", std::u16string()));
error_console->ReportError(std::make_unique<RuntimeError>(
extension->id(), false, u"source", u"message",
StackTrace(1, StackFrame(1, 1, u"source", u"function")), kContextUrl,
- logging::LOG_WARNING, 1, 1));
+ logging::LOGGING_WARNING, 1, 1));
// It's not feasible to validate every field here, because that would be
// a duplication of the logic in the method itself. Instead, test a handful
@@ -325,6 +326,7 @@ TEST_F(ExtensionInfoGeneratorUnitTest, BasicInfoTest) {
EXPECT_TRUE(info->incognito_access.is_enabled);
EXPECT_FALSE(info->incognito_access.is_active);
EXPECT_TRUE(base::StartsWith(info->icon_url, "data:image/png;base64,"));
+ EXPECT_FALSE(*info->pinned_to_toolbar);
// Strip out the kHostReadWrite permission created by the extension requesting
// host permissions above; runtime host permissions mean these are always
@@ -357,6 +359,7 @@ TEST_F(ExtensionInfoGeneratorUnitTest, BasicInfoTest) {
++i;
}
EXPECT_TRUE(info->permissions.runtime_host_permissions);
+ EXPECT_TRUE(info->permissions.can_access_site_data);
ASSERT_EQ(2u, info->runtime_errors.size());
const api::developer_private::RuntimeError& runtime_error =
@@ -477,10 +480,11 @@ TEST_F(ExtensionInfoGeneratorUnitTest, GenerateExtensionsJSONData) {
#if !BUILDFLAG(IS_CHROMEOS_ASH)
// Test Extension2
- extension_path = data_dir().AppendASCII("good")
- .AppendASCII("Extensions")
- .AppendASCII("hpiknbiabeeppbpihjehijgoemciehgk")
- .AppendASCII("2");
+ extension_path = data_dir()
+ .AppendASCII("good")
+ .AppendASCII("Extensions")
+ .AppendASCII("hpiknbiabeeppbpihjehijgoemciehgk")
+ .AppendASCII("2");
{
// It's OK to have duplicate URLs, so long as the IDs are different.
@@ -512,27 +516,60 @@ TEST_F(ExtensionInfoGeneratorUnitTest, GenerateExtensionsJSONData) {
// Test the safety check display strings
TEST_F(ExtensionInfoGeneratorUnitTest, SafetyCheckStringsTest) {
- CWSInfoService::CWSInfo cws_info;
- cws_info.is_present = true;
- cws_info.violation_type = CWSInfoService::CWSViolationType::kMalware;
- cws_info.unpublished_long_ago = true;
- developer::SafetyCheckStrings display_strings =
- CreateSafetyCheckDisplayStringTest(cws_info);
- EXPECT_EQ(l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_MALWARE),
- display_strings.detail_string);
- cws_info.is_present = true;
- cws_info.violation_type = CWSInfoService::CWSViolationType::kPolicy;
- cws_info.unpublished_long_ago = true;
- display_strings = CreateSafetyCheckDisplayStringTest(cws_info);
- EXPECT_EQ(
- l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_POLICY_VIOLATION),
- display_strings.detail_string);
- cws_info.is_present = true;
- cws_info.violation_type = CWSInfoService::CWSViolationType::kNone;
- cws_info.unpublished_long_ago = true;
- display_strings = CreateSafetyCheckDisplayStringTest(cws_info);
- EXPECT_EQ(l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_UNPUBLISHED),
- display_strings.detail_string);
+ {
+ CWSInfoService::CWSInfo cws_info;
+ cws_info.is_present = true;
+ cws_info.violation_type = CWSInfoService::CWSViolationType::kMalware;
+ cws_info.unpublished_long_ago = true;
+ developer::SafetyCheckStrings display_strings =
+ ExtensionInfoGenerator::CreateSafetyCheckDisplayString(
+ cws_info, developer::EXTENSION_STATE_DISABLED);
+ EXPECT_EQ(l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_MALWARE),
+ display_strings.detail_string);
+ EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_MALWARE),
+ display_strings.panel_string);
+ }
+ {
+ CWSInfoService::CWSInfo cws_info;
+ cws_info.is_present = true;
+ cws_info.violation_type = CWSInfoService::CWSViolationType::kPolicy;
+ cws_info.unpublished_long_ago = true;
+ developer::SafetyCheckStrings display_strings =
+ ExtensionInfoGenerator::CreateSafetyCheckDisplayString(
+ cws_info, developer::EXTENSION_STATE_DISABLED);
+ EXPECT_EQ(
+ l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_POLICY_VIOLATION),
+ display_strings.detail_string);
+ EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_POLICY_VIOLATION_OFF),
+ display_strings.panel_string);
+ }
+ {
+ CWSInfoService::CWSInfo cws_info;
+ cws_info.is_present = true;
+ cws_info.violation_type = CWSInfoService::CWSViolationType::kPolicy;
+ developer::SafetyCheckStrings display_strings =
+ ExtensionInfoGenerator::CreateSafetyCheckDisplayString(
+ cws_info, developer::EXTENSION_STATE_ENABLED);
+ EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_POLICY_VIOLATION_ON),
+ display_strings.panel_string);
+ }
+ {
+ CWSInfoService::CWSInfo cws_info;
+ cws_info.is_present = true;
+ cws_info.violation_type = CWSInfoService::CWSViolationType::kNone;
+ cws_info.unpublished_long_ago = true;
+ developer::SafetyCheckStrings display_strings =
+ ExtensionInfoGenerator::CreateSafetyCheckDisplayString(
+ cws_info, developer::EXTENSION_STATE_DISABLED);
+ EXPECT_EQ(l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_UNPUBLISHED),
+ display_strings.detail_string);
+ EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_UNPUBLISHED_OFF),
+ display_strings.panel_string);
+ display_strings = ExtensionInfoGenerator::CreateSafetyCheckDisplayString(
+ cws_info, developer::EXTENSION_STATE_ENABLED);
+ EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_UNPUBLISHED_ON),
+ display_strings.panel_string);
+ }
}
TEST_F(ExtensionInfoGeneratorUnitTest, SafetyCheckEmptyStringTest) {
@@ -541,7 +578,8 @@ TEST_F(ExtensionInfoGeneratorUnitTest, SafetyCheckEmptyStringTest) {
cws_info.violation_type = CWSInfoService::CWSViolationType::kNone;
cws_info.unpublished_long_ago = false;
developer::SafetyCheckStrings display_strings;
- display_strings = CreateSafetyCheckDisplayStringTest(cws_info);
+ display_strings = ExtensionInfoGenerator::CreateSafetyCheckDisplayString(
+ cws_info, developer::EXTENSION_STATE_DISABLED);
EXPECT_EQ(display_strings.detail_string, "");
EXPECT_EQ(display_strings.panel_string, "");
}
@@ -602,6 +640,7 @@ TEST_F(ExtensionInfoGeneratorUnitTest, RuntimeHostPermissions) {
"no urls", base::Value::List(), ManifestLocation::kInternal);
info = GenerateExtensionInfo(no_urls_extension->id());
EXPECT_FALSE(info->permissions.runtime_host_permissions);
+ EXPECT_FALSE(info->permissions.can_access_site_data);
}
// Tests that specific_site_controls is correctly populated when permissions
@@ -882,6 +921,27 @@ TEST_F(ExtensionInfoGeneratorUnitTest, ActiveTabFileUrls) {
EXPECT_FALSE(info->file_access.is_active);
}
+// Test that `permissions.can_access_site_data` is set to true for extensions
+// with API permissions that can access site data, without specifying host
+// permissions.
+TEST_F(ExtensionInfoGeneratorUnitTest,
+ CanAccessSiteDataWithoutHostPermissions) {
+ scoped_refptr<const Extension> active_tab_extension =
+ CreateExtension("activeTab", base::Value::List().Append("activeTab"),
+ ManifestLocation::kInternal);
+ scoped_refptr<const Extension> debugger_extension =
+ CreateExtension("activeTab", base::Value::List().Append("debugger"),
+ ManifestLocation::kInternal);
+
+ std::unique_ptr<developer::ExtensionInfo> active_tab_info =
+ GenerateExtensionInfo(active_tab_extension->id());
+ std::unique_ptr<developer::ExtensionInfo> debugger_info =
+ GenerateExtensionInfo(debugger_extension->id());
+
+ EXPECT_TRUE(active_tab_info->permissions.can_access_site_data);
+ EXPECT_TRUE(debugger_info->permissions.can_access_site_data);
+}
+
// Tests that blocklisted extensions are returned by the ExtensionInfoGenerator.
TEST_F(ExtensionInfoGeneratorUnitTest, Blocklisted) {
const scoped_refptr<const Extension> extension1 = CreateExtension(
@@ -911,6 +971,11 @@ TEST_F(ExtensionInfoGeneratorUnitTest, Blocklisted) {
ASSERT_NE(nullptr, info2);
EXPECT_EQ(developer::EXTENSION_STATE_BLACKLISTED, info1->state);
EXPECT_EQ(developer::EXTENSION_STATE_ENABLED, info2->state);
+
+ // Verify getExtensionInfo() returns data on blocklisted extensions.
+ auto info3 = GenerateExtensionInfo(id1);
+ ASSERT_NE(nullptr, info3);
+ EXPECT_EQ(developer::EXTENSION_STATE_BLACKLISTED, info3->state);
}
// Test generating extension action commands properly.
@@ -987,6 +1052,32 @@ TEST_F(ExtensionInfoGeneratorUnitTest,
EXPECT_FALSE(info->disable_reasons.parent_disabled_permissions);
}
+// Test that the generator returns if the extension can be pinned to the toolbar
+// and if it can, whether or not it's pinned.
+TEST_F(ExtensionInfoGeneratorUnitTest, IsPinnedToToolbar) {
+ // By default, the extension is not pinned to the toolbar but can be.
+ const scoped_refptr<const Extension> extension = CreateExtension(
+ "test1", base::Value::List(), ManifestLocation::kInternal);
+ std::unique_ptr<developer::ExtensionInfo> info =
+ GenerateExtensionInfo(extension->id());
+ EXPECT_FALSE(*info->pinned_to_toolbar);
+
+ // Pin the extension to the toolbar and test that this is reflected in the
+ // generated info.
+ ToolbarActionsModel* toolbar_actions_model =
+ ToolbarActionsModel::Get(profile());
+ toolbar_actions_model->SetActionVisibility(extension->id(), true);
+ info = GenerateExtensionInfo(extension->id());
+ EXPECT_TRUE(*info->pinned_to_toolbar);
+
+ // Disable the extension. Since disabled extensions have no action, the
+ // `pinned_to_toolbar` field should not exist.
+ service()->DisableExtension(extension->id(),
+ disable_reason::DISABLE_USER_ACTION);
+ info = GenerateExtensionInfo(extension->id());
+ EXPECT_FALSE(info->pinned_to_toolbar.has_value());
+}
+
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
// Tests for supervised users (child accounts). Supervised users are not allowed
// to install apps or extensions unless their parent approves.
diff --git a/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index fc00e8031ce..ee717af4004 100644
--- a/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/values.h"
+#include "chrome/browser/download/download_browsertest_utils.h"
#include "chrome/browser/extensions/api/downloads/downloads_api.h"
#include <stddef.h>
@@ -205,7 +206,7 @@ class DownloadsEventsListener : public EventRouter::TestObserver {
}
private:
- raw_ptr<Profile, DanglingUntriaged> profile_;
+ raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_;
std::string event_name_;
std::string json_args_;
base::Value args_;
@@ -231,7 +232,8 @@ class DownloadsEventsListener : public EventRouter::TestObserver {
}
// extensions::EventRouter::TestObserver:
- void OnDidDispatchEventToProcess(const extensions::Event& event) override {}
+ void OnDidDispatchEventToProcess(const extensions::Event& event,
+ int process_id) override {}
bool WaitFor(Profile* profile,
const std::string& event_name,
@@ -274,7 +276,7 @@ class DownloadsEventsListener : public EventRouter::TestObserver {
base::Time last_wait_;
std::unique_ptr<Event> waiting_for_;
base::circular_deque<std::unique_ptr<Event>> events_;
- raw_ptr<Profile, DanglingUntriaged> profile_;
+ raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_;
};
// Object waiting for a download open event.
@@ -420,6 +422,7 @@ class DownloadExtensionTest : public ExtensionApiTest {
DownloadTestFileActivityObserver observer(incognito_browser_->profile());
observer.EnableFileChooser(false);
}
+ SetPromptForDownload(incognito_browser_, false);
current_browser_ = incognito_browser_;
if (events_listener_.get())
events_listener_->UpdateProfile(current_browser()->profile());
@@ -758,8 +761,8 @@ class DownloadExtensionTest : public ExtensionApiTest {
raw_ptr<const Extension, DanglingUntriaged> extension_;
raw_ptr<const Extension, DanglingUntriaged> second_extension_;
- raw_ptr<Browser, DanglingUntriaged> incognito_browser_;
- raw_ptr<Browser, DanglingUntriaged> current_browser_;
+ raw_ptr<Browser, AcrossTasksDanglingUntriaged> incognito_browser_;
+ raw_ptr<Browser, AcrossTasksDanglingUntriaged> current_browser_;
std::unique_ptr<DownloadsEventsListener> events_listener_;
std::unique_ptr<net::test_server::ControllableHttpResponse> first_download_;
diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc
index 65b7a109039..54dd7a0e223 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc
@@ -71,6 +71,8 @@ class EnterpriseDeviceAttributesApiAshTest
DeviceSettingsTestBase::SetUp();
+ testing_profile_ = profile_manager_.CreateTestingProfile(kAccountId);
+
switch (GetParam()) {
case TestProfileChoice::kSigninProfile:
TestingProfile* signin_profile;
@@ -114,7 +116,7 @@ class EnterpriseDeviceAttributesApiAshTest
AccountId account_id = AccountId::FromUserEmail(kAccountId);
user_manager_->AddUserWithAffiliationAndTypeAndProfile(
account_id, is_affiliated, user_manager::USER_TYPE_REGULAR,
- profile_.get());
+ testing_profile_);
user_manager_->LoginUser(account_id);
}
@@ -128,8 +130,9 @@ class EnterpriseDeviceAttributesApiAshTest
}
}
- private:
+ protected:
TestingProfileManager profile_manager_;
+ raw_ptr<TestingProfile> testing_profile_;
std::unique_ptr<crosapi::CrosapiManager> manager_;
std::unique_ptr<policy::FakeDeviceAttributes> device_attributes_;
};
@@ -140,7 +143,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest, GetDirectoryDeviceIdFunction) {
absl::optional<base::Value> result =
api_test_utils::RunFunctionAndReturnSingleResult(
- function.get(), /*args=*/"[]", profile_.get());
+ function.get(), /*args=*/"[]", testing_profile_);
ASSERT_TRUE(result->is_string());
EXPECT_EQ(
IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeDirectoryApiId : "",
@@ -153,7 +156,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest, GetDeviceSerialNumberFunction) {
absl::optional<base::Value> result =
api_test_utils::RunFunctionAndReturnSingleResult(
- function.get(), /*args=*/"[]", profile_.get());
+ function.get(), /*args=*/"[]", testing_profile_);
ASSERT_TRUE(result->is_string());
EXPECT_EQ(IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeSerialNumber : "",
result->GetString());
@@ -165,7 +168,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest, GetDeviceAssetIdFunction) {
absl::optional<base::Value> result =
api_test_utils::RunFunctionAndReturnSingleResult(
- function.get(), /*args=*/"[]", profile_.get());
+ function.get(), /*args=*/"[]", testing_profile_);
ASSERT_TRUE(result->is_string());
EXPECT_EQ(IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeAssetId : "",
result->GetString());
@@ -178,7 +181,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest,
absl::optional<base::Value> result =
api_test_utils::RunFunctionAndReturnSingleResult(
- function.get(), /*args=*/"[]", profile_.get());
+ function.get(), /*args=*/"[]", testing_profile_);
ASSERT_TRUE(result->is_string());
EXPECT_EQ(
IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeAnnotatedLocation : "",
@@ -191,7 +194,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest, GetDeviceHostnameFunction) {
absl::optional<base::Value> result =
api_test_utils::RunFunctionAndReturnSingleResult(
- function.get(), /*args=*/"[]", profile_.get());
+ function.get(), /*args=*/"[]", testing_profile_);
ASSERT_TRUE(result->is_string());
EXPECT_EQ(IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeHostname : "",
result->GetString());
diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_lacros_apitest.cc b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_lacros_apitest.cc
index 26a26ad5314..20d4c806a37 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_lacros_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_lacros_apitest.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc
index a44a8347968..fd58a396cbb 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc
@@ -172,12 +172,13 @@ class EPKChallengeKeyTestBase : public BrowserWithTestWindowTest {
scoped_refptr<const extensions::Extension> extension_;
ash::StubInstallAttributes stub_install_attributes_;
// fake_user_manager_ is owned by user_manager_enabler_.
- raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_ =
- nullptr;
+ raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh>
+ fake_user_manager_ = nullptr;
user_manager::ScopedUserManager user_manager_enabler_;
ash::platform_keys::MockKeyPermissionsManager key_permissions_manager_;
- raw_ptr<PrefService, ExperimentalAsh> prefs_ = nullptr;
- raw_ptr<ash::attestation::MockTpmChallengeKey, ExperimentalAsh>
+ raw_ptr<PrefService, DanglingUntriaged | ExperimentalAsh> prefs_ = nullptr;
+ raw_ptr<ash::attestation::MockTpmChallengeKey,
+ DanglingUntriaged | ExperimentalAsh>
mock_tpm_challenge_key_ = nullptr;
};
diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
index 216beea8fd3..82f98dca30d 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
@@ -26,6 +26,7 @@
#include "chrome/browser/net/nss_service_factory.h"
#include "chrome/browser/policy/extension_force_install_mixin.h"
#include "chrome/common/chrome_paths.h"
+#include "chromeos/ash/components/chaps_util/test_util.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -271,8 +272,7 @@ class EnterprisePlatformKeysTest
// Allows tests to generate software-backed keys by configuring fake ChapsUtil
// instances to be created in its constructor (and undoing the change in its
// destructor).
- ash::platform_keys::test_util::ScopedChapsUtilOverride
- scoped_chaps_util_override_;
+ chromeos::ScopedChapsUtilOverride scoped_chaps_util_override_;
};
} // namespace
@@ -446,8 +446,7 @@ class EnterprisePlatformKeysLoginScreenTest
// Allows tests to generate software-backed keys by configuring fake ChapsUtil
// instances to be created in its constructor (and undoing the change in its
// destructor).
- ash::platform_keys::test_util::ScopedChapsUtilOverride
- scoped_chaps_util_override_;
+ chromeos::ScopedChapsUtilOverride scoped_chaps_util_override_;
};
IN_PROC_BROWSER_TEST_P(EnterprisePlatformKeysLoginScreenTest, Basic) {
diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc
index cd924613ff0..7b32da90c64 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc
@@ -73,10 +73,10 @@ class EPKPChallengeKeyTestBase : public BrowserWithTestWindowTest {
scoped_refptr<const Extension> extension_;
// fake_user_manager_ is owned by user_manager_enabler_.
- raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_ =
- nullptr;
+ raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh>
+ fake_user_manager_ = nullptr;
user_manager::ScopedUserManager user_manager_enabler_;
- raw_ptr<PrefService, ExperimentalAsh> prefs_ = nullptr;
+ raw_ptr<PrefService, DanglingUntriaged | ExperimentalAsh> prefs_ = nullptr;
};
class EPKPChallengeMachineKeyTest : public EPKPChallengeKeyTestBase {
diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.cc
index bf62e292d1d..d652d410065 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.cc
@@ -30,7 +30,7 @@
#endif
#if BUILDFLAG(IS_MAC)
-#include "base/mac/foundation_util.h"
+#include "base/apple/foundation_util.h"
#include "chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.h"
#include "crypto/apple_keychain.h"
#endif
@@ -170,7 +170,7 @@ int32_t ReadEncryptedSecret(std::string* password, bool force_recreate) {
crypto::AppleKeychain keychain;
UInt32 password_length = 0;
void* password_data = nullptr;
- base::ScopedCFTypeRef<SecKeychainItemRef> item_ref;
+ base::apple::ScopedCFTypeRef<SecKeychainItemRef> item_ref;
status = keychain.FindGenericPassword(
strlen(kServiceName), kServiceName, strlen(kAccountName), kAccountName,
&password_length, &password_data, item_ref.InitializeInto());
diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
index 3cbbdf4cb79..594b680912d 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
@@ -590,7 +590,7 @@ IN_PROC_BROWSER_TEST_F(EnterpriseReportingPrivateApiTest,
kOptions = base::StringPrintf(
R"(
- const test_hive = 'HKEY_LOCAL_MACHINE';
+ const test_hive = 'HKEY_CURRENT_USER';
const registry_path = '%s';
const invalid_path = 'SOFTWARE\\Chromium\\DeviceTrust\\Invalid';
const valid_key = '%s';
@@ -624,9 +624,9 @@ IN_PROC_BROWSER_TEST_F(EnterpriseReportingPrivateApiTest,
registry_path.c_str(), valid_key.c_str());
registry_util::RegistryOverrideManager registry_override_manager_;
- registry_override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE);
+ registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER);
- base::win::RegKey key(HKEY_LOCAL_MACHINE,
+ base::win::RegKey key(HKEY_CURRENT_USER,
base::SysUTF8ToWide(registry_path).c_str(),
KEY_ALL_ACCESS);
ASSERT_TRUE(key.WriteValue(base::SysUTF8ToWide(valid_key).c_str(), 37) ==
diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc
index 7841b19868e..d09fabda838 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc
@@ -7,8 +7,8 @@
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
-#include "base/mac/foundation_util.h"
-#include "base/mac/scoped_cftyperef.h"
+#include "base/apple/foundation_util.h"
+#include "base/apple/scoped_cftyperef.h"
#include "base/strings/sys_string_conversions.h"
namespace extensions {
@@ -30,7 +30,7 @@ OSStatus CreateTargetAccess(CFStringRef service_name,
return status;
}
- base::ScopedCFTypeRef<CFArrayRef> acl_list;
+ base::apple::ScopedCFTypeRef<CFArrayRef> acl_list;
status = SecAccessCopyACLList(*access_ref, acl_list.InitializeInto());
if (status != noErr) {
return status;
@@ -39,8 +39,8 @@ OSStatus CreateTargetAccess(CFStringRef service_name,
for (CFIndex i = 0; i < CFArrayGetCount(acl_list); ++i) {
SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(acl_list, i);
- base::ScopedCFTypeRef<CFArrayRef> app_list;
- base::ScopedCFTypeRef<CFStringRef> description;
+ base::apple::ScopedCFTypeRef<CFArrayRef> app_list;
+ base::apple::ScopedCFTypeRef<CFStringRef> description;
SecKeychainPromptSelector dummy_prompt_selector;
status = SecACLCopyContents(acl, app_list.InitializeInto(),
description.InitializeInto(),
@@ -93,7 +93,7 @@ OSStatus WriteKeychainItem(const std::string& service_name,
const_cast<char*>(account_name.data())}};
SecKeychainAttributeList attribute_list = {std::size(attributes), attributes};
- base::ScopedCFTypeRef<SecAccessRef> access_ref;
+ base::apple::ScopedCFTypeRef<SecAccessRef> access_ref;
OSStatus status = CreateTargetAccess(base::SysUTF8ToCFStringRef(service_name),
access_ref.InitializeInto());
if (status != noErr) {
@@ -107,7 +107,7 @@ OSStatus WriteKeychainItem(const std::string& service_name,
OSStatus VerifyKeychainForItemUnlocked(SecKeychainItemRef item_ref,
bool* unlocked) {
- base::ScopedCFTypeRef<SecKeychainRef> keychain;
+ base::apple::ScopedCFTypeRef<SecKeychainRef> keychain;
OSStatus status =
SecKeychainItemCopyKeychain(item_ref, keychain.InitializeInto());
if (status != noErr) {
@@ -118,7 +118,7 @@ OSStatus VerifyKeychainForItemUnlocked(SecKeychainItemRef item_ref,
}
OSStatus VerifyDefaultKeychainUnlocked(bool* unlocked) {
- base::ScopedCFTypeRef<SecKeychainRef> keychain;
+ base::apple::ScopedCFTypeRef<SecKeychainRef> keychain;
OSStatus status = SecKeychainCopyDefault(keychain.InitializeInto());
if (status != noErr) {
return status;
diff --git a/chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc b/chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
index 5c3f547ae65..079f1f43e07 100644
--- a/chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
+++ b/chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
@@ -858,8 +858,9 @@ IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveFencedFrameTest,
manager->GetRenderFrameHostsForExtension(extension->id());
const auto& it = base::ranges::find_if(
hosts, &content::RenderFrameHost::IsInPrimaryMainFrame);
- content::RenderFrameHost* primary_rfh = (it != hosts.end()) ? *it : nullptr;
- ASSERT_TRUE(primary_rfh);
+ content::RenderFrameHost* primary_render_frame_host =
+ (it != hosts.end()) ? *it : nullptr;
+ ASSERT_TRUE(primary_render_frame_host);
// Navigate the popup's fenced frame to a (cross-site) web page via its
// parent, and wait for that page to send a message, which will ensure that
@@ -867,19 +868,20 @@ IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveFencedFrameTest,
GURL foo_url(https_server.GetURL("a.test", "/popup_fencedframe.html"));
content::TestNavigationObserver observer(
- content::WebContents::FromRenderFrameHost(primary_rfh));
+ content::WebContents::FromRenderFrameHost(primary_render_frame_host));
std::string script =
"document.querySelector('fencedframe').config = new FencedFrameConfig('" +
foo_url.spec() + "')";
- EXPECT_TRUE(ExecJs(primary_rfh, script));
+ EXPECT_TRUE(ExecJs(primary_render_frame_host, script));
observer.WaitForNavigationFinished();
- content::RenderFrameHost* fenced_frame_rfh =
- fenced_frame_test_helper().GetMostRecentlyAddedFencedFrame(primary_rfh);
- ASSERT_TRUE(fenced_frame_rfh);
+ content::RenderFrameHost* fenced_frame_render_frame_host =
+ fenced_frame_test_helper().GetMostRecentlyAddedFencedFrame(
+ primary_render_frame_host);
+ ASSERT_TRUE(fenced_frame_render_frame_host);
// Confirm that the new page (popup_fencedframe.html) is actually loaded.
- content::DOMMessageQueue dom_message_queue(fenced_frame_rfh);
+ content::DOMMessageQueue dom_message_queue(fenced_frame_render_frame_host);
std::string json;
EXPECT_TRUE(dom_message_queue.WaitForMessage(&json));
EXPECT_EQ("\"DONE\"", json);
diff --git a/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc b/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
index 0ca967e7b98..d268ada6388 100644
--- a/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
@@ -23,6 +23,7 @@
#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/sessions/content/session_tab_helper.h"
+#include "components/version_info/channel.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
@@ -33,10 +34,12 @@
#include "extensions/browser/extension_action_manager.h"
#include "extensions/browser/extension_icon_image.h"
#include "extensions/browser/process_manager.h"
+#include "extensions/browser/service_worker/service_worker_test_utils.h"
#include "extensions/browser/state_store.h"
#include "extensions/common/api/extension_action/action_info.h"
#include "extensions/common/api/extension_action/action_info_test_util.h"
#include "extensions/common/extension.h"
+#include "extensions/common/features/feature_channel.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
@@ -104,7 +107,7 @@ class TestStateStoreObserver : public StateStore::TestObserver {
}
private:
- std::string extension_id_;
+ ExtensionId extension_id_;
std::map<std::string, int> updated_values_;
base::ScopedObservation<StateStore, StateStore::TestObserver>
@@ -1869,6 +1872,73 @@ IN_PROC_BROWSER_TEST_P(ActionAndBrowserActionAPITest,
EXPECT_EQ("", action->GetExplicitlySetBadgeText(tab_id2));
}
+class ExtensionActionStableChannelApiTest : public ExtensionActionAPITest {
+ public:
+ ExtensionActionStableChannelApiTest() = default;
+ ~ExtensionActionStableChannelApiTest() override = default;
+
+ private:
+ ScopedCurrentChannel scoped_current_channel_{version_info::Channel::STABLE};
+};
+
+// Tests that the action.openPopup() API is available to policy-installed
+// extensions on stable. Since this is controlled through our features files
+// (which are tested separately), this is more of a smoke test than an
+// end-to-end test.
+// TODO(https://crbug.com/1245093): Remove this test when the API is available
+// for all extensions on stable.
+IN_PROC_BROWSER_TEST_F(ExtensionActionStableChannelApiTest,
+ OpenPopupAvailabilityOnStableChannel) {
+ TestExtensionDir test_dir;
+ static constexpr char kManifest[] =
+ R"({
+ "name": "Test",
+ "manifest_version": 3,
+ "version": "0.1",
+ "background": {"service_worker": "background.js"},
+ "action": {}
+ })";
+ test_dir.WriteManifest(kManifest);
+ test_dir.WriteFile(FILE_PATH_LITERAL("background.js"),
+ "chrome.test.sendMessage('ready');");
+
+ auto is_open_popup_defined = [this](const Extension& extension) {
+ static constexpr char kScript[] =
+ R"(chrome.test.sendScriptResult(!!chrome.action.openPopup);)";
+ return BackgroundScriptExecutor::ExecuteScript(
+ profile(), extension.id(), kScript,
+ BackgroundScriptExecutor::ResultCapture::kSendScriptResult);
+ };
+
+ // Technically, we don't need the "ready" listener here, but this ensures we
+ // don't cross streams with the policy extension loaded below (where we do
+ // need the listener).
+ ExtensionTestMessageListener non_policy_listener("ready");
+ const Extension* non_policy_extension =
+ LoadExtension(test_dir.UnpackedPath());
+ ASSERT_TRUE(non_policy_extension);
+ ASSERT_TRUE(non_policy_listener.WaitUntilSatisfied());
+
+ // Somewhat annoying: due to how our test helpers are written,
+ // `EXPECT_EQ(false, base::Value)` works, but EXPECT_FALSE(base::Value) does
+ // not.
+ EXPECT_EQ(false, is_open_popup_defined(*non_policy_extension));
+
+ // Unlike `LoadExtension()`, `InstallExtension()` doesn't wait for the service
+ // worker to be ready, so we need a few manual waiters.
+ base::FilePath packed_path = test_dir.Pack();
+ service_worker_test_utils::TestRegistrationObserver registration_observer(
+ profile());
+ ExtensionTestMessageListener policy_listener("ready");
+ const Extension* policy_extension = InstallExtension(
+ packed_path, 1, mojom::ManifestLocation::kExternalPolicyDownload);
+ ASSERT_TRUE(policy_extension);
+ ASSERT_TRUE(policy_listener.WaitUntilSatisfied());
+ registration_observer.WaitForRegistrationStored();
+
+ EXPECT_EQ(true, is_open_popup_defined(*policy_extension));
+}
+
INSTANTIATE_TEST_SUITE_P(All,
MultiActionAPITest,
testing::Values(ActionInfo::TYPE_ACTION,
diff --git a/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc b/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
index eb76373357c..115b9112f04 100644
--- a/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
@@ -32,7 +32,7 @@
#if BUILDFLAG(IS_MAC)
#include <CoreFoundation/CoreFoundation.h>
-#include "base/mac/foundation_util.h"
+#include "base/apple/foundation_util.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
diff --git a/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.cc b/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.cc
index 219218e4914..403dac6742b 100644
--- a/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.cc
+++ b/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.cc
@@ -12,6 +12,7 @@
#include "base/functional/callback.h"
#include "base/path_service.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
+#include "chrome/browser/ash/fileapi/file_system_backend.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/child_process_security_policy.h"
@@ -25,7 +26,6 @@
#include "extensions/browser/extension_util.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
-#include "storage/browser/file_system/file_system_backend.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
#include "storage/browser/file_system/isolated_context.h"
@@ -91,8 +91,7 @@ void OnConsentReceived(content::BrowserContext* browser_context,
scoped_refptr<storage::FileSystemContext> file_system_context =
util::GetStoragePartitionForExtensionId(origin.host(), browser_context)
->GetFileSystemContext();
- storage::ExternalFileSystemBackend* const backend =
- file_system_context->external_backend();
+ auto* const backend = ash::FileSystemBackend::Get(*file_system_context);
DCHECK(backend);
base::FilePath virtual_path;
@@ -225,8 +224,7 @@ void ChromeFileSystemDelegateAsh::RequestFileSystem(
scoped_refptr<storage::FileSystemContext> file_system_context =
util::GetStoragePartitionForExtensionId(extension.id(), browser_context)
->GetFileSystemContext();
- storage::ExternalFileSystemBackend* const backend =
- file_system_context->external_backend();
+ auto* const backend = ash::FileSystemBackend::Get(*file_system_context);
DCHECK(backend);
base::FilePath virtual_path;
diff --git a/chromium/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc b/chromium/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc
index 8c0f3be44b1..7cf734507db 100644
--- a/chromium/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc
@@ -123,13 +123,13 @@ class FileSystemApiConsentProviderTest : public testing::Test {
void TearDown() override {
scoped_user_manager_enabler_.reset();
user_manager_ = nullptr;
- testing_pref_service_.reset();
TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
+ testing_pref_service_.reset();
}
protected:
std::unique_ptr<TestingPrefServiceSimple> testing_pref_service_;
- raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh>
+ raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh>
user_manager_; // Owned by the scope enabler.
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_enabler_;
content::BrowserTaskEnvironment task_environment_;
diff --git a/chromium/chrome/browser/extensions/api/file_system/file_entry_picker.cc b/chromium/chrome/browser/extensions/api/file_system/file_entry_picker.cc
index 0b1b8777b0c..deeba61820c 100644
--- a/chromium/chrome/browser/extensions/api/file_system/file_entry_picker.cc
+++ b/chromium/chrome/browser/extensions/api/file_system/file_entry_picker.cc
@@ -41,7 +41,9 @@ FileEntryPicker::FileEntryPicker(
base::FilePath::StringType(), owning_window, /*params=*/nullptr, caller);
}
-FileEntryPicker::~FileEntryPicker() = default;
+FileEntryPicker::~FileEntryPicker() {
+ select_file_dialog_->ListenerDestroyed();
+}
void FileEntryPicker::FileSelected(const base::FilePath& path,
int index,
diff --git a/chromium/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc b/chromium/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
index d925aefc663..55ebcb0b37f 100644
--- a/chromium/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
+++ b/chromium/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
@@ -104,7 +104,7 @@ class ScopedAddListenerObserver : public EventRouter::Observer {
}
private:
- const std::string extension_id_;
+ const ExtensionId extension_id_;
base::OnceClosure callback_;
const raw_ptr<EventRouter, ExperimentalAsh> event_router_;
};
@@ -189,7 +189,7 @@ class FileSystemApiTestForDrive : public PlatformAppBrowserTest {
base::ScopedTempDir drivefs_root_;
base::FilePath drivefs_mount_point_;
std::unique_ptr<drive::FakeDriveFsHelper> fake_drivefs_helper_;
- raw_ptr<drive::DriveIntegrationService, ExperimentalAsh>
+ raw_ptr<drive::DriveIntegrationService, DanglingUntriaged | ExperimentalAsh>
integration_service_ = nullptr;
drive::DriveIntegrationServiceFactory::FactoryCallback
create_drive_integration_service_;
@@ -245,7 +245,8 @@ class FileSystemApiTestForRequestFileSystem : public PlatformAppBrowserTest {
protected:
base::ScopedTempDir temp_dir_;
- raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_;
+ raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh>
+ fake_user_manager_;
std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
// Creates a testing file system in a testing directory.
diff --git a/chromium/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chromium/chrome/browser/extensions/api/font_settings/font_settings_api.cc
index 566480dfdbf..6c46d85d16f 100644
--- a/chromium/chrome/browser/extensions/api/font_settings/font_settings_api.cc
+++ b/chromium/chrome/browser/extensions/api/font_settings/font_settings_api.cc
@@ -38,6 +38,7 @@
#include "extensions/browser/extension_prefs_helper.h"
#include "extensions/browser/extension_prefs_helper_factory.h"
#include "extensions/browser/extension_system.h"
+#include "extensions/common/api/types.h"
#include "extensions/common/error_utils.h"
#if BUILDFLAG(IS_WIN)
@@ -47,6 +48,7 @@
namespace extensions {
namespace fonts = api::font_settings;
+using extensions::api::types::ChromeSettingScope;
namespace {
@@ -278,7 +280,7 @@ ExtensionFunction::ResponseAction FontSettingsClearFontFunction::Run() {
EXTENSION_FUNCTION_VALIDATE(profile->GetPrefs()->FindPreference(pref_path));
ExtensionPrefsHelper::Get(profile)->RemoveExtensionControlledPref(
- extension_id(), pref_path, kExtensionPrefsScopeRegular);
+ extension_id(), pref_path, ChromeSettingScope::kRegular);
return RespondNow(NoArguments());
}
@@ -332,7 +334,7 @@ ExtensionFunction::ResponseAction FontSettingsSetFontFunction::Run() {
EXTENSION_FUNCTION_VALIDATE(profile->GetPrefs()->FindPreference(pref_path));
ExtensionPrefsHelper::Get(profile)->SetExtensionControlledPref(
- extension_id(), pref_path, kExtensionPrefsScopeRegular,
+ extension_id(), pref_path, ChromeSettingScope::kRegular,
base::Value(params->details.font_id));
return RespondNow(NoArguments());
}
@@ -383,7 +385,7 @@ ExtensionFunction::ResponseAction ClearFontPrefExtensionFunction::Run() {
return RespondNow(Error(kSetFromIncognitoError));
ExtensionPrefsHelper::Get(profile)->RemoveExtensionControlledPref(
- extension_id(), GetPrefName(), kExtensionPrefsScopeRegular);
+ extension_id(), GetPrefName(), ChromeSettingScope::kRegular);
return RespondNow(NoArguments());
}
@@ -419,7 +421,7 @@ ExtensionFunction::ResponseAction SetFontPrefExtensionFunction::Run() {
EXTENSION_FUNCTION_VALIDATE(value);
ExtensionPrefsHelper::Get(profile)->SetExtensionControlledPref(
- extension_id(), GetPrefName(), kExtensionPrefsScopeRegular,
+ extension_id(), GetPrefName(), ChromeSettingScope::kRegular,
value->Clone());
return RespondNow(NoArguments());
}
diff --git a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
index f7a31c16327..593f8c1cbe4 100644
--- a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
+++ b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
@@ -6,26 +6,16 @@
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
-#include "base/strings/escape.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/extensions/api/identity/identity_api.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
-#include "chrome/browser/signin/account_reconcilor_factory.h"
#include "chrome/browser/signin/google_accounts_private_api_host.h"
#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/common/chrome_features.h"
-#include "components/signin/core/browser/account_reconcilor.h"
-#include "components/signin/public/base/multilogin_parameters.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h"
#include "content/public/browser/storage_partition.h"
-#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/gaia_auth_util.h"
-#include "google_apis/gaia/gaia_urls.h"
#include "net/cookies/cookie_util.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
-#include "url/gurl.h"
#include "url/url_constants.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -67,29 +57,11 @@ GaiaRemoteConsentFlow::~GaiaRemoteConsentFlow() {
void GaiaRemoteConsentFlow::Start() {
if (!web_flow_) {
- web_flow_ = std::make_unique<WebAuthFlow>(
- this, profile_, resolution_data_.url, WebAuthFlow::INTERACTIVE,
- WebAuthFlow::GET_AUTH_TOKEN, user_gesture_);
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
- // `profile_` may be nullptr in tests.
- if (profile_ &&
- !base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) {
- AccountReconcilorFactory::GetForProfile(profile_)
- ->GetConsistencyCookieManager()
- ->AddExtraCookieManager(GetCookieManagerForPartition());
- }
-#endif
+ web_flow_ =
+ std::make_unique<WebAuthFlow>(this, profile_, resolution_data_.url,
+ WebAuthFlow::INTERACTIVE, user_gesture_);
}
- if (base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) {
- StartWebFlow();
- return;
- }
-
- SetAccountsInCookie();
-}
-
-void GaiaRemoteConsentFlow::StartWebFlow() {
network::mojom::CookieManager* cookie_manager =
GetCookieManagerForPartition();
net::CookieOptions options;
@@ -104,34 +76,6 @@ void GaiaRemoteConsentFlow::StartWebFlow() {
web_flow_started_ = true;
}
-void GaiaRemoteConsentFlow::OnSetAccountsComplete(
- signin::SetAccountsInCookieResult result) {
- // No need to inject account cookies when the flow is displayed in a browser
- // tab.
- DCHECK(!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab));
-
- set_accounts_in_cookie_task_.reset();
- if (web_flow_started_) {
- return;
- }
-
- if (result != signin::SetAccountsInCookieResult::kSuccess) {
- GaiaRemoteConsentFlowFailed(
- GaiaRemoteConsentFlow::Failure::SET_ACCOUNTS_IN_COOKIE_FAILED);
- return;
- }
-
- identity_api_set_consent_result_subscription_ =
- IdentityAPI::GetFactoryInstance()
- ->Get(profile_)
- ->RegisterOnSetConsentResultCallback(
- base::BindRepeating(&GaiaRemoteConsentFlow::OnConsentResultSet,
- base::Unretained(this)));
-
- scoped_observation_.Observe(IdentityManagerFactory::GetForProfile(profile_));
- StartWebFlow();
-}
-
void GaiaRemoteConsentFlow::ReactToConsentResult(
const std::string& consent_result) {
bool consent_approved = false;
@@ -151,21 +95,6 @@ void GaiaRemoteConsentFlow::ReactToConsentResult(
delegate_->OnGaiaRemoteConsentFlowApproved(consent_result, gaia_id);
}
-void GaiaRemoteConsentFlow::OnConsentResultSet(
- const std::string& consent_result,
- const std::string& window_id) {
- // JS hook in a browser tab calls `ReactToConsentResult()` directly.
- DCHECK(!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab));
-
- if (!web_flow_ || window_id != web_flow_->GetAppWindowKey()) {
- return;
- }
-
- identity_api_set_consent_result_subscription_ = {};
-
- ReactToConsentResult(consent_result);
-}
-
void GaiaRemoteConsentFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) {
GaiaRemoteConsentFlow::Failure gaia_failure;
@@ -173,9 +102,6 @@ void GaiaRemoteConsentFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) {
case WebAuthFlow::WINDOW_CLOSED:
gaia_failure = GaiaRemoteConsentFlow::WINDOW_CLOSED;
break;
- case WebAuthFlow::USER_NAVIGATED_AWAY:
- gaia_failure = GaiaRemoteConsentFlow::USER_NAVIGATED_AWAY;
- break;
case WebAuthFlow::LOAD_FAILED:
case WebAuthFlow::TIMED_OUT:
gaia_failure = GaiaRemoteConsentFlow::LOAD_FAILED;
@@ -192,115 +118,22 @@ void GaiaRemoteConsentFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) {
GaiaRemoteConsentFlowFailed(gaia_failure);
}
-content::StoragePartition* GaiaRemoteConsentFlow::GetStoragePartition() {
- content::StoragePartition* storage_partition = web_flow_->GetGuestPartition();
- if (!storage_partition) {
- // `web_flow_` doesn't have a guest partition only when the Auth Through
- // Browser Tab flow is used.
- DCHECK(base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab));
- storage_partition = profile_->GetDefaultStoragePartition();
- }
-
- return storage_partition;
-}
-
-std::unique_ptr<GaiaAuthFetcher>
-GaiaRemoteConsentFlow::CreateGaiaAuthFetcherForPartition(
- GaiaAuthConsumer* consumer,
- const gaia::GaiaSource& source) {
- return std::make_unique<GaiaAuthFetcher>(
- consumer, source,
- GetStoragePartition()->GetURLLoaderFactoryForBrowserProcess());
-}
-
network::mojom::CookieManager*
GaiaRemoteConsentFlow::GetCookieManagerForPartition() {
- return GetStoragePartition()->GetCookieManagerForBrowserProcess();
-}
-
-void GaiaRemoteConsentFlow::OnEndBatchOfRefreshTokenStateChanges() {
- // No need to copy added accounts when showing the flow in a browser tab.
- DCHECK(!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab));
-
-// On ChromeOS, new accounts are added through the account manager. They need to
-// be pushed to the partition used by this flow explicitly.
-// On Desktop, sign-in happens on the Web and a new account is directly added to
-// this partition's cookie jar. An extra update triggered from here might change
-// cookies order in the middle of the flow. This may lead to a bug like
-// https://crbug.com/1112343.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- DCHECK(ash::IsAccountManagerAvailable(profile_));
- SetAccountsInCookie();
-#elif BUILDFLAG(IS_CHROMEOS_LACROS)
- if (AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile_))
- SetAccountsInCookie();
-#endif
+ return profile_->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess();
}
void GaiaRemoteConsentFlow::SetWebAuthFlowForTesting(
std::unique_ptr<WebAuthFlow> web_auth_flow) {
DetachWebAuthFlow();
web_flow_ = std::move(web_auth_flow);
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
- // `profile_` may be nullptr in tests.
- if (profile_) {
- AccountReconcilorFactory::GetForProfile(profile_)
- ->GetConsistencyCookieManager()
- ->AddExtraCookieManager(GetCookieManagerForPartition());
- }
-#endif
}
WebAuthFlow* GaiaRemoteConsentFlow::GetWebAuthFlowForTesting() const {
return web_flow_.get();
}
-void GaiaRemoteConsentFlow::SetAccountsInCookie() {
- // No need to inject account cookies when the flow is displayed in a browser
- // tab.
- DCHECK(!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab));
-
- // Reset a task that is already in flight because it contains stale
- // information.
- if (set_accounts_in_cookie_task_)
- set_accounts_in_cookie_task_.reset();
-
- auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
- std::vector<CoreAccountId> accounts;
- if (IdentityAPI::GetFactoryInstance()
- ->Get(profile_)
- ->AreExtensionsRestrictedToPrimaryAccount()) {
- CoreAccountId primary_account_id =
- identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync);
- accounts.push_back(primary_account_id);
- } else {
- auto chrome_accounts_with_refresh_tokens =
- identity_manager->GetAccountsWithRefreshTokens();
- for (const auto& chrome_account : chrome_accounts_with_refresh_tokens) {
- // An account in persistent error state would make multilogin fail.
- // Showing only a subset of accounts seems to be a better alternative than
- // failing with an error.
- if (identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
- chrome_account.account_id)) {
- continue;
- }
- accounts.push_back(chrome_account.account_id);
- }
- }
-
- // base::Unretained() is safe here because this class owns
- // |set_accounts_in_cookie_task_| that will eventually invoke this callback.
- set_accounts_in_cookie_task_ =
- identity_manager->GetAccountsCookieMutator()
- ->SetAccountsInCookieForPartition(
- this,
- {gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
- accounts},
- gaia::GaiaSource::kChrome,
- base::BindOnce(&GaiaRemoteConsentFlow::OnSetAccountsComplete,
- base::Unretained(this)));
-}
-
void GaiaRemoteConsentFlow::GaiaRemoteConsentFlowFailed(Failure failure) {
RecordResultHistogram(failure);
delegate_->OnGaiaRemoteConsentFlowFailed(failure);
@@ -310,26 +143,11 @@ void GaiaRemoteConsentFlow::DetachWebAuthFlow() {
if (!web_flow_)
return;
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
- // `profile_` may be nullptr in tests.
- if (profile_ &&
- !base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) {
- AccountReconcilorFactory::GetForProfile(profile_)
- ->GetConsistencyCookieManager()
- ->RemoveExtraCookieManager(GetCookieManagerForPartition());
- }
-#endif
web_flow_.release()->DetachDelegateAndDelete();
}
void GaiaRemoteConsentFlow::OnNavigationFinished(
content::NavigationHandle* navigation_handle) {
- // No need to create the receiver if we are not displaying the auth page
- // through a Browser Tgab.
- if (!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) {
- return;
- }
-
GoogleAccountsPrivateApiHost::CreateReceiver(
base::BindRepeating(&GaiaRemoteConsentFlow::ReactToConsentResult,
weak_factory.GetWeakPtr()),
diff --git a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
index e316a1dba95..b25f8fcd0fe 100644
--- a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
+++ b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
@@ -5,26 +5,17 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_GAIA_REMOTE_CONSENT_FLOW_H_
#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_GAIA_REMOTE_CONSENT_FLOW_H_
-#include "base/callback_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "base/scoped_observation.h"
#include "chrome/browser/extensions/api/identity/extension_token_key.h"
#include "chrome/browser/extensions/api/identity/web_auth_flow.h"
#include "components/signin/public/identity_manager/accounts_cookie_mutator.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h"
#include "content/public/browser/storage_partition.h"
-#include "google_apis/gaia/core_account_id.h"
-#include "google_apis/gaia/google_service_auth_error.h"
#include "google_apis/gaia/oauth2_mint_token_flow.h"
namespace extensions {
-class GaiaRemoteConsentFlow
- : public WebAuthFlow::Delegate,
- public signin::AccountsCookieMutator::PartitionDelegate,
- public signin::IdentityManager::Observer {
+class GaiaRemoteConsentFlow : public WebAuthFlow::Delegate {
public:
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
@@ -32,10 +23,12 @@ class GaiaRemoteConsentFlow
NONE = 0,
WINDOW_CLOSED = 1,
LOAD_FAILED = 2,
- SET_ACCOUNTS_IN_COOKIE_FAILED = 3,
+ // Deprecated:
+ // SET_ACCOUNTS_IN_COOKIE_FAILED = 3,
INVALID_CONSENT_RESULT = 4,
NO_GRANT = 5,
- USER_NAVIGATED_AWAY = 6,
+ // Deprecated:
+ // USER_NAVIGATED_AWAY = 6,
CANNOT_CREATE_WINDOW = 7,
kMaxValue = CANNOT_CREATE_WINDOW
};
@@ -65,14 +58,6 @@ class GaiaRemoteConsentFlow
// Starts the flow by setting accounts in cookie.
void Start();
- // Set accounts in cookie completion callback.
- void OnSetAccountsComplete(signin::SetAccountsInCookieResult result);
-
- // setConsentResult() JavaScript callback when using an App Window to display
- // the Auth page.
- void OnConsentResultSet(const std::string& consent_result,
- const std::string& window_id);
-
// Handles `consent_result` value when using either a Browser Tab or an App
// Window to display the Auth page.
void ReactToConsentResult(const std::string& consent_result);
@@ -82,29 +67,15 @@ class GaiaRemoteConsentFlow
void OnNavigationFinished(
content::NavigationHandle* navigation_handle) override;
- // signin::AccountsCookieMutator::PartitionDelegate:
- std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcherForPartition(
-
- GaiaAuthConsumer* consumer,
- const gaia::GaiaSource& source) override;
- network::mojom::CookieManager* GetCookieManagerForPartition() override;
-
- // signin::IdentityManager::Observer:
- void OnEndBatchOfRefreshTokenStateChanges() override;
-
void SetWebAuthFlowForTesting(std::unique_ptr<WebAuthFlow> web_auth_flow);
WebAuthFlow* GetWebAuthFlowForTesting() const;
private:
- void StartWebFlow();
-
- void SetAccountsInCookie();
-
void GaiaRemoteConsentFlowFailed(Failure failure);
void DetachWebAuthFlow();
- content::StoragePartition* GetStoragePartition();
+ network::mojom::CookieManager* GetCookieManagerForPartition();
const raw_ptr<Delegate> delegate_;
const raw_ptr<Profile> profile_;
@@ -114,13 +85,6 @@ class GaiaRemoteConsentFlow
std::unique_ptr<WebAuthFlow> web_flow_;
bool web_flow_started_;
- std::unique_ptr<signin::AccountsCookieMutator::SetAccountsInCookieTask>
- set_accounts_in_cookie_task_;
- base::CallbackListSubscription identity_api_set_consent_result_subscription_;
- base::ScopedObservation<signin::IdentityManager,
- signin::IdentityManager::Observer>
- scoped_observation_{this};
-
base::WeakPtrFactory<GaiaRemoteConsentFlow> weak_factory{this};
};
diff --git a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc
index 0a0d118c20f..82d8d84bd83 100644
--- a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc
@@ -5,11 +5,9 @@
#include "chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h"
#include "base/strings/strcat.h"
-#include "chrome/browser/extensions/api/identity/identity_private_api.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/identity_manager.h"
@@ -17,7 +15,6 @@
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
-#include "extensions/browser/api_test_utils.h"
#include "google_apis/gaia/core_account_id.h"
#include "google_apis/gaia/fake_gaia.h"
#include "google_apis/gaia/gaia_auth_test_util.h"
@@ -57,9 +54,7 @@ class MockGaiaRemoteConsentFlowDelegate
const std::string& gaia_id));
};
-class GaiaRemoteConsentFlowParamBrowserTest
- : public InProcessBrowserTest,
- public testing::WithParamInterface<bool> {
+class GaiaRemoteConsentFlowParamBrowserTest : public InProcessBrowserTest {
public:
GaiaRemoteConsentFlowParamBrowserTest()
: fake_gaia_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
@@ -79,9 +74,6 @@ class GaiaRemoteConsentFlowParamBrowserTest
fake_gaia_test_server()->AddDefaultHandlers(GetChromeTestDataDir());
fake_gaia_test_server_.RegisterRequestHandler(base::BindRepeating(
&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_)));
-
- scoped_feature_list_.InitWithFeatureState(
- features::kWebAuthFlowInBrowserTab, GetParam());
}
void SetUp() override {
@@ -158,30 +150,14 @@ class GaiaRemoteConsentFlowParamBrowserTest
}
void SimulateConsentResult(const std::string& consent_value) {
- // When the auth flow is using the browser tab, we are able to properly test
- // the JS injected script since we rely on the Gaia Origin to filter out
- // unwanted urls, and in the test we are overriding the value of Gaia
- // Origin, so we can bypass the filter for testing.
- if (base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) {
- // JS function is properly called but returns nullptr.
- ASSERT_EQ(nullptr, content::EvalJs(
- flow()->GetWebAuthFlowForTesting()->web_contents(),
- "window.OAuthConsent.setConsentResult(\"" +
- consent_value + "\")"));
- return;
- }
-
- // Since we cannot bypass the filter that is added in the internal extension
- // (in it's manifest) we do not directly test the JS function but instead
- // the layer right above in the API through
- // `IdentityPrivateSetConsentResultFunction`.
- std::string consent_result =
- "[\"" + consent_value + "\", \"" +
- flow()->GetWebAuthFlowForTesting()->GetAppWindowKey() + "\"]";
- scoped_refptr<ExtensionFunction> func =
- base::MakeRefCounted<IdentityPrivateSetConsentResultFunction>();
- ASSERT_TRUE(
- api_test_utils::RunFunction(func.get(), consent_result, profile()));
+ // We are able to properly test the JS injected script since we rely on the
+ // Gaia Origin to filter out unwanted urls, and in the test we are
+ // overriding the value of Gaia Origin, so we can bypass the filter for
+ // testing. JS function is properly called but returns nullptr.
+ ASSERT_EQ(nullptr, content::EvalJs(
+ flow()->GetWebAuthFlowForTesting()->web_contents(),
+ "window.OAuthConsent.setConsentResult(\"" +
+ consent_value + "\")"));
}
MockGaiaRemoteConsentFlowDelegate& mock() {
@@ -207,7 +183,7 @@ class GaiaRemoteConsentFlowParamBrowserTest
base::test::ScopedFeatureList scoped_feature_list_;
};
-IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest,
SimulateInvalidConsent) {
LaunchAndWaitGaiaRemoteConsentFlow();
@@ -217,7 +193,7 @@ IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest,
SimulateConsentResult("invalid_consent");
}
-IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest, SimulateNoGrant) {
+IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest, SimulateNoGrant) {
LaunchAndWaitGaiaRemoteConsentFlow();
EXPECT_CALL(mock(), OnGaiaRemoteConsentFlowFailed(
@@ -227,7 +203,7 @@ IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest, SimulateNoGrant) {
SimulateConsentResult(declined_consent);
}
-IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest,
SimulateAccessGranted) {
LaunchAndWaitGaiaRemoteConsentFlow();
@@ -238,15 +214,5 @@ IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest,
SimulateConsentResult(approved_consent);
}
-INSTANTIATE_TEST_SUITE_P(
- ,
- GaiaRemoteConsentFlowParamBrowserTest,
- testing::Bool(),
- [](const testing::TestParamInfo<
- GaiaRemoteConsentFlowParamBrowserTest::ParamType>& info) {
- return base::StrCat({"WebAuthFlowInBrowserTab_",
- info.param ? "FeatureOn" : "FeatureOff"});
- });
-
} // namespace extensions
#endif // !BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc
index 8ce505e6ce8..fa754041c1c 100644
--- a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc
@@ -7,11 +7,7 @@
#include <memory>
#include <vector>
-#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/common/chrome_features.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -21,52 +17,35 @@ namespace extensions {
const char kResultHistogramName[] =
"Signin.Extensions.GaiaRemoteConsentFlowResult";
-const char kWindowKey[] = "window_key";
const char kGaiaId[] = "fake_gaia_id";
const char kConsentResult[] = "CAESCUVOQ1JZUFRFRBoMZmFrZV9nYWlhX2lk";
-class FakeWebAuthFlowWithWindowKey : public WebAuthFlow {
+class FakeWebAuthFlow : public WebAuthFlow {
public:
- explicit FakeWebAuthFlowWithWindowKey(WebAuthFlow::Delegate* delegate,
- std::string window_key)
+ explicit FakeWebAuthFlow(WebAuthFlow::Delegate* delegate)
: WebAuthFlow(delegate,
nullptr,
GURL(),
WebAuthFlow::INTERACTIVE,
- WebAuthFlow::GET_AUTH_TOKEN,
- /*user_gesture=*/true),
- fake_window_key_(window_key) {}
+ /*user_gesture=*/true) {}
- ~FakeWebAuthFlowWithWindowKey() override = default;
+ ~FakeWebAuthFlow() override = default;
void Start() override {}
-
- const std::string& GetAppWindowKey() const override {
- return fake_window_key_;
- }
-
- private:
- const std::string fake_window_key_;
};
class TestGaiaRemoteConsentFlow : public GaiaRemoteConsentFlow {
public:
TestGaiaRemoteConsentFlow(GaiaRemoteConsentFlow::Delegate* delegate,
const ExtensionTokenKey& token_key,
- const RemoteConsentResolutionData& resolution_data,
- const std::string& window_key)
+ const RemoteConsentResolutionData& resolution_data)
: GaiaRemoteConsentFlow(delegate,
nullptr,
token_key,
resolution_data,
- /*user_gesture=*/true),
- window_key_(window_key) {
- SetWebAuthFlowForTesting(
- std::make_unique<FakeWebAuthFlowWithWindowKey>(this, window_key_));
+ /*user_gesture=*/true) {
+ SetWebAuthFlowForTesting(std::make_unique<FakeWebAuthFlow>(this));
}
-
- private:
- const std::string window_key_;
};
class MockGaiaRemoteConsentFlowDelegate
@@ -90,13 +69,11 @@ class IdentityGaiaRemoteConsentFlowTest : public testing::Test {
// deleted.
}
- std::unique_ptr<TestGaiaRemoteConsentFlow> CreateTestFlow(
- const std::string& window_key) {
- return CreateTestFlow(window_key, &delegate_);
+ std::unique_ptr<TestGaiaRemoteConsentFlow> CreateTestFlow() {
+ return CreateTestFlow(&delegate_);
}
std::unique_ptr<TestGaiaRemoteConsentFlow> CreateTestFlow(
- const std::string& window_key,
GaiaRemoteConsentFlow::Delegate* delegate) {
CoreAccountInfo user_info;
user_info.account_id = CoreAccountId::FromGaiaId("account_id");
@@ -107,8 +84,8 @@ class IdentityGaiaRemoteConsentFlowTest : public testing::Test {
std::set<std::string>());
RemoteConsentResolutionData resolution_data;
resolution_data.url = GURL("https://example.com/auth/");
- return std::make_unique<TestGaiaRemoteConsentFlow>(
- delegate, token_key, resolution_data, window_key);
+ return std::make_unique<TestGaiaRemoteConsentFlow>(delegate, token_key,
+ resolution_data);
}
base::HistogramTester* histogram_tester() { return &histogram_tester_; }
@@ -120,7 +97,7 @@ class IdentityGaiaRemoteConsentFlowTest : public testing::Test {
};
TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult) {
- std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+ std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow();
EXPECT_CALL(delegate_,
OnGaiaRemoteConsentFlowApproved(kConsentResult, kGaiaId));
flow->ReactToConsentResult(kConsentResult);
@@ -129,11 +106,9 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult) {
}
TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult_TwoWindows) {
- std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
- const char kWindowKey2[] = "window_key2";
+ std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow();
testing::StrictMock<MockGaiaRemoteConsentFlowDelegate> delegate2;
- std::unique_ptr<TestGaiaRemoteConsentFlow> flow2 =
- CreateTestFlow(kWindowKey2, &delegate2);
+ std::unique_ptr<TestGaiaRemoteConsentFlow> flow2 = CreateTestFlow(&delegate2);
const char kConsentResult2[] = "CAESCkVOQ1JZUFRFRDI";
EXPECT_CALL(delegate2, OnGaiaRemoteConsentFlowApproved(kConsentResult2, ""));
@@ -148,7 +123,7 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult_TwoWindows) {
TEST_F(IdentityGaiaRemoteConsentFlowTest, InvalidConsentResult) {
const char kInvalidConsentResult[] = "abc";
- std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+ std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow();
EXPECT_CALL(delegate_,
OnGaiaRemoteConsentFlowFailed(
GaiaRemoteConsentFlow::Failure::INVALID_CONSENT_RESULT));
@@ -159,7 +134,7 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, InvalidConsentResult) {
TEST_F(IdentityGaiaRemoteConsentFlowTest, NoGrant) {
const char kNoGrantConsentResult[] = "CAA";
- std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+ std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow();
EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed(
GaiaRemoteConsentFlow::Failure::NO_GRANT));
flow->ReactToConsentResult(kNoGrantConsentResult);
@@ -168,7 +143,7 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, NoGrant) {
}
TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_WindowClosed) {
- std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+ std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow();
EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed(
GaiaRemoteConsentFlow::Failure::WINDOW_CLOSED));
flow->OnAuthFlowFailure(WebAuthFlow::Failure::WINDOW_CLOSED);
@@ -177,7 +152,7 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_WindowClosed) {
}
TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_LoadFailed) {
- std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
+ std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow();
EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed(
GaiaRemoteConsentFlow::Failure::LOAD_FAILED));
flow->OnAuthFlowFailure(WebAuthFlow::Failure::LOAD_FAILED);
@@ -185,39 +160,4 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_LoadFailed) {
GaiaRemoteConsentFlow::LOAD_FAILED, 1);
}
-// The following tests are only meaningful if the feature
-// `kWebAuthFlowInBrowserTab` is disabled
-class IdentityGaiaRemoteConsentFlowWebAuthFlowInBrowserTabOffTest
- : public IdentityGaiaRemoteConsentFlowTest {
- public:
- IdentityGaiaRemoteConsentFlowWebAuthFlowInBrowserTabOffTest() {
- scoped_feature_list_.InitAndDisableFeature(
- features::kWebAuthFlowInBrowserTab);
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(IdentityGaiaRemoteConsentFlowWebAuthFlowInBrowserTabOffTest,
- ConsentResult_WrongWindowIgnored) {
- std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
- // No call is expected.
- flow->OnConsentResultSet(kConsentResult, "another_window_key");
-}
-
-TEST_F(IdentityGaiaRemoteConsentFlowWebAuthFlowInBrowserTabOffTest,
- SetAccountsFailure) {
- std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey);
- EXPECT_CALL(
- delegate_,
- OnGaiaRemoteConsentFlowFailed(
- GaiaRemoteConsentFlow::Failure::SET_ACCOUNTS_IN_COOKIE_FAILED));
- flow->OnSetAccountsComplete(
- signin::SetAccountsInCookieResult::kPersistentError);
- histogram_tester()->ExpectUniqueSample(
- kResultHistogramName,
- GaiaRemoteConsentFlow::SET_ACCOUNTS_IN_COOKIE_FAILED, 1);
-}
-
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_api.cc b/chromium/chrome/browser/extensions/api/identity/identity_api.cc
index 4a8d4ba3679..3e2ff468d4a 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_api.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_api.cc
@@ -98,16 +98,6 @@ void IdentityAPI::EraseStaleGaiaIdsForAllExtensions() {
}
}
-void IdentityAPI::SetConsentResult(const std::string& result,
- const std::string& window_id) {
- on_set_consent_result_callback_list_.Notify(result, window_id);
-}
-
-base::CallbackListSubscription IdentityAPI::RegisterOnSetConsentResultCallback(
- const base::RepeatingCallback<OnSetConsentResultSignature>& callback) {
- return on_set_consent_result_callback_list_.Add(callback);
-}
-
void IdentityAPI::Shutdown() {
on_shutdown_callback_list_.Notify();
identity_manager_->RemoveObserver(this);
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_api.h b/chromium/chrome/browser/extensions/api/identity/identity_api.h
index 1986a6cf897..657d14a0a87 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_api.h
+++ b/chromium/chrome/browser/extensions/api/identity/identity_api.h
@@ -40,9 +40,6 @@ namespace extensions {
class IdentityAPI : public BrowserContextKeyedAPI,
public signin::IdentityManager::Observer {
public:
- using OnSetConsentResultSignature = void(const std::string&,
- const std::string&);
-
explicit IdentityAPI(content::BrowserContext* context);
~IdentityAPI() override;
@@ -63,12 +60,6 @@ class IdentityAPI : public BrowserContextKeyedAPI,
// longer signed in to Chrome for all extensions.
void EraseStaleGaiaIdsForAllExtensions();
- // Consent result.
- void SetConsentResult(const std::string& result,
- const std::string& window_id);
- base::CallbackListSubscription RegisterOnSetConsentResultCallback(
- const base::RepeatingCallback<OnSetConsentResultSignature>& callback);
-
// BrowserContextKeyedAPI:
void Shutdown() override;
static BrowserContextKeyedAPIFactory<IdentityAPI>* GetFactoryInstance();
@@ -126,8 +117,6 @@ class IdentityAPI : public BrowserContextKeyedAPI,
OnSignInChangedCallback on_signin_changed_callback_for_testing_;
- base::RepeatingCallbackList<OnSetConsentResultSignature>
- on_set_consent_result_callback_list_;
base::OnceCallbackList<void()> on_shutdown_callback_list_;
};
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc
index 09e44df583f..8f7b190234a 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -78,9 +78,11 @@
#include "content/public/test/test_utils.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/api_test_utils.h"
+#include "extensions/browser/pref_names.h"
#include "extensions/common/api/oauth2.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_features.h"
+#include "extensions/common/extension_id.h"
#include "extensions/common/manifest_handlers/oauth2_manifest_handler.h"
#include "google_apis/gaia/oauth2_mint_token_flow.h"
#include "net/cookies/cookie_util.h"
@@ -113,10 +115,6 @@
#endif
using extensions::ExtensionsAPIClient;
-using guest_view::GuestViewBase;
-using guest_view::GuestViewManager;
-using guest_view::TestGuestViewManager;
-using guest_view::TestGuestViewManagerFactory;
using testing::_;
using testing::Return;
@@ -342,6 +340,14 @@ void SimulateUrlRedirect(const std::string& url_prefix,
"apply_consent(\"" + url_prefix + "\");"));
}
+// Similar to SimulateUrlRedirect, but uses provided url instead of the pattern
+void SimulateCustomUrlRedirect(const std::string& redirect_url,
+ content::WebContents* auth_web_contents) {
+ ASSERT_EQ(nullptr,
+ content::EvalJs(auth_web_contents, "window.location.replace(\"" +
+ redirect_url + "\");"));
+}
+
} // namespace
class FakeGetAuthTokenFunction : public IdentityGetAuthTokenFunction {
@@ -1061,7 +1067,7 @@ class GetAuthTokenFunctionTest
base::test::ScopedFeatureList feature_list_;
base::HistogramTester histogram_tester_;
- std::string extension_id_;
+ ExtensionId extension_id_;
std::set<std::string> oauth_scopes_;
std::unique_ptr<ExtensionFunction::ScopedUserGestureForTests> user_gesture_;
};
@@ -1645,24 +1651,6 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
- InteractiveApprovalSetAccountsInCookieFailed) {
- SignIn("primary@example.com");
- scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
- func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
- func->push_mint_token_result(TestOAuth2MintTokenFlow::REMOTE_CONSENT_SUCCESS);
- func->set_scope_ui_failure(
- GaiaRemoteConsentFlow::SET_ACCOUNTS_IN_COOKIE_FAILED);
- std::string error = utils::RunFunctionAndReturnError(
- func.get(), "[{\"interactive\": true}]", browser()->profile());
- EXPECT_EQ(std::string(errors::kSetAccountsInCookieFailure), error);
- EXPECT_FALSE(func->login_ui_shown());
- EXPECT_TRUE(func->scope_ui_shown());
- histogram_tester()->ExpectUniqueSample(
- kGetAuthTokenResultHistogramName,
- IdentityGetAuthTokenError::State::kSetAccountsInCookieFailure, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveApprovalInvalidConsentResult) {
SignIn("primary@example.com");
scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
@@ -3555,7 +3543,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) {
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function =
CreateLaunchWebAuthFlowFunction();
- function->InitFinalRedirectURLPrefixForTest("abcdefghij");
+ function->InitFinalRedirectURLDomainsForTest("abcdefghij");
absl::optional<base::Value> value = utils::RunFunctionAndReturnSingleResult(
function.get(),
"[{\"interactive\": false,"
@@ -3580,7 +3568,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function =
CreateLaunchWebAuthFlowFunction();
- function->InitFinalRedirectURLPrefixForTest("abcdefghij");
+ function->InitFinalRedirectURLDomainsForTest("abcdefghij");
std::string args = base::StringPrintf(
R"([{
"interactive": false,
@@ -3602,7 +3590,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function =
CreateLaunchWebAuthFlowFunction();
- function->InitFinalRedirectURLPrefixForTest("abcdefghij");
+ function->InitFinalRedirectURLDomainsForTest("abcdefghij");
absl::optional<base::Value> value = utils::RunFunctionAndReturnSingleResult(
function.get(),
"[{\"interactive\": true,"
@@ -3625,7 +3613,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function =
CreateLaunchWebAuthFlowFunction();
- function->InitFinalRedirectURLPrefixForTest("abcdefghij");
+ function->InitFinalRedirectURLDomainsForTest("abcdefghij");
std::string args =
"[{\"interactive\": true, \"url\": \"" + auth_url.spec() + "\"}]";
absl::optional<base::Value> value = utils::RunFunctionAndReturnSingleResult(
@@ -3639,63 +3627,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
IdentityLaunchWebAuthFlowFunction::Error::kNone, 1);
}
-class LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam
- : public LaunchWebAuthFlowFunctionTest,
- public testing::WithParamInterface<bool> {
- public:
- LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam() {
- scoped_feature_list_.InitWithFeatureState(
- features::kWebAuthFlowInBrowserTab, use_tab_feature_enabled());
- }
-
- void SetUp() override {
- GuestViewManager::set_factory_for_testing(&factory_);
- LaunchWebAuthFlowFunctionTest::SetUp();
- }
-
- void TearDown() override {
- LaunchWebAuthFlowFunctionTest::TearDown();
- GuestViewManager::set_factory_for_testing(nullptr);
- }
-
- protected:
- bool use_tab_feature_enabled() { return GetParam(); }
-
- void CloseGuestView() {
- TestGuestViewManager* guest_view_manager = GetGuestViewManager();
- auto* guest_view = guest_view_manager->WaitForSingleGuestViewCreated();
- ASSERT_TRUE(guest_view);
-
- guest_view_manager->WaitUntilAttached(guest_view);
-
- auto* embedder_web_contents = guest_view->embedder_web_contents();
- ASSERT_TRUE(embedder_web_contents);
- embedder_web_contents->Close();
- }
-
- private:
- TestGuestViewManagerFactory factory_;
- base::test::ScopedFeatureList scoped_feature_list_;
-
- TestGuestViewManager* GetGuestViewManager() {
- TestGuestViewManager* manager = static_cast<TestGuestViewManager*>(
- TestGuestViewManager::FromBrowserContext(browser()->profile()));
- // Test code may access the TestGuestViewManager before it would be created
- // during creation of the first guest.
- if (!manager) {
- manager = static_cast<TestGuestViewManager*>(
- GuestViewManager::CreateWithDelegate(
- browser()->profile(),
- ExtensionsAPIClient::Get()->CreateGuestViewManagerDelegate(
- browser()->profile())));
- }
- return manager;
- }
-};
-
-IN_PROC_BROWSER_TEST_P(
- LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam,
- UserCloseWindow) {
+IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) {
std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer();
GURL auth_url(https_server->GetURL("/interaction_required.html"));
@@ -3703,30 +3635,21 @@ IN_PROC_BROWSER_TEST_P(
CreateLaunchWebAuthFlowFunction();
content::TestNavigationObserver url_obvserver(auth_url);
- if (use_tab_feature_enabled()) {
- url_obvserver.StartWatchingNewWebContents();
- }
+ url_obvserver.StartWatchingNewWebContents();
std::string args =
"[{\"interactive\": true, \"url\": \"" + auth_url.spec() + "\"}]";
RunFunctionAsync(function.get(), args);
+ url_obvserver.Wait();
+
+ Browser* popup_browser = chrome::FindBrowserWithWebContents(
+ function->GetWebAuthFlowForTesting()->web_contents());
+ TabStripModel* tabs = popup_browser->tab_strip_model();
+ EXPECT_NE(browser(), popup_browser);
+ ASSERT_EQ(tabs->GetActiveWebContents()->GetURL(), auth_url);
// Close the opened auth web contents.
- // Depending on the feature `WebAuthFlowInBrowserTab`, close the opened
- // GuestView (simulating the AppWindow), or close the new tab through the
- // created WebContents.
- if (use_tab_feature_enabled()) {
- url_obvserver.Wait();
-
- Browser* popup_browser = chrome::FindBrowserWithWebContents(
- function->GetWebAuthFlowForTesting()->web_contents());
- TabStripModel* tabs = popup_browser->tab_strip_model();
- EXPECT_NE(browser(), popup_browser);
- ASSERT_EQ(tabs->GetActiveWebContents()->GetURL(), auth_url);
- tabs->CloseWebContentsAt(tabs->active_index(), 0);
- } else {
- CloseGuestView();
- }
+ tabs->CloseWebContentsAt(tabs->active_index(), 0);
EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function.get()));
histogram_tester()->ExpectUniqueSample(
@@ -3734,25 +3657,27 @@ IN_PROC_BROWSER_TEST_P(
IdentityLaunchWebAuthFlowFunction::Error::kUserRejected, 1);
}
-INSTANTIATE_TEST_SUITE_P(
- ,
- LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam,
- testing::Bool(),
- [](const testing::TestParamInfo<
- LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam::
- ParamType>& info) {
- return base::StrCat(
- {info.param ? "With" : "Without", "WebAuthFlowInBrowserTab"});
- });
-
-class LaunchWebAuthFlowFunctionTestWithNewTab
- : public LaunchWebAuthFlowFunctionTest {
- public:
- LaunchWebAuthFlowFunctionTestWithNewTab() {
- scoped_feature_list_.InitAndEnableFeature(
- features::kWebAuthFlowInBrowserTab);
- }
+// Regression test for http://b/290733700.
+IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
+ SchemeOtherThanHttpOrHttpsNotAllowed) {
+ // Only http and https schemes are allowed.
+ GURL invalid_auth_url("chrome-untrusted://some_chrome_url");
+ scoped_refptr<IdentityLaunchWebAuthFlowFunction> function =
+ CreateLaunchWebAuthFlowFunction();
+
+ std::string args =
+ "[{\"interactive\": true, \"url\": \"" + invalid_auth_url.spec() + "\"}]";
+ RunFunctionAsync(function.get(), args);
+ EXPECT_EQ(std::string(errors::kInvalidURLScheme),
+ WaitForError(function.get()));
+ histogram_tester()->ExpectUniqueSample(
+ kLaunchWebAuthFlowResultHistogramName,
+ IdentityLaunchWebAuthFlowFunction::Error::kInvalidURLScheme, 1);
+}
+
+class LaunchWebAuthFlowFunctionTestWithBrowserTab
+ : public LaunchWebAuthFlowFunctionTest {
protected:
void RunFunctionAndWaitForNavigation(
IdentityLaunchWebAuthFlowFunction* function,
@@ -3763,19 +3688,16 @@ class LaunchWebAuthFlowFunctionTestWithNewTab
RunFunctionAsync(function, args);
url_observer.Wait();
}
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
};
-IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab,
+IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab,
PageNavigateFromInitURLToFinalURL) {
std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer();
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function =
CreateLaunchWebAuthFlowFunction();
const std::string extension_id("abcdefghij");
- function->InitFinalRedirectURLPrefixForTest(extension_id);
+ function->InitFinalRedirectURLDomainsForTest(extension_id);
const GURL auth_url(https_server->GetURL("/consent_page.html"));
const GURL final_url("https://" + extension_id + ".chromiumapp.org/");
@@ -3790,7 +3712,38 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab,
base::Value output;
WaitForOneResult(function.get(), &output);
EXPECT_FALSE(function->GetWebAuthFlowForTesting());
- EXPECT_TRUE(output.GetString().find(final_url.spec()) != std::string::npos);
+ EXPECT_EQ(GURL(output.GetString()).Resolve("/"), final_url);
+ histogram_tester()->ExpectUniqueSample(
+ kLaunchWebAuthFlowResultHistogramName,
+ IdentityLaunchWebAuthFlowFunction::Error::kNone, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab,
+ PageNavigateFromInitURLToCustomFinalURL) {
+ std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer();
+ scoped_refptr<IdentityLaunchWebAuthFlowFunction> function =
+ CreateLaunchWebAuthFlowFunction();
+
+ const GURL auth_url(https_server->GetURL("/consent_page.html"));
+ const GURL final_url("example://example.com/");
+
+ const std::string args =
+ "[{\"interactive\": true, \"url\": \"" + auth_url.spec() + "\"}]";
+
+ browser()->profile()->GetPrefs()->SetDict(
+ extensions::pref_names::kOAuthRedirectUrls,
+ base::Value::Dict().Set(function->extension()->id(),
+ base::Value::List().Append(final_url.spec())));
+ RunFunctionAndWaitForNavigation(function.get(), auth_url, args);
+
+ SimulateCustomUrlRedirect(
+ final_url.spec() + "#some_information",
+ function->GetWebAuthFlowForTesting()->web_contents());
+
+ base::Value output;
+ WaitForOneResult(function.get(), &output);
+ EXPECT_FALSE(function->GetWebAuthFlowForTesting());
+ EXPECT_EQ(GURL(output.GetString()).Resolve("/"), final_url);
histogram_tester()->ExpectUniqueSample(
kLaunchWebAuthFlowResultHistogramName,
IdentityLaunchWebAuthFlowFunction::Error::kNone, 1);
@@ -3798,7 +3751,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab,
// TODO(crbug/1421278): This test should be adapted after the implementation of
// the bug. Multiple TODOs in the test to fix.
-IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab,
+IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab,
SimilarExtensionAndArgsShouldGenerateSameFlow) {
std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer();
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function1 =
@@ -3807,8 +3760,8 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab,
CreateLaunchWebAuthFlowFunction();
const std::string extension_id("final_url");
- function1->InitFinalRedirectURLPrefixForTest(extension_id);
- function2->InitFinalRedirectURLPrefixForTest(extension_id);
+ function1->InitFinalRedirectURLDomainsForTest(extension_id);
+ function2->InitFinalRedirectURLDomainsForTest(extension_id);
const GURL auth_url(https_server->GetURL("/consent_page.html"));
const GURL final_url("https://" + extension_id + ".chromiumapp.org/");
@@ -3847,7 +3800,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab,
IdentityLaunchWebAuthFlowFunction::Error::kNone, 1);
}
-IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab,
+IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab,
DifferentExtensionsShouldGenerateDifferentFlows) {
std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer();
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function1 =
@@ -3856,9 +3809,9 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab,
CreateLaunchWebAuthFlowFunction();
const std::string extension_id1("extension1");
- function1->InitFinalRedirectURLPrefixForTest(extension_id1);
+ function1->InitFinalRedirectURLDomainsForTest(extension_id1);
const std::string extension_id2("extension2");
- function2->InitFinalRedirectURLPrefixForTest(extension_id2);
+ function2->InitFinalRedirectURLDomainsForTest(extension_id2);
const GURL auth_url(https_server->GetURL("/consent_page.html"));
// Different final_urls.
@@ -3906,7 +3859,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab,
// TODO(crbug/1421278): This test should be adapted after the implementation of
// the bug.
IN_PROC_BROWSER_TEST_F(
- LaunchWebAuthFlowFunctionTestWithNewTab,
+ LaunchWebAuthFlowFunctionTestWithBrowserTab,
ExtensionWithDifferentArgsShouldGenerateDifferentFlowsInAQueue) {
std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer();
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function1 =
@@ -3915,7 +3868,7 @@ IN_PROC_BROWSER_TEST_F(
CreateLaunchWebAuthFlowFunction();
const std::string extension_id("extension");
- function1->InitFinalRedirectURLPrefixForTest(extension_id);
+ function1->InitFinalRedirectURLDomainsForTest(extension_id);
const GURL auth_url1(https_server->GetURL("/consent_page.html"));
const GURL auth_url2(https_server->GetURL("/interaction_required.html"));
@@ -3933,8 +3886,8 @@ IN_PROC_BROWSER_TEST_F(
function1->GetWebAuthFlowForTesting()->web_contents();
content::WebContents* consent_web_contents2 =
function2->GetWebAuthFlowForTesting()->web_contents();
- // TODO(crbug/1421278): `function2->GetWebAuthFlowForTesting()` should be null
- // after the changes since it would be in a queue.
+ // TODO(crbug/1421278): `function2->GetWebAuthFlowForTesting()` should be
+ // null after the changes since it would be in a queue.
EXPECT_NE(consent_web_contents1, consent_web_contents2);
const std::string& current_consent_url2 =
@@ -4007,58 +3960,6 @@ IN_PROC_BROWSER_TEST_F(ClearAllCachedAuthTokensFunctionTest,
id_api()->token_cache()->GetToken(token_key).status());
}
-class ClearAllCachedAuthTokensFunctionTestWithPartitionParam
- : public ClearAllCachedAuthTokensFunctionTest,
- public testing::WithParamInterface<WebAuthFlow::Partition> {
- public:
- network::mojom::CookieManager* GetCookieManager() {
- Profile* profile = browser()->profile();
- return profile
- ->GetStoragePartition(
- WebAuthFlow::GetWebViewPartitionConfig(GetParam(), profile))
- ->GetCookieManagerForBrowserProcess();
- }
-
- // Returns the list of cookies in the cookie manager.
- net::CookieList GetCookies() {
- net::CookieList result;
- base::RunLoop get_all_cookies_loop;
- GetCookieManager()->GetAllCookies(base::BindLambdaForTesting(
- [&get_all_cookies_loop, &result](const net::CookieList& cookie_list) {
- result = cookie_list;
- get_all_cookies_loop.Quit();
- }));
- get_all_cookies_loop.Run();
- return result;
- }
-};
-
-IN_PROC_BROWSER_TEST_P(ClearAllCachedAuthTokensFunctionTestWithPartitionParam,
- CleanWebAuthFlowCookies) {
- auto test_cookie = net::CanonicalCookie::CreateUnsafeCookieForTesting(
- "test_name", "test_value", "test.com", "/", base::Time(), base::Time(),
- base::Time(), base::Time(), true, false,
- net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT, false);
- base::test::TestFuture<bool> future;
- GetCookieManager()->SetCanonicalCookie(
- *test_cookie,
- net::cookie_util::SimulatedCookieSource(*test_cookie, url::kHttpsScheme),
- net::CookieOptions(),
- base::BindOnce(net::cookie_util::IsCookieAccessResultInclude)
- .Then(future.GetCallback()));
- EXPECT_TRUE(future.Get());
-
- EXPECT_FALSE(GetCookies().empty());
- ASSERT_TRUE(RunClearAllCachedAuthTokensFunction());
- EXPECT_TRUE(GetCookies().empty());
-}
-
-INSTANTIATE_TEST_SUITE_P(
- All,
- ClearAllCachedAuthTokensFunctionTestWithPartitionParam,
- ::testing::Values(WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::Partition::GET_AUTH_TOKEN));
-
class OnSignInChangedEventTest : public IdentityTestWithSignin {
protected:
void SetUpOnMainThread() override {
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc b/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc
index 06a3fb6c919..0ab43bb2f4a 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc
@@ -4,26 +4,13 @@
#include "chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h"
-#include "base/functional/bind.h"
-#include "base/location.h"
-#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/extensions/api/identity/identity_api.h"
#include "chrome/browser/extensions/api/identity/identity_constants.h"
#include "chrome/browser/extensions/api/identity/web_auth_flow.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/extensions/api/identity.h"
-#include "content/public/browser/storage_partition.h"
-#include "services/network/public/mojom/cookie_manager.mojom.h"
namespace extensions {
-namespace {
-
-constexpr WebAuthFlow::Partition kPartitionsToClean[] = {
- WebAuthFlow::GET_AUTH_TOKEN, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW};
-
-}
-
IdentityClearAllCachedAuthTokensFunction::
IdentityClearAllCachedAuthTokensFunction() = default;
IdentityClearAllCachedAuthTokensFunction::
@@ -39,35 +26,7 @@ IdentityClearAllCachedAuthTokensFunction::Run() {
id_api->EraseGaiaIdForExtension(extension()->id());
id_api->token_cache()->EraseAllTokensForExtension(extension()->id());
- for (WebAuthFlow::Partition partition : kPartitionsToClean) {
- profile
- ->GetStoragePartition(
- WebAuthFlow::GetWebViewPartitionConfig(partition, profile))
- ->GetCookieManagerForBrowserProcess()
- ->DeleteCookies(
- network::mojom::CookieDeletionFilter::New(),
- base::BindOnce(
- &IdentityClearAllCachedAuthTokensFunction::OnCookiesDeleted,
- this));
- }
-
- // This object is retained by the DeleteCookies callbacks.
- return RespondLater();
-}
-
-void IdentityClearAllCachedAuthTokensFunction::OnCookiesDeleted(
- uint32_t num_deleted) {
- ++cleaned_partitions_;
-
- if (cleaned_partitions_ < std::size(kPartitionsToClean))
- return;
-
- // Post a task to ensure Respond() is not synchronously called from Run(). The
- // object is retained by this task.
- base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
- FROM_HERE,
- base::BindOnce(&IdentityClearAllCachedAuthTokensFunction::Respond, this,
- NoArguments()));
+ return RespondNow(NoArguments());
}
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h b/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h
index 066987d20f1..774f4d48cde 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h
+++ b/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h
@@ -21,10 +21,6 @@ class IdentityClearAllCachedAuthTokensFunction : public ExtensionFunction {
// ExtensionFunction:
ResponseAction Run() override;
-
- void OnCookiesDeleted(uint32_t num_deleted);
-
- size_t cleaned_partitions_ = 0;
};
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_constants.cc b/chromium/chrome/browser/extensions/api/identity/identity_constants.cc
index 37d177dd0b8..ec84ec0adf8 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_constants.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_constants.cc
@@ -25,11 +25,13 @@ const char kInvalidRedirect[] = "Did not redirect to the right URL.";
const char kOffTheRecord[] = "Identity API is disabled in incognito windows.";
const char kPageLoadFailure[] = "Authorization page could not be loaded.";
const char kPageLoadTimedOut[] = "Authorization page load timed out.";
-const char kSetAccountsInCookieFailure[] = "Account cookies could not be set.";
const char kInvalidConsentResult[] = "Returned an invalid consent result.";
const char kCanceled[] = "canceled";
const char kCannotCreateWindow[] =
"Couldn't create a browser window to display an authorization page.";
+const char kInvalidURLScheme[] =
+ "The auth url has an invalid scheme. Only http:// and https:// schemes are "
+ "allowed.";
const int kCachedRemoteConsentTTLSeconds = 1;
} // namespace identity_constants
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_constants.h b/chromium/chrome/browser/extensions/api/identity/identity_constants.h
index 91039aa4a2e..e8aa98376e0 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_constants.h
+++ b/chromium/chrome/browser/extensions/api/identity/identity_constants.h
@@ -22,10 +22,10 @@ extern const char kInvalidRedirect[];
extern const char kOffTheRecord[];
extern const char kPageLoadFailure[];
extern const char kPageLoadTimedOut[];
-extern const char kSetAccountsInCookieFailure[];
extern const char kInvalidConsentResult[];
extern const char kCanceled[];
extern const char kCannotCreateWindow[];
+extern const char kInvalidURLScheme[];
extern const int kCachedRemoteConsentTTLSeconds;
} // namespace identity_constants
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc
index e65baad32fc..670a7790de3 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc
@@ -64,8 +64,6 @@ std::string IdentityGetAuthTokenError::ToString() const {
return identity_constants::kOffTheRecord;
case State::kRemoteConsentPageLoadFailure:
return identity_constants::kPageLoadFailure;
- case State::kSetAccountsInCookieFailure:
- return identity_constants::kSetAccountsInCookieFailure;
case State::kInvalidConsentResult:
return identity_constants::kInvalidConsentResult;
case State::kCanceled:
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h
index 1624c64f0b4..84b4694931d 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h
+++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h
@@ -41,7 +41,7 @@ class IdentityGetAuthTokenError {
kOffTheRecord = 22,
// kPageLoadFailure = 23, // Deprecated
kRemoteConsentPageLoadFailure = 24,
- kSetAccountsInCookieFailure = 25,
+ // kSetAccountsInCookieFailure = 25, // Deprecated
kInvalidConsentResult = 26,
kCanceled = 27,
kInteractivityDenied = 28,
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
index 87dc7d1caae..04470955181 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
@@ -13,6 +13,7 @@
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
@@ -60,12 +61,12 @@ bool IsBrowserSigninAllowed(Profile* profile) {
return profile->GetPrefs()->GetBoolean(prefs::kSigninAllowed);
}
-std::string GetOAuth2MintTokenFlowVersion() {
- return std::string(version_info::GetVersionNumber());
+base::StringPiece GetOAuth2MintTokenFlowVersion() {
+ return version_info::GetVersionNumber();
}
-std::string GetOAuth2MintTokenFlowChannel() {
- return std::string(version_info::GetChannelString(chrome::GetChannel()));
+base::StringPiece GetOAuth2MintTokenFlowChannel() {
+ return version_info::GetChannelString(chrome::GetChannel());
}
void RecordFunctionResult(const IdentityGetAuthTokenError& error,
@@ -237,7 +238,7 @@ void IdentityGetAuthTokenFunction::OnReceivedExtensionAccountInfo(
#if BUILDFLAG(IS_CHROMEOS)
if (g_browser_process->browser_policy_connector()
->IsDeviceEnterpriseManaged()) {
- if (profiles::IsPublicSession()) {
+ if (profiles::IsManagedGuestSession()) {
CompleteFunctionWithError(IdentityGetAuthTokenError(
IdentityGetAuthTokenError::State::kNotAllowlistedInPublicSession));
return;
@@ -456,8 +457,9 @@ void IdentityGetAuthTokenFunction::StartMintToken(
switch (cache_status) {
case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND:
#if BUILDFLAG(IS_CHROMEOS)
- // Always force minting token for ChromeOS kiosk app and public session.
- if (profiles::IsPublicSession()) {
+ // Always force minting token for ChromeOS kiosk app and managed guest
+ // session.
+ if (profiles::IsManagedGuestSession()) {
CompleteFunctionWithError(
IdentityGetAuthTokenError(IdentityGetAuthTokenError::State::
kNotAllowlistedInPublicSession));
@@ -656,16 +658,10 @@ void IdentityGetAuthTokenFunction::OnGaiaRemoteConsentFlowFailed(
switch (failure) {
case GaiaRemoteConsentFlow::WINDOW_CLOSED:
- case GaiaRemoteConsentFlow::USER_NAVIGATED_AWAY:
error = IdentityGetAuthTokenError(
IdentityGetAuthTokenError::State::kRemoteConsentFlowRejected);
break;
- case GaiaRemoteConsentFlow::SET_ACCOUNTS_IN_COOKIE_FAILED:
- error = IdentityGetAuthTokenError(
- IdentityGetAuthTokenError::State::kSetAccountsInCookieFailure);
- break;
-
case GaiaRemoteConsentFlow::LOAD_FAILED:
error = IdentityGetAuthTokenError(
IdentityGetAuthTokenError::State::kRemoteConsentPageLoadFailure);
@@ -876,13 +872,13 @@ IdentityGetAuthTokenFunction::CreateMintTokenFlow() {
GetSigninScopedDeviceIdForProfile(GetProfile());
auto mint_token_flow = std::make_unique<OAuth2MintTokenFlow>(
this,
- OAuth2MintTokenFlow::Parameters(
+ OAuth2MintTokenFlow::Parameters::CreateForExtensionFlow(
extension()->id(), oauth2_client_id_,
- std::vector<std::string>(token_key_.scopes.begin(),
- token_key_.scopes.end()),
- enable_granular_permissions_, signin_scoped_device_id,
- GetSelectedUserId(), consent_result_, GetOAuth2MintTokenFlowVersion(),
- GetOAuth2MintTokenFlowChannel(), gaia_mint_token_mode_));
+ std::vector<base::StringPiece>(token_key_.scopes.begin(),
+ token_key_.scopes.end()),
+ gaia_mint_token_mode_, enable_granular_permissions_,
+ GetOAuth2MintTokenFlowVersion(), GetOAuth2MintTokenFlowChannel(),
+ signin_scoped_device_id, GetSelectedUserId(), consent_result_));
return mint_token_flow;
}
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc b/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc
index 810633b5b67..350fd13cfca 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc
@@ -14,6 +14,8 @@
#include "chrome/browser/extensions/api/identity/identity_constants.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/identity.h"
+#include "components/prefs/pref_service.h"
+#include "extensions/browser/pref_names.h"
namespace extensions {
@@ -26,7 +28,6 @@ IdentityLaunchWebAuthFlowFunction::Error WebAuthFlowFailureToError(
WebAuthFlow::Failure failure) {
switch (failure) {
case WebAuthFlow::WINDOW_CLOSED:
- case WebAuthFlow::USER_NAVIGATED_AWAY:
return IdentityLaunchWebAuthFlowFunction::Error::kUserRejected;
case WebAuthFlow::INTERACTION_REQUIRED:
return IdentityLaunchWebAuthFlowFunction::Error::kInteractionRequired;
@@ -62,6 +63,8 @@ std::string ErrorToString(IdentityLaunchWebAuthFlowFunction::Error error) {
return identity_constants::kPageLoadTimedOut;
case IdentityLaunchWebAuthFlowFunction::Error::kCannotCreateWindow:
return identity_constants::kCannotCreateWindow;
+ case IdentityLaunchWebAuthFlowFunction::Error::kInvalidURLScheme:
+ return identity_constants::kInvalidURLScheme;
}
}
@@ -99,6 +102,13 @@ ExtensionFunction::ResponseAction IdentityLaunchWebAuthFlowFunction::Run() {
EXTENSION_FUNCTION_VALIDATE(params);
GURL auth_url(params->details.url);
+ if (!auth_url.SchemeIsHTTPOrHTTPS()) {
+ Error error = Error::kInvalidURLScheme;
+
+ RecordHistogramFunctionResult(error);
+ return RespondNow(ExtensionFunction::Error(ErrorToString(error)));
+ }
+
WebAuthFlow::Mode mode =
params->details.interactive && *params->details.interactive
? WebAuthFlow::INTERACTIVE
@@ -120,14 +130,18 @@ ExtensionFunction::ResponseAction IdentityLaunchWebAuthFlowFunction::Run() {
// Set up acceptable target URLs. (Does not include chrome-extension
// scheme for this version of the API.)
- InitFinalRedirectURLPrefix(extension()->id());
+ InitFinalRedirectURLDomains(
+ extension()->id(),
+ Profile::FromBrowserContext(browser_context())
+ ->GetPrefs()
+ ->GetDict(extensions::pref_names::kOAuthRedirectUrls)
+ .FindList(extension()->id()));
AddRef(); // Balanced in OnAuthFlowSuccess/Failure.
auth_flow_ = std::make_unique<WebAuthFlow>(
- this, profile, auth_url, mode, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
- user_gesture(), abort_on_load_for_non_interactive,
- timeout_for_non_interactive);
+ this, profile, auth_url, mode, user_gesture(),
+ abort_on_load_for_non_interactive, timeout_for_non_interactive);
// An extension might call `launchWebAuthFlow()` with any URL. Add an infobar
// to attribute displayed URL to the extension.
auth_flow_->SetShouldShowInfoBar(extension()->name());
@@ -142,16 +156,26 @@ bool IdentityLaunchWebAuthFlowFunction::ShouldKeepWorkerAliveIndefinitely() {
return true;
}
-void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefixForTest(
+void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLDomainsForTest(
const std::string& extension_id) {
- InitFinalRedirectURLPrefix(extension_id);
+ InitFinalRedirectURLDomains(extension_id, nullptr);
}
-void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefix(
- const std::string& extension_id) {
- if (final_url_prefix_.is_empty()) {
- final_url_prefix_ = GURL(base::StringPrintf(
- kChromiumDomainRedirectUrlPattern, extension_id.c_str()));
+void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLDomains(
+ const std::string& extension_id,
+ const base::Value::List* redirect_urls) {
+ if (!final_url_domains_.empty()) {
+ return;
+ }
+ final_url_domains_.emplace_back(base::StringPrintf(
+ kChromiumDomainRedirectUrlPattern, extension_id.c_str()));
+ if (redirect_urls) {
+ for (const auto& value : *redirect_urls) {
+ GURL domain(value.GetString());
+ if (domain.is_valid()) {
+ final_url_domains_.push_back(domain.Resolve("/"));
+ }
+ }
}
}
@@ -168,14 +192,16 @@ void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure(
void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange(
const GURL& redirect_url) {
- if (redirect_url.GetWithEmptyPath() == final_url_prefix_) {
- RecordHistogramFunctionResult(
- IdentityLaunchWebAuthFlowFunction::Error::kNone);
- Respond(WithArguments(redirect_url.spec()));
- if (auth_flow_)
- auth_flow_.release()->DetachDelegateAndDelete();
- Release(); // Balanced in RunAsync.
+ if (!base::Contains(final_url_domains_, redirect_url.Resolve("/"))) {
+ return;
+ }
+ RecordHistogramFunctionResult(
+ IdentityLaunchWebAuthFlowFunction::Error::kNone);
+ Respond(WithArguments(redirect_url.spec()));
+ if (auth_flow_) {
+ auth_flow_.release()->DetachDelegateAndDelete();
}
+ Release(); // Balanced in RunAsync.
}
WebAuthFlow* IdentityLaunchWebAuthFlowFunction::GetWebAuthFlowForTesting() {
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h b/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h
index 9b4aace4145..2c52713b892 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h
+++ b/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h
@@ -38,13 +38,14 @@ class IdentityLaunchWebAuthFlowFunction : public ExtensionFunction,
kUnexpectedError = 5,
kPageLoadTimedOut = 6,
kCannotCreateWindow = 7,
- kMaxValue = kCannotCreateWindow,
+ kInvalidURLScheme = 8,
+ kMaxValue = kInvalidURLScheme,
};
IdentityLaunchWebAuthFlowFunction();
// Tests may override extension_id.
- void InitFinalRedirectURLPrefixForTest(const std::string& extension_id);
+ void InitFinalRedirectURLDomainsForTest(const std::string& extension_id);
WebAuthFlow* GetWebAuthFlowForTesting();
@@ -60,10 +61,11 @@ class IdentityLaunchWebAuthFlowFunction : public ExtensionFunction,
void OnAuthFlowTitleChange(const std::string& title) override {}
// Helper to initialize final URL prefix.
- void InitFinalRedirectURLPrefix(const std::string& extension_id);
+ void InitFinalRedirectURLDomains(const std::string& extension_id,
+ const base::Value::List* redirect_urls);
std::unique_ptr<WebAuthFlow> auth_flow_;
- GURL final_url_prefix_;
+ std::vector<GURL> final_url_domains_;
};
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_private_api.cc b/chromium/chrome/browser/extensions/api/identity/identity_private_api.cc
deleted file mode 100644
index 6737801a056..00000000000
--- a/chromium/chrome/browser/extensions/api/identity/identity_private_api.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/api/identity/identity_private_api.h"
-
-#include "chrome/browser/extensions/api/identity/identity_api.h"
-
-namespace extensions {
-
-IdentityPrivateSetConsentResultFunction::
- IdentityPrivateSetConsentResultFunction() = default;
-IdentityPrivateSetConsentResultFunction::
- ~IdentityPrivateSetConsentResultFunction() = default;
-
-ExtensionFunction::ResponseAction
-IdentityPrivateSetConsentResultFunction::Run() {
- absl::optional<Params> params = Params::Create(args());
- EXTENSION_FUNCTION_VALIDATE(params);
-
- IdentityAPI::GetFactoryInstance()
- ->Get(browser_context())
- ->SetConsentResult(params->result, params->window_id);
-
- return RespondNow(NoArguments());
-}
-
-} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_private_api.h b/chromium/chrome/browser/extensions/api/identity/identity_private_api.h
deleted file mode 100644
index a0c6cf921af..00000000000
--- a/chromium/chrome/browser/extensions/api/identity/identity_private_api.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_PRIVATE_API_H_
-#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_PRIVATE_API_H_
-
-#include "chrome/common/extensions/api/identity_private.h"
-#include "extensions/browser/extension_function.h"
-
-namespace extensions {
-
-class IdentityPrivateSetConsentResultFunction : public ExtensionFunction {
- public:
- DECLARE_EXTENSION_FUNCTION("identityPrivate.setConsentResult",
- IDENTITYPRIVATE_SETCONSENTRESULT)
-
- IdentityPrivateSetConsentResultFunction();
-
- private:
- using Params = api::identity_private::SetConsentResult::Params;
- ~IdentityPrivateSetConsentResultFunction() override;
-
- ExtensionFunction::ResponseAction Run() override;
-};
-
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_PRIVATE_API_H_
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_private_apitest.cc b/chromium/chrome/browser/extensions/api/identity/identity_private_apitest.cc
deleted file mode 100644
index 9fc9fc686be..00000000000
--- a/chromium/chrome/browser/extensions/api/identity/identity_private_apitest.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/api/identity/identity_private_api.h"
-
-#include "base/memory/scoped_refptr.h"
-#include "base/run_loop.h"
-#include "chrome/browser/extensions/api/identity/identity_api.h"
-#include "chrome/browser/extensions/extension_apitest.h"
-#include "content/public/test/browser_test.h"
-#include "extensions/browser/api_test_utils.h"
-
-namespace extensions {
-
-struct SetConsentResultParams {
- std::string consent_result;
- std::string window_id;
-};
-
-class IdentityPrivateApiTest : public ExtensionBrowserTest {
- protected:
- void SetUpOnMainThread() override {
- ExtensionBrowserTest::SetUpOnMainThread();
- callback_loop_ = std::make_unique<base::RunLoop>();
- // base::Unretained(this) is safe because the callback will be unregistered
- // on |callback_subscription_| destruction.
- callback_subscription_ = identity_api()->RegisterOnSetConsentResultCallback(
- base::BindRepeating(&IdentityPrivateApiTest::OnSetConsentResult,
- base::Unretained(this)));
- }
-
- IdentityAPI* identity_api() {
- return IdentityAPI::GetFactoryInstance()->Get(profile());
- }
-
- SetConsentResultParams WaitForConsentResult() {
- callback_loop_->Run();
- return {consent_result_, window_id_};
- }
-
- private:
- void OnSetConsentResult(const std::string& consent_result,
- const std::string& window_id) {
- consent_result_ = consent_result;
- window_id_ = window_id;
- callback_loop_->Quit();
- }
-
- std::string consent_result_;
- std::string window_id_;
- std::unique_ptr<base::RunLoop> callback_loop_;
- base::CallbackListSubscription callback_subscription_;
-};
-
-IN_PROC_BROWSER_TEST_F(IdentityPrivateApiTest, SetConsentResult) {
- scoped_refptr<ExtensionFunction> func =
- base::MakeRefCounted<IdentityPrivateSetConsentResultFunction>();
- bool success = api_test_utils::RunFunction(
- func.get(),
- std::string("[\"consent_result_value\", \"window_id_value\"]"),
- profile());
- ASSERT_TRUE(success);
- SetConsentResultParams params = WaitForConsentResult();
- EXPECT_EQ(params.consent_result, "consent_result_value");
- EXPECT_EQ(params.window_id, "window_id_value");
-}
-
-} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h b/chromium/chrome/browser/extensions/api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h
deleted file mode 100644
index cc378b42fa7..00000000000
--- a/chromium/chrome/browser/extensions/api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_TEST_SCOPED_SHOULD_ANIMATE_WEB_AUTH_FLOW_INFO_BAR_H_
-#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_TEST_SCOPED_SHOULD_ANIMATE_WEB_AUTH_FLOW_INFO_BAR_H_
-
-#include "chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace extensions {
-
-class TestScopedShouldAnimateWebAuthFlowInfoBar {
- public:
- explicit TestScopedShouldAnimateWebAuthFlowInfoBar(bool should_animate) {
- previous_state_ = WebAuthFlowInfoBarDelegate::should_animate_for_testing_;
- WebAuthFlowInfoBarDelegate::should_animate_for_testing_ = should_animate;
- }
-
- ~TestScopedShouldAnimateWebAuthFlowInfoBar() {
- WebAuthFlowInfoBarDelegate::should_animate_for_testing_ = previous_state_;
- }
-
- private:
- absl::optional<bool> previous_state_;
-};
-
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_TEST_SCOPED_SHOULD_ANIMATE_WEB_AUTH_FLOW_INFO_BAR_H_
diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc b/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc
index ee6bfbcbbb0..affc352d603 100644
--- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc
+++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc
@@ -7,31 +7,16 @@
#include <memory>
#include <utility>
-#include "base/base64.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
-#include "base/location.h"
-#include "base/notreached.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/trace_event/trace_event.h"
#include "chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h"
-#include "chrome/browser/extensions/component_loader.h"
-#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_navigator.h"
-#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/common/extensions/api/identity_private.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/grit/browser_resources.h"
-#include "components/guest_view/browser/guest_view_base.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
@@ -39,75 +24,22 @@
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
-#include "crypto/random.h"
-#include "extensions/browser/app_window/app_window.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "ui/base/page_transition_types.h"
-#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"
#include "url/url_constants.h"
-using content::RenderViewHost;
using content::WebContents;
using content::WebContentsObserver;
-using guest_view::GuestViewBase;
namespace extensions {
-namespace {
-
-// Returns whether `partition` should be persisted on disk.
-bool ShouldPersistStorage(WebAuthFlow::Partition partition) {
- switch (partition) {
- case WebAuthFlow::LAUNCH_WEB_AUTH_FLOW:
- return base::FeatureList::IsEnabled(kPersistentStorageForWebAuthFlow);
- case WebAuthFlow::GET_AUTH_TOKEN:
- return false;
- }
-
- NOTREACHED() << "Unexpected partition value " << partition;
- return false;
-}
-
-// Returns a unique identifier of the storage partition corresponding to
-// `partition`.
-std::string GetStoragePartitionId(WebAuthFlow::Partition partition) {
- switch (partition) {
- case WebAuthFlow::LAUNCH_WEB_AUTH_FLOW:
- return "launchWebAuthFlow";
- case WebAuthFlow::GET_AUTH_TOKEN:
- return "getAuthFlow";
- }
-
- NOTREACHED() << "Unexpected partition value " << partition;
- return std::string();
-}
-
-// Returns a partition name suitable to use in the `webview.partition`
-// parameter.
-std::string GetPartitionNameForWebView(WebAuthFlow::Partition partition) {
- std::string persist_prefix =
- ShouldPersistStorage(partition) ? "persist:" : "";
- return persist_prefix + GetStoragePartitionId(partition);
-}
-} // namespace
-
-namespace identity_private = api::identity_private;
-
-BASE_FEATURE(kPersistentStorageForWebAuthFlow,
- "PersistentStorageForWebAuthFlow",
- base::FEATURE_DISABLED_BY_DEFAULT);
-
WebAuthFlow::WebAuthFlow(
Delegate* delegate,
Profile* profile,
const GURL& provider_url,
Mode mode,
- Partition partition,
bool user_gesture,
AbortOnLoad abort_on_load_for_non_interactive,
absl::optional<base::TimeDelta> timeout_for_non_interactive)
@@ -115,7 +47,6 @@ WebAuthFlow::WebAuthFlow(
profile_(profile),
provider_url_(provider_url),
mode_(mode),
- partition_(partition),
user_gesture_(user_gesture),
abort_on_load_for_non_interactive_(abort_on_load_for_non_interactive),
timeout_for_non_interactive_(timeout_for_non_interactive),
@@ -130,7 +61,7 @@ WebAuthFlow::WebAuthFlow(
WebAuthFlow::~WebAuthFlow() {
DCHECK(!delegate_);
- if (using_auth_with_browser_tab_ && web_contents()) {
+ if (web_contents()) {
web_contents()->Close();
}
@@ -140,12 +71,6 @@ WebAuthFlow::~WebAuthFlow() {
// below may generate notifications.
WebContentsObserver::Observe(nullptr);
- if (!app_window_key_.empty()) {
- AppWindowRegistry::Get(profile_)->RemoveObserver(this);
-
- if (app_window_ && app_window_->web_contents())
- app_window_->web_contents()->Close();
- }
TRACE_EVENT_NESTABLE_ASYNC_END0("identity", "WebAuthFlow", this);
}
@@ -161,54 +86,12 @@ void WebAuthFlow::Start() {
DCHECK(profile_);
DCHECK(!profile_->IsOffTheRecord());
- if (base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) {
- using_auth_with_browser_tab_ = true;
-
- content::WebContents::CreateParams params(profile_);
- web_contents_ = content::WebContents::Create(params);
- WebContentsObserver::Observe(web_contents_.get());
+ content::WebContents::CreateParams params(profile_);
+ web_contents_ = content::WebContents::Create(params);
+ WebContentsObserver::Observe(web_contents_.get());
- content::NavigationController::LoadURLParams load_params(provider_url_);
- web_contents_->GetController().LoadURLWithParams(load_params);
-
- MaybeStartTimeout();
- return;
- }
-
- AppWindowRegistry::Get(profile_)->AddObserver(this);
-
- // Attach a random ID string to the window so we can recognize it
- // in OnAppWindowAdded.
- std::string random_bytes;
- crypto::RandBytes(base::WriteInto(&random_bytes, 33), 32);
- base::Base64Encode(random_bytes, &app_window_key_);
-
- // identityPrivate.onWebFlowRequest(app_window_key, provider_url_, mode_)
- base::Value::List args;
- args.Append(app_window_key_);
- args.Append(provider_url_.spec());
- if (mode_ == WebAuthFlow::INTERACTIVE)
- args.Append("interactive");
- else
- args.Append("silent");
- args.Append(GetPartitionNameForWebView(partition_));
-
- auto event =
- std::make_unique<Event>(events::IDENTITY_PRIVATE_ON_WEB_FLOW_REQUEST,
- identity_private::OnWebFlowRequest::kEventName,
- std::move(args), profile_);
- ExtensionSystem* system = ExtensionSystem::Get(profile_);
-
- extensions::ComponentLoader* component_loader =
- system->extension_service()->component_loader();
- if (!component_loader->Exists(extension_misc::kIdentityApiUiAppId)) {
- component_loader->Add(
- IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST,
- base::FilePath(FILE_PATH_LITERAL("identity_scope_approval_dialog")));
- }
-
- EventRouter::Get(profile_)->DispatchEventWithLazyListener(
- extension_misc::kIdentityApiUiAppId, std::move(event));
+ content::NavigationController::LoadURLParams load_params(provider_url_);
+ web_contents_->GetController().LoadURLWithParams(load_params);
MaybeStartTimeout();
}
@@ -219,67 +102,8 @@ void WebAuthFlow::DetachDelegateAndDelete() {
this);
}
-content::StoragePartition* WebAuthFlow::GetGuestPartition() {
- // When using the Auth through the Browser Tab, the guest partition shouldn't
- // be used, consider using `Profile::GetDefaultStoragePartition()` instead.
- if (base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) {
- return nullptr;
- }
-
- return profile_->GetStoragePartition(
- GetWebViewPartitionConfig(partition_, profile_));
-}
-
-const std::string& WebAuthFlow::GetAppWindowKey() const {
- return app_window_key_;
-}
-
-// static
-content::StoragePartitionConfig WebAuthFlow::GetWebViewPartitionConfig(
- Partition partition,
- content::BrowserContext* browser_context) {
- // This has to mirror the logic in WebViewGuest::CreateWebContents for
- // creating the correct StoragePartitionConfig.
- auto result = content::StoragePartitionConfig::Create(
- browser_context, extension_misc::kIdentityApiUiAppId,
- GetStoragePartitionId(partition),
- /*in_memory=*/!ShouldPersistStorage(partition));
- result.set_fallback_to_partition_domain_for_blob_urls(
- browser_context->IsOffTheRecord()
- ? content::StoragePartitionConfig::FallbackMode::
- kFallbackPartitionInMemory
- : content::StoragePartitionConfig::FallbackMode::
- kFallbackPartitionOnDisk);
- return result;
-}
-
-void WebAuthFlow::OnAppWindowAdded(AppWindow* app_window) {
- if (app_window->window_key() == app_window_key_ &&
- app_window->extension_id() == extension_misc::kIdentityApiUiAppId) {
- app_window_ = app_window;
- WebContentsObserver::Observe(app_window->web_contents());
- }
-}
-
-void WebAuthFlow::OnAppWindowRemoved(AppWindow* app_window) {
- if (app_window->window_key() == app_window_key_ &&
- app_window->extension_id() == extension_misc::kIdentityApiUiAppId) {
- app_window_ = nullptr;
- WebContentsObserver::Observe(nullptr);
-
- if (delegate_)
- delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
- }
-}
-
-bool WebAuthFlow::IsObservingProviderWebContents() const {
- return web_contents() &&
- (embedded_window_created_ || using_auth_with_browser_tab_);
-}
-
void WebAuthFlow::DisplayInfoBar() {
DCHECK(web_contents());
- DCHECK(using_auth_with_browser_tab_);
info_bar_delegate_ = WebAuthFlowInfoBarDelegate::Create(
web_contents(), info_bar_parameters_.extension_display_name);
@@ -291,11 +115,6 @@ void WebAuthFlow::CloseInfoBar() {
}
}
-bool WebAuthFlow::IsDisplayingAuthPageInTab() const {
- // If web_contents_ is nullptr, then the auth page tab is opened.
- return using_auth_with_browser_tab_ && !web_contents_;
-}
-
bool WebAuthFlow::DisplayAuthPageInPopupWindow() {
if (Browser::GetCreationStatusForProfile(profile_) !=
Browser::CreationStatus::kOk) {
@@ -318,15 +137,14 @@ bool WebAuthFlow::DisplayAuthPageInPopupWindow() {
}
void WebAuthFlow::BeforeUrlLoaded(const GURL& url) {
- if (delegate_ && IsObservingProviderWebContents()) {
+ if (delegate_) {
delegate_->OnAuthFlowURLChange(url);
}
}
void WebAuthFlow::AfterUrlLoaded() {
initial_url_loaded_ = true;
- if (delegate_ && IsObservingProviderWebContents() &&
- mode_ == WebAuthFlow::SILENT) {
+ if (delegate_ && mode_ == WebAuthFlow::SILENT) {
if (abort_on_load_for_non_interactive_ == AbortOnLoad::kYes) {
non_interactive_timeout_timer_->Stop();
delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED);
@@ -338,27 +156,11 @@ void WebAuthFlow::AfterUrlLoaded() {
// If `web_contents_` is nullptr, this means that the interactive tab has
// already been opened once.
- if (delegate_ && using_auth_with_browser_tab_ && web_contents_ &&
- mode_ == WebAuthFlow::INTERACTIVE) {
- switch (features::kWebAuthFlowInBrowserTabMode.Get()) {
- case features::WebAuthFlowInBrowserTabMode::kNewTab: {
- // Displays the auth page in a new tab attached to an existing/new
- // browser.
- chrome::ScopedTabbedBrowserDisplayer browser_displayer(profile_);
- NavigateParams params(browser_displayer.browser(),
- std::move(web_contents_));
- Navigate(&params);
- break;
- }
- case features::WebAuthFlowInBrowserTabMode::kPopupWindow: {
- bool is_auth_page_displayed = DisplayAuthPageInPopupWindow();
- if (!is_auth_page_displayed) {
- delegate_->OnAuthFlowFailure(
- WebAuthFlow::Failure::CANNOT_CREATE_WINDOW);
- return;
- }
- break;
- }
+ if (delegate_ && web_contents_ && mode_ == WebAuthFlow::INTERACTIVE) {
+ bool is_auth_page_displayed = DisplayAuthPageInPopupWindow();
+ if (!is_auth_page_displayed) {
+ delegate_->OnAuthFlowFailure(WebAuthFlow::Failure::CANNOT_CREATE_WINDOW);
+ return;
}
if (info_bar_parameters_.should_show) {
@@ -394,26 +196,6 @@ void WebAuthFlow::OnTimeout() {
}
}
-void WebAuthFlow::InnerWebContentsCreated(
- content::WebContents* inner_web_contents) {
- DCHECK(app_window_);
-
- if (!delegate_ || embedded_window_created_)
- return;
-
- // Switch from watching the app window to the guest inside it.
- embedded_window_created_ = true;
- WebContentsObserver::Observe(inner_web_contents);
-}
-
-void WebAuthFlow::PrimaryMainFrameRenderProcessGone(
- base::TerminationStatus status) {
- // When in `using_auth_with_browser_tab_` mode,
- // `WebAuthFlow::WebContentsDestroyed()` takes care of this flow.
- if (delegate_ && !using_auth_with_browser_tab_)
- delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
-}
-
void WebAuthFlow::WebContentsDestroyed() {
WebContentsObserver::Observe(nullptr);
if (delegate_) {
@@ -432,22 +214,6 @@ void WebAuthFlow::DidStopLoading() {
void WebAuthFlow::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
- // If the navigation is initiated by the user, the tab will exit the auth
- // flow screen, this should result in a declined authentication and deleting
- // the flow.
- // These conditions do not apply for the Popup Window, where the url bar is
- // deactivated and the user cannot navigate away directly, to allow going back
- // and forth within the same flow.
- if (IsDisplayingAuthPageInTab() &&
- features::kWebAuthFlowInBrowserTabMode.Get() !=
- features::WebAuthFlowInBrowserTabMode::kPopupWindow &&
- !navigation_handle->IsRendererInitiated()) {
- // Stop observing the web contents since it is not part of the flow anymore.
- WebContentsObserver::Observe(nullptr);
- delegate_->OnAuthFlowFailure(Failure::USER_NAVIGATED_AWAY);
- return;
- }
-
if (navigation_handle->IsInPrimaryMainFrame()) {
BeforeUrlLoaded(navigation_handle->GetURL());
}
diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow.h b/chromium/chrome/browser/extensions/api/identity/web_auth_flow.h
index acacaf3b44d..3fe9f2b11bd 100644
--- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow.h
+++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow.h
@@ -11,11 +11,8 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
-#include "content/public/browser/storage_partition_config.h"
#include "content/public/browser/web_contents_observer.h"
-#include "extensions/browser/app_window/app_window_registry.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "ui/gfx/geometry/rect.h"
#include "url/gurl.h"
class Profile;
@@ -25,22 +22,14 @@ class OneShotTimer;
class TickClock;
} // namespace base
-namespace content {
-class StoragePartition;
-}
-
namespace extensions {
class WebAuthFlowInfoBarDelegate;
-// When enabled, cookies in the `launchWebAuthFlow()` partition are persisted
-// across browser restarts.
-BASE_DECLARE_FEATURE(kPersistentStorageForWebAuthFlow);
-
// Controller class for web based auth flows. The WebAuthFlow creates
-// a dialog window in the scope approval component app by firing an
-// event. A webview embedded in the dialog will navigate to the
-// |provider_url| passed to the WebAuthFlow constructor.
+// a browser popup window (or a new tab based on the feature setting)
+// with a webview that will navigate to the |provider_url| passed to the
+// WebAuthFlow constructor.
//
// The WebAuthFlow monitors the WebContents of the webview, and
// notifies its delegate interface any time the WebContents navigates
@@ -54,24 +43,17 @@ BASE_DECLARE_FEATURE(kPersistentStorageForWebAuthFlow);
//
// A WebAuthFlow can be started in Mode::SILENT, which never displays
// a window. If a window would be required, the flow fails.
-class WebAuthFlow : public content::WebContentsObserver,
- public AppWindowRegistry::Observer {
+class WebAuthFlow : public content::WebContentsObserver {
public:
enum Mode {
INTERACTIVE, // Show UI to the user if necessary.
SILENT // No UI should be shown.
};
- enum Partition {
- GET_AUTH_TOKEN, // Use the getAuthToken() partition.
- LAUNCH_WEB_AUTH_FLOW // Use the launchWebAuthFlow() partition.
- };
-
enum Failure {
WINDOW_CLOSED, // Window closed by user (app or tab).
INTERACTION_REQUIRED, // Non-redirect page load in silent mode.
LOAD_FAILED,
- USER_NAVIGATED_AWAY, // The user navigated away from the auth page.
TIMED_OUT,
CANNOT_CREATE_WINDOW // Couldn't create a browser window.
};
@@ -110,7 +92,6 @@ class WebAuthFlow : public content::WebContentsObserver,
Profile* profile,
const GURL& provider_url,
Mode mode,
- Partition partition,
bool user_gesture,
AbortOnLoad abort_on_load_for_non_interactive = AbortOnLoad::kYes,
absl::optional<base::TimeDelta> timeout_for_non_interactive =
@@ -131,19 +112,6 @@ class WebAuthFlow : public content::WebContentsObserver,
// Prevents further calls to the delegate and deletes the flow.
void DetachDelegateAndDelete();
- // Returns a StoragePartition of the guest webview. Used to inject cookies
- // into Gaia page. Can override for testing.
- virtual content::StoragePartition* GetGuestPartition();
-
- // Returns an ID string attached to the window. Can override for testing.
- virtual const std::string& GetAppWindowKey() const;
-
- // Returns the StoragePartitionConfig for a given |partition| used in the
- // WebAuthFlow.
- static content::StoragePartitionConfig GetWebViewPartitionConfig(
- Partition partition,
- content::BrowserContext* browser_context);
-
// This call will make the interactive mode, that opens up a browser tab for
// auth, display an Infobar that shows the extension name.
void SetShouldShowInfoBar(const std::string& extension_display_name);
@@ -152,16 +120,8 @@ class WebAuthFlow : public content::WebContentsObserver,
base::WeakPtr<WebAuthFlowInfoBarDelegate> GetInfoBarDelegateForTesting();
private:
- // ::AppWindowRegistry::Observer implementation.
- void OnAppWindowAdded(AppWindow* app_window) override;
- void OnAppWindowRemoved(AppWindow* app_window) override;
-
// WebContentsObserver implementation.
void DidStopLoading() override;
- void InnerWebContentsCreated(
- content::WebContents* inner_web_contents) override;
- void PrimaryMainFrameRenderProcessGone(
- base::TerminationStatus status) override;
void WebContentsDestroyed() override;
void TitleWasSet(content::NavigationEntry* entry) override;
void DidStartNavigation(
@@ -177,31 +137,17 @@ class WebAuthFlow : public content::WebContentsObserver,
void MaybeStartTimeout();
void OnTimeout();
- bool IsObservingProviderWebContents() const;
-
bool DisplayAuthPageInPopupWindow();
void DisplayInfoBar();
void CloseInfoBar();
- bool IsDisplayingAuthPageInTab() const;
-
raw_ptr<Delegate> delegate_ = nullptr;
const raw_ptr<Profile> profile_;
const GURL provider_url_;
const Mode mode_;
- const Partition partition_;
const bool user_gesture_;
- // Variables used only if displaying the auth flow in an app window.
- raw_ptr<AppWindow> app_window_ = nullptr;
- std::string app_window_key_;
- bool embedded_window_created_ = false;
-
- // Variables used only if displaying the auth flow in a browser tab.
- //
- // Checks that the auth with browser tab is activated.
- bool using_auth_with_browser_tab_ = false;
// WebContents used to initialize the authentication. It is not displayed
// and not owned by browser window. This WebContents is observed by
// `this`. When this value becomes nullptr, this means that the browser tab
diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_browsertest.cc b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_browsertest.cc
index d2322bcd205..98fbef9f164 100644
--- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_browsertest.cc
@@ -8,10 +8,8 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
-#include "base/test/test_future.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/time/time.h"
-#include "chrome/browser/extensions/api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h"
#include "chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
@@ -21,11 +19,9 @@
#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
-#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
@@ -78,7 +74,6 @@ class WebAuthFlowBrowserTest : public InProcessBrowserTest {
void StartWebAuthFlow(
const GURL& url,
- WebAuthFlow::Partition partition = WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
WebAuthFlow::Mode mode = WebAuthFlow::Mode::INTERACTIVE,
Profile* profile = nullptr,
WebAuthFlow::AbortOnLoad abort_on_load_for_non_interactive =
@@ -89,7 +84,7 @@ class WebAuthFlowBrowserTest : public InProcessBrowserTest {
profile = browser()->profile();
web_auth_flow_ = std::make_unique<WebAuthFlow>(
- &mock_web_auth_flow_delegate_, profile, url, mode, partition,
+ &mock_web_auth_flow_delegate_, profile, url, mode,
/*user_gesture=*/true, abort_on_load_for_non_interactive,
timeout_for_non_interactive);
@@ -120,17 +115,8 @@ class WebAuthFlowBrowserTest : public InProcessBrowserTest {
scoped_refptr<base::TestMockTimeTaskRunner> timeout_task_runner_;
};
-class WebAuthFlowInBrowserTabParamBrowserTest
- : public WebAuthFlowBrowserTest,
- public testing::WithParamInterface<bool> {
+class WebAuthFlowInBrowserTabParamBrowserTest : public WebAuthFlowBrowserTest {
public:
- WebAuthFlowInBrowserTabParamBrowserTest() {
- scoped_feature_list_.InitWithFeatureState(
- features::kWebAuthFlowInBrowserTab, use_tab_feature_enabled());
- }
-
- bool use_tab_feature_enabled() { return GetParam(); }
-
bool JsRedirectToUrl(const GURL& url) {
content::TestNavigationObserver redirect_observer(url);
redirect_observer.WatchExistingWebContents();
@@ -142,12 +128,9 @@ class WebAuthFlowInBrowserTabParamBrowserTest
}
return result;
}
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
};
-IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest,
OnAuthFlowURLChangeCalled) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
@@ -163,7 +146,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
navigation_observer.WaitForNavigationFinished();
}
-IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest,
OnAuthFlowFailureChangeCalled) {
// Navigate to a url that doesn't exist.
const GURL error_url = embedded_test_server()->GetURL("/error");
@@ -182,7 +165,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// Tests that the flow launched in silent mode with default parameters will
// terminate immediately with the "interacation required" error if the page
// loads and does not navigate to the redirect URL.
-IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest,
OnAuthFlowFailureCalledInteractionRequired) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
@@ -195,8 +178,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// In SILENT mode, DidStopLoading() will force the auth flow to fail if it has
// not already redirected, because we did not specify a timeout.
EXPECT_CALL(mock(), OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED));
- StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::SILENT);
+ StartWebAuthFlow(auth_url, WebAuthFlow::SILENT);
navigation_observer.Wait();
}
@@ -205,7 +187,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// `abortOnLoadForNonInteractive` set to `false` will terminate with the
// "interaction required" after a specified timeout if the page loads and does
// not navigate to the redirect URL.
-IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest,
OnAuthFlowInteractionRequiredWithTimeout) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
@@ -218,8 +200,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// In SILENT mode, DidStopLoading() will wait for our specified 50ms timeout
// before calling OnAuthFlowFailure.
EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0);
- StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::SILENT, /*profile=*/nullptr,
+ StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr,
WebAuthFlow::AbortOnLoad::kNo,
/*timeout_for_non_interactive=*/base::Milliseconds(50));
navigation_observer.Wait();
@@ -239,7 +220,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// `abortOnLoadForNonInteractive` set to `false` will terminate with the
// "interaction required" error after a default timeout if the page loads and
// does not navigate to the redirect URL.
-IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest,
OnAuthFlowInteractionRequiredWithDefaultTimeout) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
@@ -252,8 +233,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// In SILENT mode, DidStopLoading() will wait for the default 1 minute timeout
// before calling OnAuthFlowFailure.
EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0);
- StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::SILENT, /*profile=*/nullptr,
+ StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr,
WebAuthFlow::AbortOnLoad::kNo);
navigation_observer.Wait();
testing::Mock::VerifyAndClearExpectations(&mock());
@@ -274,7 +254,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// set will terminate with the "timed out" error after a timeout if the page
// fails to load (distinct from the flow failing to navigate to the redirect URL
// in time).
-IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest,
OnAuthFlowPageLoadTimeout) {
const GURL auth_url = embedded_test_server()->GetURL("/hung-after-headers");
@@ -287,8 +267,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// In SILENT mode, DidStopLoading() will wait for our specified 50ms timeout
// before calling OnAuthFlowFailure.
EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0);
- StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::SILENT, /*profile=*/nullptr,
+ StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr,
WebAuthFlow::AbortOnLoad::kYes,
/*timeout_for_non_interactive=*/base::Milliseconds(50));
// Wait for navigation to the failing page to start first.
@@ -310,7 +289,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// `abortOnLoadForNonInteractive` set to `false` and
// `timeoutMsForNonInteractive` set will succeed if it navigates to the redirect
// URL before the timeout.
-IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest,
OnAuthFlowRedirectBeforeTimeout) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
@@ -323,8 +302,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// In SILENT mode, DidStopLoading() will wait for our specified 50ms timeout
// before calling OnAuthFlowFailure.
EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0);
- StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::SILENT, /*profile=*/nullptr,
+ StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr,
WebAuthFlow::AbortOnLoad::kNo,
/*timeout_for_non_interactive=*/base::Milliseconds(50));
@@ -344,7 +322,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// Tests that the loaded auth page can redirect multiple times and fails only
// after the timeout.
-IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest,
OnAuthFlowMultipleRedirects) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
@@ -357,8 +335,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
// In SILENT mode, DidStopLoading() will wait for our specified 50ms timeout
// before calling OnAuthFlowFailure.
EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0);
- StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::SILENT, /*profile=*/nullptr,
+ StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr,
WebAuthFlow::AbortOnLoad::kNo,
/*timeout_for_non_interactive=*/base::Milliseconds(50));
@@ -396,126 +373,6 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest,
timeout_task_runner()->FastForwardBy(base::Milliseconds(30));
}
-INSTANTIATE_TEST_SUITE_P(
- ,
- WebAuthFlowInBrowserTabParamBrowserTest,
- testing::Bool(),
- [](const testing::TestParamInfo<
- WebAuthFlowInBrowserTabParamBrowserTest::ParamType>& info) {
- return base::StrCat(
- {info.param ? "With" : "Without", "WebAuthFlowInBrowserTab"});
- });
-
-class WebAuthFlowGuestPartitionParamTest
- : public WebAuthFlowBrowserTest,
- public testing::WithParamInterface<
- std::tuple<bool, WebAuthFlow::Partition>> {
- public:
- WebAuthFlowGuestPartitionParamTest() {
- std::vector<base::test::FeatureRef> enabled_features;
- std::vector<base::test::FeatureRef> disabled_features;
-
- persist_storage_feature_enabled()
- ? enabled_features.push_back(kPersistentStorageForWebAuthFlow)
- : disabled_features.push_back(kPersistentStorageForWebAuthFlow);
-
- // Explicitly disable the `kWebAuthFlowInBrowserTab` feature as it is
- // incompatible with the Guest Partition tests and
- // `kPersistentStorageForWebAuthFlow`.
- disabled_features.push_back(features::kWebAuthFlowInBrowserTab);
-
- scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
- }
-
- bool persist_storage_feature_enabled() { return std::get<0>(GetParam()); }
-
- WebAuthFlow::Partition partition() { return std::get<1>(GetParam()); }
-
- void LoadWebAuthFlow() {
- const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
-
- // Observer for waiting until a navigation to a url has finished.
- content::TestNavigationObserver navigation_observer(auth_url);
- navigation_observer.StartWatchingNewWebContents();
-
- StartWebAuthFlow(auth_url, partition());
- EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url));
-
- navigation_observer.WaitForNavigationFinished();
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-// Tests that the partition returned by `WebAuthFlow::GetGuestPartition()`
-// matches the one used by the webview.
-IN_PROC_BROWSER_TEST_P(WebAuthFlowGuestPartitionParamTest, GetGuestPartition) {
- LoadWebAuthFlow();
-
- // Set a test cookie on the page.
- ASSERT_TRUE(
- content::ExecJs(web_contents(), "document.cookie = \"testCookie=1\""));
-
- // Verify that the cookie was added to the guest partition.
- base::test::TestFuture<const net::CookieList&> get_cookies_future;
- web_auth_flow()
- ->GetGuestPartition()
- ->GetCookieManagerForBrowserProcess()
- ->GetAllCookies(get_cookies_future.GetCallback());
- const net::CookieList cookies = get_cookies_future.Get();
- ASSERT_EQ(1u, cookies.size());
- EXPECT_EQ("testCookie", cookies[0].Name());
- EXPECT_EQ("1", cookies[0].Value());
-}
-
-IN_PROC_BROWSER_TEST_P(WebAuthFlowGuestPartitionParamTest,
- PRE_PersistenceTest) {
- LoadWebAuthFlow();
- // Set a test cookie on the page.
- ASSERT_TRUE(content::ExecJs(
- web_contents(), "document.cookie = \"testCookie=1; max-age=3600\""));
-}
-
-IN_PROC_BROWSER_TEST_P(WebAuthFlowGuestPartitionParamTest, PersistenceTest) {
- LoadWebAuthFlow();
-
- base::test::TestFuture<const net::CookieList&> get_cookies_future;
- web_auth_flow()
- ->GetGuestPartition()
- ->GetCookieManagerForBrowserProcess()
- ->GetAllCookies(get_cookies_future.GetCallback());
- const net::CookieList cookies = get_cookies_future.Get();
-
- // Verify that the cookie set in the previous test is persisted for the
- // webAuthFlow if the feature is enabled.
- // Read from the cookie store directly rather than execute a script on the
- // auth page because the page URL changes between test (test server doesn't
- // have a fixed port).
- if (persist_storage_feature_enabled() &&
- partition() == WebAuthFlow::LAUNCH_WEB_AUTH_FLOW) {
- ASSERT_EQ(1u, cookies.size());
- EXPECT_EQ("testCookie", cookies[0].Name());
- EXPECT_EQ("1", cookies[0].Value());
- } else {
- EXPECT_EQ(0u, cookies.size());
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ,
- WebAuthFlowGuestPartitionParamTest,
- testing::Combine(testing::Bool(),
- testing::Values(WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::GET_AUTH_TOKEN)),
- [](const testing::TestParamInfo<
- WebAuthFlowGuestPartitionParamTest::ParamType>& info) {
- return base::StrCat(
- {std::get<0>(info.param) ? "FeatureOn" : "FeatureOff",
- std::get<1>(info.param) == WebAuthFlow::LAUNCH_WEB_AUTH_FLOW
- ? "WebAuthFlow"
- : "GetAuthToken"});
- });
class WebAuthFlowFencedFrameTest
: public WebAuthFlowInBrowserTabParamBrowserTest {
public:
@@ -527,7 +384,7 @@ class WebAuthFlowFencedFrameTest
content::test::FencedFrameTestHelper fenced_frame_helper_;
};
-IN_PROC_BROWSER_TEST_P(WebAuthFlowFencedFrameTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowFencedFrameTest,
FencedFrameNavigationSuccess) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
@@ -554,7 +411,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowFencedFrameTest,
embedded_test_server()->GetURL("/fenced_frames/title1.html")));
}
-IN_PROC_BROWSER_TEST_P(WebAuthFlowFencedFrameTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowFencedFrameTest,
FencedFrameNavigationFailure) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
@@ -582,27 +439,6 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowFencedFrameTest,
embedded_test_server()->GetURL("/error"), net::Error::ERR_FAILED));
}
-INSTANTIATE_TEST_SUITE_P(,
- WebAuthFlowFencedFrameTest,
- testing::Bool(),
- [](const testing::TestParamInfo<
- WebAuthFlowFencedFrameTest::ParamType>& info) {
- return base::StrCat({info.param ? "With" : "Without",
- "WebAuthFlowInBrowserTab"});
- });
-
-class WebAuthFlowWithBrowserTabBrowserTest : public WebAuthFlowBrowserTest {
- public:
- WebAuthFlowWithBrowserTabBrowserTest() {
- // By default the feature param is {{"browser_tab_mode", "popup_window"}}.
- scoped_feature_list_.InitAndEnableFeature(
- features::kWebAuthFlowInBrowserTab);
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
// This test is in two parts:
// - First create a WebAuthFlow in interactive mode that will create a new tab
// with the auth_url.
@@ -611,15 +447,14 @@ class WebAuthFlowWithBrowserTabBrowserTest : public WebAuthFlowBrowserTest {
//
// These two tests are combined into one in order not to re-test the tab
// creation twice.
-IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest,
InteractivePopupWindowCreatedWithAuthURL_ThenCloseTab) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
content::TestNavigationObserver navigation_observer(auth_url);
navigation_observer.StartWatchingNewWebContents();
EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url));
- StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::Mode::INTERACTIVE);
+ StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE);
const char extension_name[] = "extension_name";
web_auth_flow()->SetShouldShowInfoBar(extension_name);
@@ -650,15 +485,14 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
}
IN_PROC_BROWSER_TEST_F(
- WebAuthFlowWithBrowserTabBrowserTest,
+ WebAuthFlowBrowserTest,
InteractivePopupWindowCreatedWithAuthURL_NavigationInURLDoesNotBreakTheFlow) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
content::TestNavigationObserver navigation_observer(auth_url);
navigation_observer.StartWatchingNewWebContents();
EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url));
- StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::Mode::INTERACTIVE);
+ StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE);
web_auth_flow()->SetShouldShowInfoBar("extension name");
navigation_observer.Wait();
@@ -683,13 +517,6 @@ IN_PROC_BROWSER_TEST_F(
// Simulate an internal navigation, such as an authentication that needs an
// input of username and password on two different pages/urls.
GURL new_url = embedded_test_server()->GetURL("/title2.html");
- // Below a first navigation will be done, then going back on the initial auth
- // page, in the popup window mode the error should not trigger and the auth
- // flow should stay alive.
- EXPECT_CALL(mock(),
- OnAuthFlowFailure(WebAuthFlow::Failure::USER_NAVIGATED_AWAY))
- .Times(0);
-
EXPECT_CALL(mock(), OnAuthFlowURLChange(new_url));
ASSERT_TRUE(content::NavigateToURL(web_contents(), new_url));
@@ -713,7 +540,7 @@ IN_PROC_BROWSER_TEST_F(
}
IN_PROC_BROWSER_TEST_F(
- WebAuthFlowWithBrowserTabBrowserTest,
+ WebAuthFlowBrowserTest,
InteractiveNoBrowser_WebAuthCreatesBrowserWithPopupWindow) {
Profile* profile = browser()->profile();
// Simulates an extension being opened, in order for the profile not to be
@@ -730,8 +557,7 @@ IN_PROC_BROWSER_TEST_F(
navigation_observer.StartWatchingNewWebContents();
EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url));
- StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::Mode::INTERACTIVE, profile);
+ StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE, profile);
navigation_observer.Wait();
@@ -746,7 +572,7 @@ IN_PROC_BROWSER_TEST_F(
// This is a regression test for crbug/1445824, makes sure the opened popup
// window does not trigger Session restore.
-IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest,
InteractiveNoBrowser_NotActivatingSessionRestore) {
Profile* profile = browser()->profile();
@@ -767,8 +593,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
navigation_observer.StartWatchingNewWebContents();
EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url));
- StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::Mode::INTERACTIVE, profile);
+ StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE, profile);
navigation_observer.Wait();
// Makes sure only one browser is created and profile is not trying to restore
@@ -785,8 +610,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
auth_url);
}
-IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
- SilentNewTabNotCreated) {
+IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest, SilentNewTabNotCreated) {
TabStripModel* tabs = browser()->tab_strip_model();
int initial_tab_count = tabs->count();
@@ -797,8 +621,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
EXPECT_CALL(mock(),
OnAuthFlowFailure(WebAuthFlow::Failure::INTERACTION_REQUIRED));
EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url));
- StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::Mode::SILENT);
+ StartWebAuthFlow(auth_url, WebAuthFlow::Mode::SILENT);
navigation_observer.Wait();
@@ -806,15 +629,14 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
EXPECT_EQ(tabs->count(), initial_tab_count);
}
-IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest,
InteractiveNewTabCreatedWithAuthURL_NoInfoBarByDefault) {
const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
content::TestNavigationObserver navigation_observer(auth_url);
navigation_observer.StartWatchingNewWebContents();
EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url));
- StartWebAuthFlow(auth_url, WebAuthFlow::Partition::GET_AUTH_TOKEN,
- WebAuthFlow::Mode::INTERACTIVE);
+ StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE);
navigation_observer.Wait();
@@ -830,7 +652,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
EXPECT_FALSE(infobar_delegate);
}
-IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest,
PopupWindowOpened_ThenCloseWindow) {
size_t initial_browser_count = chrome::GetTotalBrowserCount();
@@ -839,8 +661,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
navigation_observer.StartWatchingNewWebContents();
EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url));
- StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::Mode::INTERACTIVE);
+ StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE);
navigation_observer.Wait();
@@ -865,7 +686,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest,
}
IN_PROC_BROWSER_TEST_F(
- WebAuthFlowWithBrowserTabBrowserTest,
+ WebAuthFlowBrowserTest,
Interactive_MarkedForDeletionProfileNotAllowedToCreatePopupWindow) {
// Marking active profile for deletion.
MarkProfileDirectoryForDeletion(browser()->profile()->GetPath());
@@ -880,78 +701,8 @@ IN_PROC_BROWSER_TEST_F(
// should return an error.
EXPECT_CALL(mock(),
OnAuthFlowFailure(WebAuthFlow::Failure::CANNOT_CREATE_WINDOW));
- StartWebAuthFlow(auth_url, WebAuthFlow::Partition::GET_AUTH_TOKEN,
- WebAuthFlow::Mode::INTERACTIVE);
+ StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE);
navigation_observer.Wait();
}
-class WebAuthFlowWithBrowserTabInNewTabBrowserTest
- : public WebAuthFlowBrowserTest {
- public:
- WebAuthFlowWithBrowserTabInNewTabBrowserTest() {
- // Enables feature with New tab mode.
- scoped_feature_list_.InitAndEnableFeatureWithParameters(
- features::kWebAuthFlowInBrowserTab, {{"browser_tab_mode", "new_tab"}});
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(
- WebAuthFlowWithBrowserTabInNewTabBrowserTest,
- InteractiveNewTabCreatedWithAuthURL_ThenChangeURLBeforeAuthResult) {
- const GURL auth_url = embedded_test_server()->GetURL("/title1.html");
- content::TestNavigationObserver navigation_observer(auth_url);
- navigation_observer.StartWatchingNewWebContents();
-
- EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url));
- // Remove the animation mainly for the deleting part as it could create
- // flakiness when checking for the deletion of the info bar.
- TestScopedShouldAnimateWebAuthFlowInfoBar should_animate(false);
- StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW,
- WebAuthFlow::Mode::INTERACTIVE);
- web_auth_flow()->SetShouldShowInfoBar("extension name");
-
- navigation_observer.Wait();
-
- //---------------------------------------------------------------------
- // Browser-initiated URL change in the opened tab before completing the auth
- // flow should trigger an auth flow failure.
- //---------------------------------------------------------------------
- testing::Mock::VerifyAndClearExpectations(&mock());
-
- // Keeping a reference to the info bar delegate to check later.
- base::WeakPtr<WebAuthFlowInfoBarDelegate> auth_info_bar =
- web_auth_flow()->GetInfoBarDelegateForTesting();
- ASSERT_TRUE(auth_info_bar);
-
- Browser* newtab_browser = chrome::FindBrowserWithWebContents(web_contents());
- EXPECT_EQ(browser(), newtab_browser);
- TabStripModel* tabs = newtab_browser->tab_strip_model();
-
- // Simulating a non user navigation, it shouldn't break the flow.
- GURL internal_url = embedded_test_server()->GetURL("/title2.html");
- EXPECT_CALL(mock(), OnAuthFlowURLChange(internal_url));
- EXPECT_CALL(mock(), OnAuthFlowFailure(testing::_)).Times(0);
- ASSERT_TRUE(content::NavigateToURLFromRenderer(web_contents(), internal_url));
- EXPECT_TRUE(web_auth_flow());
- EXPECT_TRUE(auth_info_bar);
- testing::Mock::VerifyAndClearExpectations(&mock());
-
- // Simulating user manually navigating to another URL.
- GURL browsing_url = embedded_test_server()->GetURL("/simple.html");
- EXPECT_CALL(mock(),
- OnAuthFlowFailure(WebAuthFlow::Failure::USER_NAVIGATED_AWAY));
- ASSERT_TRUE(content::NavigateToURL(web_contents(), browsing_url));
-
- // New tab is not expected to be closed, it is now used for navigation and
- // not part of the flow anymore.
- EXPECT_FALSE(web_contents());
- EXPECT_FALSE(web_auth_flow());
- EXPECT_EQ(tabs->GetActiveWebContents()->GetLastCommittedURL(), browsing_url);
- // Infobar should be closed on navigation.
- EXPECT_FALSE(auth_info_bar);
-}
-
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.cc b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.cc
index c9ad487f7e1..a84f713fd5c 100644
--- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.cc
@@ -16,9 +16,6 @@
namespace extensions {
-absl::optional<bool> WebAuthFlowInfoBarDelegate::should_animate_for_testing_ =
- absl::nullopt;
-
base::WeakPtr<WebAuthFlowInfoBarDelegate> WebAuthFlowInfoBarDelegate::Create(
content::WebContents* web_contents,
const std::string& extension_name) {
@@ -67,12 +64,4 @@ void WebAuthFlowInfoBarDelegate::CloseInfoBar() {
infobar()->RemoveSelf();
}
-bool WebAuthFlowInfoBarDelegate::ShouldAnimate() const {
- if (should_animate_for_testing_.has_value()) {
- return should_animate_for_testing_.value();
- }
-
- return ConfirmInfoBarDelegate::ShouldAnimate();
-}
-
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h
index 15429e47fb3..88eee975166 100644
--- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h
+++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h
@@ -8,7 +8,6 @@
#include "components/infobars/core/confirm_infobar_delegate.h"
#include "base/memory/weak_ptr.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
class WebContents;
@@ -16,8 +15,6 @@ class WebContents;
namespace extensions {
-class TestScopedShouldAnimateWebAuthFlowInfoBar;
-
// Infobar used by extension auth flow `chrome.identity.launchWebAuthFlow()`
// when authentication is done through a Browser Tab. A browser tab is opened
// when needing action from the user in this flow.
@@ -40,19 +37,13 @@ class WebAuthFlowInfoBarDelegate : public ConfirmInfoBarDelegate {
// ConfirmInfoBarDelegate:
std::u16string GetMessageText() const override;
int GetButtons() const override;
- bool ShouldAnimate() const override;
// Closes the info bar this delegate is associated with.
void CloseInfoBar();
private:
- friend TestScopedShouldAnimateWebAuthFlowInfoBar;
-
explicit WebAuthFlowInfoBarDelegate(const std::string& extension_name);
- // Only controlled by `TestScopedShouldAnimateWebAuthFlowInfoBar`.
- static absl::optional<bool> should_animate_for_testing_;
-
const std::string extension_name_;
base::WeakPtrFactory<WebAuthFlowInfoBarDelegate> weak_factory_{this};
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.cc b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.cc
index ac96347fa34..80ae9c39a8f 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.cc
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.cc
@@ -13,6 +13,7 @@
#include "chromeos/lacros/lacros_service.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/event_router.h"
+#include "extensions/common/extension_id.h"
namespace image_writer_api = extensions::api::image_writer_private;
@@ -113,7 +114,7 @@ class ImageWriterControllerLacros::ImageWriterClientLacros
// Note: |this| is deleted at this point.
}
- const std::string extension_id_;
+ const ExtensionId extension_id_;
// Both pointers of |browser_context_| and |controller_| are guaranteed
// to be valid for the lifetime of this class, as destruction of either
// BrowserContext or ImageWriterControllerLacros will result in synchronous
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc b/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
index 614302e4896..b0734b96152 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
@@ -25,7 +25,6 @@
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_host.h"
-#include "extensions/browser/notification_types.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc
index 870709971da..adc0b2b3d3a 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc
@@ -102,7 +102,7 @@ class RemovableStorageProviderChromeOsUnitTest : public testing::Test {
}
content::BrowserTaskEnvironment task_environment_;
- raw_ptr<ash::disks::MockDiskMountManager, ExperimentalAsh>
+ raw_ptr<ash::disks::MockDiskMountManager, DanglingUntriaged | ExperimentalAsh>
disk_mount_manager_mock_;
scoped_refptr<StorageDeviceList> devices_;
};
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc
index 3add088c13c..1ed76b28bcd 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc
@@ -11,8 +11,8 @@
#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
#include <stdint.h>
-#include "base/mac/foundation_util.h"
-#include "base/mac/scoped_cftyperef.h"
+#include "base/apple/foundation_util.h"
+#include "base/apple/scoped_cftyperef.h"
#include "base/mac/scoped_ioobject.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/sys_string_conversions.h"
@@ -27,7 +27,7 @@ RemovableStorageProvider::PopulateDeviceList() {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
// Match only writable whole-disks.
- base::ScopedCFTypeRef<CFMutableDictionaryRef> matching(
+ base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> matching(
IOServiceMatching(kIOMediaClass));
CFDictionaryAddValue(matching, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
CFDictionaryAddValue(matching, CFSTR(kIOMediaWritableKey), kCFBooleanTrue);
@@ -54,7 +54,7 @@ RemovableStorageProvider::PopulateDeviceList() {
if (!is_suitable)
continue;
- base::ScopedCFTypeRef<CFMutableDictionaryRef> dict;
+ base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> dict;
if (IORegistryEntryCreateCFProperties(disk_obj, dict.InitializeInto(),
kCFAllocatorDefault,
0) != KERN_SUCCESS) {
@@ -62,12 +62,10 @@ RemovableStorageProvider::PopulateDeviceList() {
continue;
}
- base::ScopedCFTypeRef<CFDictionaryRef> characteristics(
+ base::apple::ScopedCFTypeRef<CFDictionaryRef> characteristics(
static_cast<CFDictionaryRef>(IORegistryEntrySearchCFProperty(
- disk_obj,
- kIOServicePlane,
- CFSTR(kIOPropertyDeviceCharacteristicsKey),
- kCFAllocatorDefault,
+ disk_obj, kIOServicePlane,
+ CFSTR(kIOPropertyDeviceCharacteristicsKey), kCFAllocatorDefault,
kIORegistryIterateParents | kIORegistryIterateRecursively)));
if (!characteristics) {
@@ -75,11 +73,11 @@ RemovableStorageProvider::PopulateDeviceList() {
continue;
}
- CFStringRef cf_vendor = base::mac::GetValueFromDictionary<CFStringRef>(
+ CFStringRef cf_vendor = base::apple::GetValueFromDictionary<CFStringRef>(
characteristics, CFSTR(kIOPropertyVendorNameKey));
std::string vendor = base::SysCFStringRefToUTF8(cf_vendor);
- CFStringRef cf_model = base::mac::GetValueFromDictionary<CFStringRef>(
+ CFStringRef cf_model = base::apple::GetValueFromDictionary<CFStringRef>(
characteristics, CFSTR(kIOPropertyProductNameKey));
std::string model = base::SysCFStringRefToUTF8(cf_model);
diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
index 6bd29e79444..e36c0a59364 100644
--- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
+++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
@@ -26,6 +26,7 @@
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/process_manager.h"
#include "extensions/common/manifest_handlers/background_info.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/ime/ash/component_extension_ime_manager.h"
#include "ui/base/ime/ash/extension_ime_util.h"
#include "ui/base/ime/ash/ime_keymap.h"
@@ -854,7 +855,7 @@ class ImeObserverChromeOS
}
}
- std::string extension_id_;
+ extensions::ExtensionId extension_id_;
raw_ptr<Profile, DanglingUntriaged> profile_;
};
@@ -919,7 +920,9 @@ bool InputImeEventRouter::RegisterImeExtension(
std::string(), // TODO(uekawa): Set short name.
layout, languages,
false, // 3rd party IMEs are always not for login.
- component.options_page_url, component.input_view_url));
+ component.options_page_url, component.input_view_url,
+ // Not applicable to 3rd-party IMEs.
+ /*handwriting_language=*/absl::nullopt));
}
}
diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_apitest_chromeos.cc b/chromium/chrome/browser/extensions/api/input_ime/input_ime_apitest_chromeos.cc
index 5b09ea8f979..ab6c7b226b8 100644
--- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_apitest_chromeos.cc
+++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_apitest_chromeos.cc
@@ -39,7 +39,7 @@ IN_PROC_BROWSER_TEST_F(InputImeApiTest, Basic) {
"_ext_ime_ilanclmaeigfpnmdlgelmhkpkegdioiptest"};
ash::input_method::InputMethodManager::Get()
->GetActiveIMEState()
- ->SetEnabledExtensionImes(&extension_ime_ids);
+ ->SetEnabledExtensionImes(extension_ime_ids);
ASSERT_TRUE(RunExtensionTest("input_ime")) << message_;
}
diff --git a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
index d4c0e987adc..f2731f32ddf 100644
--- a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
@@ -341,11 +341,6 @@ LanguageSettingsPrivateEnableLanguageFunction::Run() {
std::string chrome_language = language_code;
language::ToChromeLanguageSynonym(&chrome_language);
- if (base::Contains(languages, chrome_language)) {
- LOG(ERROR) << "Language " << chrome_language << " already enabled";
- return RespondNow(NoArguments());
- }
-
translate_prefs->AddToLanguageList(language_code, /*force_blocked=*/false);
return RespondNow(NoArguments());
@@ -372,15 +367,7 @@ LanguageSettingsPrivateDisableLanguageFunction::Run() {
std::string chrome_language = language_code;
language::ToChromeLanguageSynonym(&chrome_language);
- if (!base::Contains(languages, chrome_language)) {
- LOG(ERROR) << "Language " << chrome_language << " not enabled";
- return RespondNow(NoArguments());
- }
-
translate_prefs->RemoveFromLanguageList(language_code);
- if (language_code == translate_prefs->GetRecentTargetLanguage()) {
- translate_prefs->ResetRecentTargetLanguage();
- }
return RespondNow(NoArguments());
}
diff --git a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
index bf355ff4813..a35e53d2a8e 100644
--- a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
@@ -31,6 +31,7 @@
#include "extensions/browser/api_test_utils.h"
#include "extensions/browser/event_router_factory.h"
#include "extensions/browser/extension_prefs.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
@@ -463,14 +464,17 @@ class TestInputMethodManager : public input_method::MockInputMethodManager {
std::string layout("us");
InputMethodDescriptor extension_ime(
GetExtensionImeId(), "ExtensionIme", "", layout, {"vi"},
- false /* is_login_keyboard */, GURL(), GURL());
+ false /* is_login_keyboard */, GURL(), GURL(),
+ /*handwriting_language=*/absl::nullopt);
InputMethodDescriptor component_extension_ime(
GetComponentExtensionImeId(), "ComponentExtensionIme", "", layout,
- {"en-US", "en"}, false /* is_login_keyboard */, GURL(), GURL());
+ {"en-US", "en"}, false /* is_login_keyboard */, GURL(), GURL(),
+ /*handwriting_language=*/absl::nullopt);
InputMethodDescriptor arc_ime(GetArcImeId(), "ArcIme", "", layout,
{ash::extension_ime_util::kArcImeLanguage},
false /* is_login_keyboard */, GURL(),
- GURL());
+ GURL(),
+ /*handwriting_language=*/absl::nullopt);
input_methods_ = {extension_ime, component_extension_ime, arc_ime};
}
diff --git a/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
index f5ea98249a1..98847287ab3 100644
--- a/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -9,7 +9,6 @@
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
-#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
@@ -63,65 +62,20 @@
#include "extensions/common/api/management.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_urls.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "ash/components/arc/arc_util.h"
-#include "ash/components/arc/mojom/intent_helper.mojom.h"
-#include "ash/components/arc/session/arc_bridge_service.h"
-#include "ash/components/arc/session/arc_service_manager.h"
-#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ash/arc/arc_util.h"
#include "chrome/browser/ash/login/demo_mode/demo_session.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
namespace {
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-const char kPlayIntentPrefix[] =
- "https://play.google.com/store/apps/details?id=";
-const char kChromeWebStoreReferrer[] = "&referrer=chrome_web_store";
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
-using InstallAndroidAppCallback =
- extensions::ManagementAPIDelegate::InstallAndroidAppCallback;
-using AndroidAppInstallStatusCallback =
- extensions::ManagementAPIDelegate::AndroidAppInstallStatusCallback;
using InstallOrLaunchWebAppCallback =
extensions::ManagementAPIDelegate::InstallOrLaunchWebAppCallback;
using InstallOrLaunchWebAppResult =
extensions::ManagementAPIDelegate::InstallOrLaunchWebAppResult;
using InstallableCheckResult = web_app::InstallableCheckResult;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-void OnDidCheckForIntentToPlayStore(const std::string& intent,
- InstallAndroidAppCallback callback,
- bool installable) {
- if (!installable) {
- std::move(callback).Run(false);
- return;
- }
-
- auto* arc_service_manager = arc::ArcServiceManager::Get();
- if (!arc_service_manager) {
- std::move(callback).Run(false);
- return;
- }
-
- auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_service_manager->arc_bridge_service()->intent_helper(), HandleUrl);
- if (!instance) {
- std::move(callback).Run(false);
- return;
- }
-
- instance->HandleUrl(intent, arc::kPlayStorePackage);
- std::move(callback).Run(true);
-}
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
class ManagementSetEnabledFunctionInstallPromptDelegate
: public extensions::InstallPromptDelegate {
public:
@@ -266,7 +220,7 @@ class ChromeAppForLinkDelegate : public extensions::AppForLinkDelegate {
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
- auto web_app_info = std::make_unique<WebAppInstallInfo>();
+ auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
web_app_info->title = base::UTF8ToUTF16(title);
web_app_info->start_url = launch_url;
web_app_info->display_mode = web_app::DisplayMode::kBrowser;
@@ -491,18 +445,6 @@ extensions::LaunchType ChromeManagementAPIDelegate::GetLaunchType(
return extensions::GetLaunchType(prefs, extension);
}
-void ChromeManagementAPIDelegate::
- GetPermissionWarningsByManifestFunctionDelegate(
- extensions::ManagementGetPermissionWarningsByManifestFunction* function,
- const std::string& manifest_str) const {
- data_decoder::DataDecoder::ParseJsonIsolated(
- manifest_str,
- base::BindOnce(
- &extensions::ManagementGetPermissionWarningsByManifestFunction::
- OnParse,
- function));
-}
-
std::unique_ptr<extensions::InstallPromptDelegate>
ChromeManagementAPIDelegate::SetEnabledFunctionDelegate(
content::WebContents* web_contents,
@@ -612,66 +554,6 @@ void ChromeManagementAPIDelegate::InstallOrLaunchReplacementWebApp(
std::move(callback), std::move(web_contents)));
}
-bool ChromeManagementAPIDelegate::CanContextInstallAndroidApps(
- content::BrowserContext* context) const {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- return arc::IsArcAllowedForProfile(Profile::FromBrowserContext(context));
-#else
- return false;
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-}
-
-void ChromeManagementAPIDelegate::CheckAndroidAppInstallStatus(
- const std::string& package_name,
- AndroidAppInstallStatusCallback callback) const {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- auto* arc_service_manager = arc::ArcServiceManager::Get();
- if (!arc_service_manager) {
- std::move(callback).Run(false);
- return;
- }
-
- auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_service_manager->arc_bridge_service()->app(), IsInstallable);
- if (!instance) {
- std::move(callback).Run(false);
- return;
- }
-
- instance->IsInstallable(package_name, std::move(callback));
-#else
- std::move(callback).Run(false);
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-}
-
-void ChromeManagementAPIDelegate::InstallReplacementAndroidApp(
- const std::string& package_name,
- InstallAndroidAppCallback callback) const {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- std::string intent =
- base::StrCat({kPlayIntentPrefix, package_name, kChromeWebStoreReferrer});
-
- auto* arc_service_manager = arc::ArcServiceManager::Get();
- if (!arc_service_manager) {
- std::move(callback).Run(false);
- return;
- }
-
- auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
- arc_service_manager->arc_bridge_service()->app(), IsInstallable);
- if (!instance) {
- std::move(callback).Run(false);
- return;
- }
-
- instance->IsInstallable(
- package_name, base::BindOnce(&OnDidCheckForIntentToPlayStore, intent,
- std::move(callback)));
-#else
- std::move(callback).Run(false);
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-}
-
void ChromeManagementAPIDelegate::EnableExtension(
content::BrowserContext* context,
const std::string& extension_id) const {
diff --git a/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.h b/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.h
index d6f29588886..91da4eb8e88 100644
--- a/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.h
+++ b/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.h
@@ -23,9 +23,6 @@ class ChromeManagementAPIDelegate : public extensions::ManagementAPIDelegate {
extensions::LaunchType GetLaunchType(
const extensions::ExtensionPrefs* prefs,
const extensions::Extension* extension) const override;
- void GetPermissionWarningsByManifestFunctionDelegate(
- extensions::ManagementGetPermissionWarningsByManifestFunction* function,
- const std::string& manifest_str) const override;
std::unique_ptr<extensions::InstallPromptDelegate> SetEnabledFunctionDelegate(
content::WebContents* web_contents,
content::BrowserContext* browser_context,
@@ -53,15 +50,6 @@ class ChromeManagementAPIDelegate : public extensions::ManagementAPIDelegate {
const GURL& web_app_url,
ManagementAPIDelegate::InstallOrLaunchWebAppCallback callback)
const override;
- bool CanContextInstallAndroidApps(
- content::BrowserContext* context) const override;
- void CheckAndroidAppInstallStatus(
- const std::string& package_name,
- ManagementAPIDelegate::AndroidAppInstallStatusCallback callback)
- const override;
- void InstallReplacementAndroidApp(
- const std::string& package_name,
- ManagementAPIDelegate::InstallAndroidAppCallback callback) const override;
void EnableExtension(content::BrowserContext* context,
const std::string& extension_id) const override;
void DisableExtension(
diff --git a/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc b/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc
index 7bbe060e189..e7877539aa5 100644
--- a/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc
@@ -32,7 +32,6 @@
#include "extensions/browser/extension_host_test_helper.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/notification_types.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_id.h"
#include "extensions/test/extension_test_message_listener.h"
diff --git a/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc b/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc
index 57cf8c624f9..1ac7fbf7ae7 100644
--- a/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc
@@ -865,9 +865,6 @@ class TestManagementAPIDelegate : public ManagementAPIDelegate {
const Extension* extension) const override {
return LaunchType::LAUNCH_TYPE_DEFAULT;
}
- void GetPermissionWarningsByManifestFunctionDelegate(
- ManagementGetPermissionWarningsByManifestFunction* function,
- const std::string& manifest_str) const override {}
std::unique_ptr<InstallPromptDelegate> SetEnabledFunctionDelegate(
content::WebContents* web_contents,
content::BrowserContext* browser_context,
@@ -905,6 +902,7 @@ class TestManagementAPIDelegate : public ManagementAPIDelegate {
void SetLaunchType(content::BrowserContext* context,
const std::string& extension_id,
LaunchType launch_type) const override {}
+
std::unique_ptr<AppForLinkDelegate> GenerateAppForLinkFunctionDelegate(
ManagementGenerateAppForLinkFunction* function,
content::BrowserContext* context,
@@ -920,16 +918,6 @@ class TestManagementAPIDelegate : public ManagementAPIDelegate {
content::BrowserContext* context,
const GURL& web_app_url,
InstallOrLaunchWebAppCallback callback) const override {}
- bool CanContextInstallAndroidApps(
- content::BrowserContext* context) const override {
- return true;
- }
- void CheckAndroidAppInstallStatus(
- const std::string& package_name,
- AndroidAppInstallStatusCallback callback) const override {}
- void InstallReplacementAndroidApp(
- const std::string& package_name,
- InstallAndroidAppCallback callback) const override {}
GURL GetIconURL(const Extension* extension,
int icon_size,
ExtensionIconSet::MatchType match,
diff --git a/chromium/chrome/browser/extensions/api/management/management_apitest.cc b/chromium/chrome/browser/extensions/api/management/management_apitest.cc
index f620dc0e9dc..5f6e396ca92 100644
--- a/chromium/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/management/management_apitest.cc
@@ -7,7 +7,6 @@
#include "base/auto_reset.h"
#include "base/strings/stringprintf.h"
#include "base/test/gtest_tags.h"
-#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/extensions/extension_apitest.h"
@@ -27,7 +26,6 @@
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
-#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_constants.h"
#include "content/public/test/browser_test.h"
@@ -198,32 +196,6 @@ IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, GenerateAppForLink) {
ASSERT_TRUE(RunExtensionTest("management/generate_app_for_link"));
}
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-class GenerateAppForLinkWithLacrosWebAppsApiTest
- : public ExtensionManagementApiTest {
- public:
- GenerateAppForLinkWithLacrosWebAppsApiTest() {
- features_.InitAndEnableFeature(features::kWebAppsCrosapi);
- }
-
- private:
- base::test::ScopedFeatureList features_;
-};
-
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
- GenerateAppForLinkWithLacrosWebAppsApiTest,
- ::testing::Values(ContextType::kPersistentBackground));
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
- GenerateAppForLinkWithLacrosWebAppsApiTest,
- ::testing::Values(ContextType::kServiceWorker));
-
-IN_PROC_BROWSER_TEST_P(GenerateAppForLinkWithLacrosWebAppsApiTest,
- GenerateAppForLink) {
- web_app::test::WaitUntilReady(web_app::WebAppProvider::GetForTest(profile()));
- ASSERT_TRUE(RunExtensionTest("management/generate_app_for_link_lacros"));
-}
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
class InstallReplacementWebAppApiTest : public ExtensionManagementApiTest {
public:
InstallReplacementWebAppApiTest()
@@ -388,42 +360,6 @@ IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, InstallableWebApp) {
}
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-class InstallReplacementWebAppWithLacrosWebAppsApiTest
- : public InstallReplacementWebAppApiTest {
- public:
- InstallReplacementWebAppWithLacrosWebAppsApiTest() {
- features_.InitAndEnableFeature(features::kWebAppsCrosapi);
- }
-
- private:
- base::test::ScopedFeatureList features_;
-};
-
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
- InstallReplacementWebAppWithLacrosWebAppsApiTest,
- ::testing::Values(ContextType::kPersistentBackground));
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
- InstallReplacementWebAppWithLacrosWebAppsApiTest,
- ::testing::Values(ContextType::kServiceWorker));
-
-IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppWithLacrosWebAppsApiTest,
- InstallableWebApp) {
- static constexpr char kGoodWebAppURL[] =
- "/management/install_replacement_web_app/acceptable_web_app/index.html";
- static constexpr char kBackground[] =
- R"(chrome.test.runWithUserGesture(function() {
- chrome.management.installReplacementWebApp(function() {
- chrome.test.assertLastError(
- 'Web apps can\'t be installed in the current user profile.');
- chrome.test.notifyPass();
- });
- });)";
-
- RunTest(kManifest, kGoodWebAppURL, kBackground, true /* from_webstore */);
-}
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
// TODO(crbug.com/1288199): Run these tests on Chrome OS with both Ash and
// Lacros processes active.
diff --git a/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc b/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc
index 85c074cf1a5..49f16877a8e 100644
--- a/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc
+++ b/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc
@@ -241,10 +241,10 @@ void MDnsAPI::WriteToConsole(const std::string& service_type,
extensions::ExtensionHost* host =
extensions::ProcessManager::Get(browser_context_)
->GetBackgroundHostForExtension(extension_id);
- content::RenderFrameHost* rfh =
+ content::RenderFrameHost* render_frame_host =
host ? host->host_contents()->GetPrimaryMainFrame() : nullptr;
- if (rfh) {
- rfh->AddMessageToConsole(level, logged_message);
+ if (render_frame_host) {
+ render_frame_host->AddMessageToConsole(level, logged_message);
}
}
}
diff --git a/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc b/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
index d062790716a..b4f1776306d 100644
--- a/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
@@ -119,10 +119,10 @@ std::unique_ptr<MessagePort> ChromeMessagingDelegate::CreateReceiverForTab(
bool include_child_frames =
receiver_frame_id == -1 && receiver_document_id.empty();
- content::RenderFrameHost* receiver_rfh = nullptr;
+ content::RenderFrameHost* receiver_render_frame_host = nullptr;
if (include_child_frames) {
// The target is the active outermost main frame of the WebContents.
- receiver_rfh = receiver_contents->GetPrimaryMainFrame();
+ receiver_render_frame_host = receiver_contents->GetPrimaryMainFrame();
} else if (!receiver_document_id.empty()) {
ExtensionApiFrameIdMap::DocumentId document_id =
ExtensionApiFrameIdMap::DocumentIdFromString(receiver_document_id);
@@ -131,28 +131,30 @@ std::unique_ptr<MessagePort> ChromeMessagingDelegate::CreateReceiverForTab(
if (!document_id)
return nullptr;
- receiver_rfh =
+ receiver_render_frame_host =
ExtensionApiFrameIdMap::Get()->GetRenderFrameHostByDocumentId(
document_id);
// If both |document_id| and |receiver_frame_id| are provided they
// should find the same RenderFrameHost, if not return early.
if (receiver_frame_id != -1 &&
- ExtensionApiFrameIdMap::GetRenderFrameHostById(
- receiver_contents, receiver_frame_id) != receiver_rfh) {
+ ExtensionApiFrameIdMap::GetRenderFrameHostById(receiver_contents,
+ receiver_frame_id) !=
+ receiver_render_frame_host) {
return nullptr;
}
} else {
DCHECK_GT(receiver_frame_id, -1);
- receiver_rfh = ExtensionApiFrameIdMap::GetRenderFrameHostById(
+ receiver_render_frame_host = ExtensionApiFrameIdMap::GetRenderFrameHostById(
receiver_contents, receiver_frame_id);
}
- if (!receiver_rfh)
+ if (!receiver_render_frame_host) {
return nullptr;
+ }
return std::make_unique<ExtensionMessagePort>(
- channel_delegate, receiver_port_id, extension_id, receiver_rfh,
- include_child_frames);
+ channel_delegate, receiver_port_id, extension_id,
+ receiver_render_frame_host, include_child_frames);
}
std::unique_ptr<MessagePort>
diff --git a/chromium/chrome/browser/extensions/api/messaging/messaging_apitest.cc b/chromium/chrome/browser/extensions/api/messaging/messaging_apitest.cc
index 139a2e1facf..0a04d003a92 100644
--- a/chromium/chrome/browser/extensions/api/messaging/messaging_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/messaging_apitest.cc
@@ -39,6 +39,7 @@
#include "components/infobars/content/content_infobar_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/storage_partition.h"
@@ -335,6 +336,18 @@ class ExternallyConnectableMessagingTest : public MessagingApiTest {
return static_cast<Result>(result);
}
+ Result CanUseSendMessagePromise(const Extension* extension) {
+ content::RenderFrameHost* frame = browser()
+ ->tab_strip_model()
+ ->GetActiveWebContents()
+ ->GetPrimaryMainFrame();
+ std::string command =
+ content::JsReplace("assertions.canUseSendMessagePromise($1, $2)",
+ extension->id(), extension->is_platform_app());
+ int result = content::EvalJs(frame, command).ExtractInt();
+ return static_cast<Result>(result);
+ }
+
testing::AssertionResult AreAnyNonWebApisDefinedForMainFrame() {
return AreAnyNonWebApisDefinedForFrame(browser()
->tab_strip_model()
@@ -655,6 +668,18 @@ IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
}
+// Tests that an externally connectable web page context can use the promise
+// based form of sendMessage.
+IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
+ SendMessagePromiseSignatureExposed) {
+ // Install the web connectable extension.
+ scoped_refptr<const Extension> chromium_connectable =
+ LoadChromiumConnectableExtension();
+
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), chromium_org_url()));
+ EXPECT_EQ(OK, CanUseSendMessagePromise(chromium_connectable.get()));
+}
+
// See http://crbug.com/297866
IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
DISABLED_BackgroundPageClosesOnMessageReceipt) {
diff --git a/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc b/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
index c64f72ca057..2e1268e6972 100644
--- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
@@ -69,8 +69,7 @@ IN_PROC_BROWSER_TEST_F(NativeMessagingApiTestBase, UserLevelSendNativeMessage) {
#if BUILDFLAG(IS_WIN)
// On Windows, a new codepath is used to directly launch .EXE-based Native
// Hosts. This codepath allows launching of Native Hosts even when cmd.exe is
-// disabled, or if the path to the host contains a character that prevents
-// cmd.exe from successfully launching it (e.g. "&" in this test).
+// disabled or misconfigured.
class NativeMessagingLaunchExeTest : public NativeMessagingApiTestBase,
public testing::WithParamInterface<bool> {
public:
@@ -90,23 +89,33 @@ INSTANTIATE_TEST_SUITE_P(NativeMessagingLaunchExe,
NativeMessagingLaunchExeTest,
testing::Bool());
-IN_PROC_BROWSER_TEST_P(NativeMessagingLaunchExeTest, SendNativeMessageWinExe) {
- ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost(/*user_level=*/false));
+IN_PROC_BROWSER_TEST_P(NativeMessagingLaunchExeTest,
+ UserLevelSendNativeMessageWinExe) {
+ ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost(
+ "native_messaging_test_echo_host.exe", /*user_level=*/true));
- // The extension works properly only if the host launches successfully, which
- // requires the kLaunchWindowsNativeHostsDirectly feature to be enabled.
- ASSERT_EQ(IsDirectLaunchEnabled(),
- RunExtensionTest("native_messaging_send_native_message_exe"));
+ ASSERT(RunExtensionTest("native_messaging_send_native_message_exe"));
}
+// The Host's filename deliberately contains the character '&' which causes the
+// Host to fail to launch if cmd.exe is used as an intermediary between the
+// extension and the host executable, unless extra quotes are used.
+// crbug.com/335558
IN_PROC_BROWSER_TEST_P(NativeMessagingLaunchExeTest,
- UserLevelSendNativeMessageWinExe) {
- ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost(/*user_level=*/true));
+ SendNativeMessageWinExeAmpersand) {
+ ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost(
+ "native_messaging_test_echo_&_host.exe", /*user_level=*/false));
+
+ ASSERT(RunExtensionTest("native_messaging_send_native_message_exe"));
+}
+
+// Make sure that a filename with a space is supported.
+IN_PROC_BROWSER_TEST_P(NativeMessagingLaunchExeTest,
+ SendNativeMessageWinExeSpace) {
+ ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost(
+ "native_messaging_test_echo_ _host.exe", /*user_level=*/false));
- // The extension works properly only if the host launches successfully, which
- // requires the kLaunchWindowsNativeHostsDirectly feature to be enabled.
- ASSERT_EQ(IsDirectLaunchEnabled(),
- RunExtensionTest("native_messaging_send_native_message_exe"));
+ ASSERT(RunExtensionTest("native_messaging_send_native_message_exe"));
}
#endif
diff --git a/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc b/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc
index 719f64b9f2d..7d1bfe22b01 100644
--- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc
@@ -116,7 +116,7 @@ class ExtensionSupportsConnectionFromNativeAppTest : public ::testing::Test {
content::BrowserTaskEnvironment task_environment_;
bool has_listener_result_ = true;
TestingProfile profile_;
- std::string extension_id_;
+ ExtensionId extension_id_;
};
TEST_F(ExtensionSupportsConnectionFromNativeAppTest, Success) {
diff --git a/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc b/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc
index 3861f5e1c6e..6ff032ba83c 100644
--- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc
@@ -130,7 +130,9 @@ void ScopedTestNativeMessagingHost::RegisterTestHost(bool user_level) {
#if BUILDFLAG(IS_WIN)
// On Windows, a new codepath is used to directly launch .EXE-based Native
// Hosts.
-void ScopedTestNativeMessagingHost::RegisterTestExeHost(bool user_level) {
+void ScopedTestNativeMessagingHost::RegisterTestExeHost(
+ std::string_view filename,
+ bool user_level) {
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -142,12 +144,7 @@ void ScopedTestNativeMessagingHost::RegisterTestExeHost(bool user_level) {
// Unlike in the |RegisterTestHost| case above, we must leave the Host
// .exe where it was built, because the Host will fail to run from the
// temp_dir_ if is_component_build is set for the build.
- //
- // The Host's filename deliberately contains the character '&' which causes
- // the Host to fail to launch if cmd.exe is used as an intermediary between
- // the extension and the host executable. crbug.com/335558
- base::FilePath host_path =
- binary_dir.AppendASCII("native_messaging_test_echo_&_host.exe");
+ base::FilePath host_path = binary_dir.AppendASCII(filename);
ASSERT_NO_FATAL_FAILURE(WriteTestNativeHostManifest(
temp_dir_.GetPath(), kHostExeName, host_path, user_level, false));
}
diff --git a/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.h b/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.h
index 525fc3e1b4f..007cf5d05a0 100644
--- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.h
+++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGING_TEST_UTIL_H_
#include <memory>
+#include <string_view>
#include "base/files/scoped_temp_dir.h"
#include "build/build_config.h"
@@ -47,9 +48,8 @@ class ScopedTestNativeMessagingHost {
void RegisterTestHost(bool user_level);
#if BUILDFLAG(IS_WIN)
- // Register the Windows-only |native_messaging_test_echo_host.exe| Native
- // Host.
- void RegisterTestExeHost(bool user_level);
+ // Register the Windows-only Native Host exe.
+ void RegisterTestExeHost(std::string_view filename, bool user_level);
#endif
const base::FilePath& temp_dir() { return temp_dir_.GetPath(); }
diff --git a/chromium/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc b/chromium/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc
index 20680fbf140..5de3521d4d3 100644
--- a/chromium/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc
@@ -145,7 +145,7 @@ base::Process LaunchNativeHostViaCmd(const std::wstring& command,
L"COMSPEC", base::WriteInto(&comspec, comspec_length), comspec_length);
std::wstring wrapped_command = base::StringPrintf(
- L"%ls /d /c %ls < %ls > %ls", comspec.c_str(), command.c_str(),
+ L"%ls /d /s /c \"%ls\" < %ls > %ls", comspec.c_str(), command.c_str(),
in_pipe_name.c_str(), out_pipe_name.c_str());
return base::LaunchProcess(wrapped_command, options);
diff --git a/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
index 42dba0f0ca1..cf27fcf9bbd 100644
--- a/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
@@ -11,6 +11,7 @@
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
+#include "base/test/test_future.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "components/onc/onc_constants.h"
@@ -53,11 +54,8 @@
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h"
#include "chromeos/crosapi/mojom/test_controller.mojom.h"
#include "chromeos/lacros/lacros_service.h"
-
-using crosapi::mojom::ShillClientTestInterfaceAsyncWaiter;
#endif
// This tests the Chrome OS implementation of the networkingPrivate API
@@ -510,11 +508,12 @@ class NetworkingPrivateChromeOSApiTestLacros
LOG(ERROR) << "Unsupported ash version.";
return false;
}
- crosapi::mojom::TestControllerAsyncWaiter test_controller_waiter{
- service->GetRemote<crosapi::mojom::TestController>().get()};
- test_controller_waiter.BindShillClientTestInterface(
- shill_test_.BindNewPipeAndPassReceiver());
+ base::test::TestFuture<void> future;
+ service->GetRemote<crosapi::mojom::TestController>()
+ ->BindShillClientTestInterface(shill_test_.BindNewPipeAndPassReceiver(),
+ future.GetCallback());
+ EXPECT_TRUE(future.Wait());
ConfigFakeNetwork();
@@ -533,74 +532,89 @@ class NetworkingPrivateChromeOSApiTestLacros
return "";
}
- crosapi::mojom::TestControllerAsyncWaiter test_controller_waiter{
- service->GetRemote<crosapi::mojom::TestController>().get()};
-
- std::string userhash;
- test_controller_waiter.GetSanitizedActiveUsername(&userhash);
- return userhash;
+ base::test::TestFuture<const std::string&> future;
+ service->GetRemote<crosapi::mojom::TestController>()
+ ->GetSanitizedActiveUsername(future.GetCallback());
+ return future.Take();
}
void AddDevice(const std::string& device_path,
const std::string& type,
const std::string& name) override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get())
- .AddDevice(device_path, type, name);
+ base::test::TestFuture<void> future;
+ shill_test_->AddDevice(device_path, type, name, future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
void SetDeviceProperty(const std::string& device_path,
const std::string& name,
const base::Value& value) override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get())
- .SetDeviceProperty(device_path, name, value.Clone(),
- /*notify_changed=*/true);
+ base::test::TestFuture<void> future;
+ shill_test_->SetDeviceProperty(device_path, name, value.Clone(),
+ /*notify_changed=*/true,
+ future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
void SetSimLocked(const std::string& device_path, bool enabled) override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get())
- .SetSimLocked(device_path, enabled);
+ base::test::TestFuture<void> future;
+ shill_test_->SetSimLocked(device_path, enabled, future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
void ClearDevices() override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get()).ClearDevices();
+ base::test::TestFuture<void> future;
+ shill_test_->ClearDevices(future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
void AddService(const std::string& service_path,
const std::string& name,
const std::string& type,
const std::string& state) override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get())
- .AddService(service_path, service_path + "_guid", name, type, state,
- true /* add_to_visible */);
+ base::test::TestFuture<void> future;
+ shill_test_->AddService(service_path, service_path + "_guid", name, type,
+ state, true /* add_to_visible */,
+ future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
void ClearServices() override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get()).ClearServices();
+ base::test::TestFuture<void> future;
+ shill_test_->ClearServices(future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
void SetServiceProperty(const std::string& service_path,
const std::string& property,
const base::Value& value) override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get())
- .SetServiceProperty(service_path, property, value.Clone());
+ base::test::TestFuture<void> future;
+ shill_test_->SetServiceProperty(service_path, property, value.Clone(),
+ future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
void AddIPConfig(const std::string& ip_config_path,
base::Value::Dict properties) override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get())
- .AddIPConfig(ip_config_path, base::Value(std::move(properties)));
+ base::test::TestFuture<void> future;
+ shill_test_->AddIPConfig(ip_config_path, base::Value(std::move(properties)),
+ future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
void AddProfile(const std::string& profile_path,
const std::string& userhash) override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get())
- .AddProfile(profile_path, userhash);
+ base::test::TestFuture<void> future;
+ shill_test_->AddProfile(profile_path, userhash, future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
void AddServiceToProfile(const std::string& profile_path,
const std::string& service_path) override {
- ShillClientTestInterfaceAsyncWaiter(shill_test_.get())
- .AddServiceToProfile(profile_path, service_path);
+ base::test::TestFuture<void> future;
+ shill_test_->AddServiceToProfile(profile_path, service_path,
+ future.GetCallback());
+ ASSERT_TRUE(future.Wait());
}
std::string GetSharedProfilePath() override {
diff --git a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc
index ab06550c58b..173bb0edf07 100644
--- a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc
+++ b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc
@@ -39,11 +39,11 @@ ExtensionNotificationDisplayHelperFactory::
ExtensionNotificationDisplayHelperFactory::
~ExtensionNotificationDisplayHelperFactory() = default;
-KeyedService*
-ExtensionNotificationDisplayHelperFactory::BuildServiceInstanceFor(
- content::BrowserContext* context) const {
+std::unique_ptr<KeyedService> ExtensionNotificationDisplayHelperFactory::
+ BuildServiceInstanceForBrowserContext(
+ content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
- return new ExtensionNotificationDisplayHelper(profile);
+ return std::make_unique<ExtensionNotificationDisplayHelper>(profile);
}
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h
index 1f726c1262f..d31b3040d97 100644
--- a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h
+++ b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h
@@ -30,7 +30,7 @@ class ExtensionNotificationDisplayHelperFactory
protected:
// Overridden from BrowserContextKeyedServiceFactory.
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
private:
diff --git a/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc b/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc
index ea75249bd7f..d9b0f852288 100644
--- a/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc
@@ -48,7 +48,7 @@ class TestExtensionNotificationHandler : public ExtensionNotificationHandler {
}
private:
- std::string extension_id_;
+ ExtensionId extension_id_;
std::string event_name_;
size_t param_count_;
};
diff --git a/chromium/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc b/chromium/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
index 682ba13ad4d..58be93e34f2 100644
--- a/chromium/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
+++ b/chromium/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
@@ -26,6 +26,7 @@
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_result.h"
+#include "components/omnibox/browser/omnibox_controller.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "components/omnibox/browser/omnibox_view.h"
#include "content/public/test/browser_test.h"
@@ -130,7 +131,7 @@ class OmniboxApiTest : public ExtensionApiTest,
Browser* browser) {
return GetLocationBar(browser)
->GetOmniboxView()
- ->model()
+ ->controller()
->autocomplete_controller();
}
};
diff --git a/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc b/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc
index 0dbcb52167b..d532c4a1aff 100644
--- a/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc
+++ b/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc
@@ -20,7 +20,6 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/mhtml_generation_params.h"
#include "extensions/browser/extension_util.h"
-#include "extensions/common/extension_messages.h"
#include "extensions/common/permissions/permissions_data.h"
using content::BrowserThread;
@@ -116,30 +115,7 @@ bool PageCaptureSaveAsMHTMLFunction::CanCaptureCurrentPage(std::string* error) {
return can_capture_page;
}
-bool PageCaptureSaveAsMHTMLFunction::OnMessageReceived(
- const IPC::Message& message) {
- if (message.type() != ExtensionHostMsg_ResponseAck::ID)
- return false;
-
- int message_request_id;
- base::PickleIterator iter(message);
- if (!iter.ReadInt(&message_request_id)) {
- NOTREACHED() << "malformed extension message";
- return true;
- }
-
- if (message_request_id != request_id())
- return false;
-
- // The extension process has processed the response and has created a
- // reference to the blob, it is safe for us to go away.
- Release(); // Balanced in Run()
-
- return true;
-}
-
-void PageCaptureSaveAsMHTMLFunction::OnServiceWorkerAck() {
- DCHECK(is_from_service_worker());
+void PageCaptureSaveAsMHTMLFunction::OnResponseAck() {
// The extension process has processed the response and has created a
// reference to the blob, it is safe for us to go away.
// This instance may be deleted after this call, so no code goes after
@@ -238,7 +214,7 @@ void PageCaptureSaveAsMHTMLFunction::ReturnSuccess(int file_size) {
base::Value::Dict response;
response.Set("mhtmlFilePath", mhtml_path_.AsUTF8Unsafe());
response.Set("mhtmlFileLength", file_size);
- response.Set("requestId", request_id());
+ response.Set("requestId", request_uuid().AsLowercaseString());
// Add a reference, extending the lifespan of this extension function until
// the response has been received by the renderer. This function generates a
@@ -248,8 +224,7 @@ void PageCaptureSaveAsMHTMLFunction::ReturnSuccess(int file_size) {
// renderer has it's reference, so we can release ours.
// TODO(crbug.com/1050887): Potential memory leak here.
AddRef(); // Balanced in either OnMessageReceived()
- if (is_from_service_worker())
- AddWorkerResponseTarget();
+ AddResponseTarget();
Respond(WithArguments(std::move(response)));
}
diff --git a/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.h b/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.h
index ac5d18d2b81..cf8ef0643d5 100644
--- a/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.h
+++ b/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.h
@@ -39,13 +39,11 @@ class PageCaptureSaveAsMHTMLFunction : public ExtensionFunction {
};
static void SetTestDelegate(TestDelegate* delegate);
- // ExtensionFunction:
- void OnServiceWorkerAck() override;
-
private:
+ // ExtensionFunction:
~PageCaptureSaveAsMHTMLFunction() override;
ResponseAction Run() override;
- bool OnMessageReceived(const IPC::Message& message) override;
+ void OnResponseAck() override;
// Returns whether or not the extension has permission to capture the current
// page. Sets |*error| to an error value on failure.
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
index 3f0d9413528..29a779743fe 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
@@ -172,7 +172,7 @@ api::passwords_private::PasswordCheckState ConvertPasswordCheckState(
std::string FormatElapsedTime(base::Time time) {
const base::TimeDelta elapsed_time = base::Time::Now() - time;
if (elapsed_time < base::Minutes(1))
- return l10n_util::GetStringUTF8(IDS_SETTINGS_PASSWORDS_JUST_NOW);
+ return l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_UI_JUST_NOW);
return base::UTF16ToUTF8(TimeFormat::SimpleWithMonthAndYear(
TimeFormat::FORMAT_ELAPSED, TimeFormat::LENGTH_LONG, elapsed_time, true));
@@ -293,7 +293,7 @@ PasswordCheckDelegate::GetCredentialsWithReusedPassword() {
bool PasswordCheckDelegate::MuteInsecureCredential(
const api::passwords_private::PasswordUiEntry& credential) {
// Try to obtain the original CredentialUIEntry. Return false if fails.
- const CredentialUIEntry* entry = FindMatchingEntry(credential);
+ const CredentialUIEntry* entry = id_generator_->TryGetKey(credential.id);
if (!entry)
return false;
@@ -303,25 +303,13 @@ bool PasswordCheckDelegate::MuteInsecureCredential(
bool PasswordCheckDelegate::UnmuteInsecureCredential(
const api::passwords_private::PasswordUiEntry& credential) {
// Try to obtain the original CredentialUIEntry. Return false if fails.
- const CredentialUIEntry* entry = FindMatchingEntry(credential);
+ const CredentialUIEntry* entry = id_generator_->TryGetKey(credential.id);
if (!entry)
return false;
return insecure_credentials_manager_.UnmuteCredential(*entry);
}
-// Records that a change password flow was started for |credential|.
-void PasswordCheckDelegate::RecordChangePasswordFlowStarted(
- const api::passwords_private::PasswordUiEntry& credential) {
- // If the |credential| does not have a |change_password_url|, skip it.
- if (!credential.change_password_url)
- return;
-
- GetPasswordChangeSuccessTracker()->OnManualChangePasswordFlowStarted(
- GURL(*credential.change_password_url), credential.username,
- PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings);
-}
-
void PasswordCheckDelegate::StartPasswordCheck(
StartPasswordCheckCallback callback) {
// If the delegate isn't initialized yet, enqueue the callback and return
@@ -347,12 +335,9 @@ void PasswordCheckDelegate::StartPasswordAnalyses(
insecure_credentials_manager_.StartWeakCheck(base::BindOnce(
&PasswordCheckDelegate::RecordAndNotifyAboutCompletedWeakPasswordCheck,
weak_ptr_factory_.GetWeakPtr()));
- if (base::FeatureList::IsEnabled(
- password_manager::features::kPasswordManagerRedesign)) {
- insecure_credentials_manager_.StartReuseCheck(
- base::BindOnce(&PasswordCheckDelegate::NotifyPasswordCheckStatusChanged,
- weak_ptr_factory_.GetWeakPtr()));
- }
+ insecure_credentials_manager_.StartReuseCheck(
+ base::BindOnce(&PasswordCheckDelegate::NotifyPasswordCheckStatusChanged,
+ weak_ptr_factory_.GetWeakPtr()));
auto progress = base::MakeRefCounted<PasswordCheckProgress>();
for (const auto& password : saved_passwords_presenter_->GetSavedPasswords())
progress->IncrementCounts(password);
@@ -366,16 +351,6 @@ void PasswordCheckDelegate::StartPasswordAnalyses(
bulk_leak_check_service_adapter_.GetBulkLeakCheckState());
}
-void PasswordCheckDelegate::StopPasswordCheck() {
- if (!is_initialized_) {
- for (auto&& callback : std::exchange(start_check_callbacks_, {}))
- std::move(callback).Run(State::kIdle);
- return;
- }
-
- bulk_leak_check_service_adapter_.StopBulkLeakCheck();
-}
-
api::passwords_private::PasswordCheckStatus
PasswordCheckDelegate::GetPasswordCheckStatus() const {
api::passwords_private::PasswordCheckStatus result;
@@ -481,22 +456,6 @@ void PasswordCheckDelegate::OnCredentialDone(
}
}
-const CredentialUIEntry* PasswordCheckDelegate::FindMatchingEntry(
- const api::passwords_private::PasswordUiEntry& credential) const {
- const CredentialUIEntry* entry = id_generator_->TryGetKey(credential.id);
- if (!entry)
- return nullptr;
-
- if (credential.urls.signon_realm != entry->GetFirstSignonRealm() ||
- credential.username != base::UTF16ToUTF8(entry->username) ||
- (credential.password &&
- *credential.password != base::UTF16ToUTF8(entry->password))) {
- return nullptr;
- }
-
- return entry;
-}
-
void PasswordCheckDelegate::
RecordAndNotifyAboutCompletedCompromisedPasswordCheck() {
profile_->GetPrefs()->SetDouble(
@@ -534,10 +493,7 @@ api::passwords_private::PasswordUiEntry
PasswordCheckDelegate::ConstructInsecureCredentialUiEntry(
CredentialUIEntry entry) {
api::passwords_private::PasswordUiEntry api_credential;
- api_credential.is_android_credential =
- password_manager::IsValidAndroidFacetURI(entry.GetFirstSignonRealm());
api_credential.username = base::UTF16ToUTF8(entry.username);
- api_credential.urls = CreateUrlCollectionFromCredential(entry);
api_credential.stored_in = StoreSetFromCredential(entry);
api_credential.compromised_info = CreateCompromiseInfo(entry);
absl::optional<GURL> change_password_url = entry.GetChangePasswordURL();
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.h b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.h
index 630d8861872..8c144fddc8d 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.h
+++ b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.h
@@ -76,18 +76,12 @@ class PasswordCheckDelegate
bool UnmuteInsecureCredential(
const api::passwords_private::PasswordUiEntry& credential);
- // Records that a change password flow was started for `credential`.
- void RecordChangePasswordFlowStarted(
- const api::passwords_private::PasswordUiEntry& credential);
-
// Checks that all preconditions for running a password check are fulfilled
// and, once that is the case, launches the password check. Invokes `callback`
// once a check is running or the request was stopped via
// `StopPasswordCheck()`.
void StartPasswordCheck(
StartPasswordCheckCallback callback = base::DoNothing());
- // Stops checking for insecure passwords.
- void StopPasswordCheck();
// Returns the current status of the password check.
api::passwords_private::PasswordCheckStatus GetPasswordCheckStatus() const;
@@ -113,14 +107,6 @@ class PasswordCheckDelegate
void OnCredentialDone(const password_manager::LeakCheckCredential& credential,
password_manager::IsLeaked is_leaked) override;
- // Tries to find the matching CredentialUIEntry for |credential|. It
- // performs a look-up in |id_generator_| using |credential.id|. If a matching
- // value exists it also verifies that signon realm, username and when possible
- // password match. Returns a pointer to the matching CredentialUIEntry on
- // success or nullptr otherwise.
- const password_manager::CredentialUIEntry* FindMatchingEntry(
- const api::passwords_private::PasswordUiEntry& credential) const;
-
// Starts the analyses of whether credentials are compromised and/or weak.
// Assumes that `StartPasswordCheck()` was called prior.
void StartPasswordAnalyses(StartPasswordCheckCallback callback);
@@ -168,7 +154,7 @@ class PasswordCheckDelegate
// List of callbacks that were passed to `StartPasswordCheck()` prior to the
// delegate being initialized. These will be run when either initialization
- // finishes, or `StopPasswordCheck()` gets invoked before hand.
+ // finishes.
std::vector<StartPasswordCheckCallback> start_check_callbacks_;
// Remembers the progress of the ongoing check. Null if no check is currently
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
index aa9f3a7fa25..2c1b03607c7 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
@@ -33,7 +33,7 @@
#include "chrome/test/base/testing_profile.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/password_manager/content/browser/password_change_success_tracker_factory.h"
-#include "components/password_manager/core/browser/affiliation/mock_affiliation_service.h"
+#include "components/password_manager/core/browser/affiliation/fake_affiliation_service.h"
#include "components/password_manager/core/browser/bulk_leak_check_service.h"
#include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
#include "components/password_manager/core/browser/leak_detection/leak_detection_delegate_interface.h"
@@ -89,9 +89,9 @@ constexpr char16_t kWeakPassword2[] = u"111111";
constexpr char kGoogleAccounts[] = "https://accounts.google.com";
using api::passwords_private::CompromisedInfo;
+using api::passwords_private::DomainInfo;
using api::passwords_private::PasswordCheckStatus;
using api::passwords_private::PasswordUiEntry;
-using api::passwords_private::UrlCollection;
using password_manager::BulkLeakCheckDelegateInterface;
using password_manager::BulkLeakCheckService;
using password_manager::InsecureType;
@@ -241,12 +241,6 @@ PasswordForm MakeSavedAndroidPassword(
return form;
}
-auto ExpectUrls(const std::string& formatted_origin,
- const std::string& detailed_origin) {
- return AllOf(Field(&UrlCollection::shown, formatted_origin),
- Field(&UrlCollection::link, detailed_origin));
-}
-
// Creates matcher for a given compromised info.
auto ExpectCompromisedInfo(
base::TimeDelta elapsed_time_since_compromise,
@@ -262,21 +256,15 @@ auto ExpectCompromisedInfo(
}
// Creates matcher for a given compromised credential
-auto ExpectCredential(const std::string& formatted_origin,
- const std::string& detailed_origin,
- const absl::optional<std::string>& change_password_url,
+auto ExpectCredential(const absl::optional<std::string>& change_password_url,
const std::u16string& username) {
return AllOf(
Field(&PasswordUiEntry::username, base::UTF16ToASCII(username)),
- Field(&PasswordUiEntry::urls,
- ExpectUrls(formatted_origin, detailed_origin)),
Field(&PasswordUiEntry::change_password_url, change_password_url));
}
// Creates matcher for a given compromised credential
auto ExpectCompromisedCredential(
- const std::string& formatted_origin,
- const std::string& detailed_origin,
const absl::optional<std::string>& change_password_url,
const std::u16string& username,
base::TimeDelta elapsed_time_since_compromise,
@@ -291,12 +279,10 @@ auto ExpectCompromisedCredential(
return AllOf(
Field(&PasswordUiEntry::username, base::UTF16ToASCII(username)),
change_password_url_field_matcher,
- Field(&PasswordUiEntry::urls,
- ExpectUrls(formatted_origin, detailed_origin)),
Field(&PasswordUiEntry::compromised_info,
Optional(ExpectCompromisedInfo(elapsed_time_since_compromise,
- elapsed_time_since_compromise_str,
- compromise_types))));
+ elapsed_time_since_compromise_str,
+ compromise_types))));
}
class PasswordCheckDelegateTest : public ::testing::Test {
@@ -349,7 +335,7 @@ class PasswordCheckDelegateTest : public ::testing::Test {
raw_ptr<syncer::TestSyncService> sync_service_ =
CreateAndUseSyncService(&profile_);
IdGenerator credential_id_generator_;
- password_manager::MockAffiliationService affiliation_service_;
+ password_manager::FakeAffiliationService affiliation_service_;
SavedPasswordsPresenter presenter_{&affiliation_service_, store_,
account_store_};
PasswordCheckDelegate delegate_{&profile_, &presenter_,
@@ -370,13 +356,10 @@ TEST_F(PasswordCheckDelegateTest, GetInsecureCredentialsFillsFieldsCorrectly) {
EXPECT_THAT(
delegate().GetInsecureCredentials(),
UnorderedElementsAre(
- ExpectCredential("example.com", "https://example.com/",
- "https://example.com/.well-known/change-password",
+ ExpectCredential("https://example.com/.well-known/change-password",
kUsername1),
- ExpectCredential(
- "Example App",
- "https://play.google.com/store/apps/details?id=com.example.app",
- "https://example.com/.well-known/change-password", kUsername2)));
+ ExpectCredential("https://example.com/.well-known/change-password",
+ kUsername2)));
}
// Verify that computation of weak credentials notifies observers.
@@ -404,7 +387,6 @@ TEST_F(PasswordCheckDelegateTest, WeakCheckWhenUserSignedOut) {
EXPECT_THAT(
delegate().GetInsecureCredentials(),
ElementsAre(ExpectCredential(
- "example.com", "https://example.com/",
"https://example.com/.well-known/change-password", kUsername1)));
EXPECT_EQ(api::passwords_private::PASSWORD_CHECK_STATE_SIGNED_OUT,
delegate().GetPasswordCheckStatus().state);
@@ -435,25 +417,25 @@ TEST_F(PasswordCheckDelegateTest, GetInsecureCredentialsHandlesTimes) {
EXPECT_THAT(
delegate().GetInsecureCredentials(),
ElementsAre(ExpectCompromisedCredential(
- "example.com", "https://example.com/",
"https://example.com/.well-known/change-password",
kUsername1, base::Seconds(59), "Just now",
- {api::passwords_private::COMPROMISE_TYPE_LEAKED}),
+ {api::passwords_private::COMPROMISE_TYPE_LEAKED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED}),
ExpectCompromisedCredential(
- "example.com", "https://example.com/",
"https://example.com/.well-known/change-password",
kUsername2, base::Seconds(60), "1 minute ago",
- {api::passwords_private::COMPROMISE_TYPE_LEAKED}),
+ {api::passwords_private::COMPROMISE_TYPE_LEAKED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED}),
ExpectCompromisedCredential(
- "example.org", "http://www.example.org/",
"http://www.example.org/.well-known/change-password",
kUsername1, base::Days(100), "3 months ago",
- {api::passwords_private::COMPROMISE_TYPE_LEAKED}),
+ {api::passwords_private::COMPROMISE_TYPE_LEAKED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED}),
ExpectCompromisedCredential(
- "example.org", "http://www.example.org/",
"http://www.example.org/.well-known/change-password",
kUsername2, base::Days(800), "2 years ago",
- {api::passwords_private::COMPROMISE_TYPE_LEAKED})));
+ {api::passwords_private::COMPROMISE_TYPE_LEAKED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED})));
}
// Verifies that both leaked and phished credentials are ordered correctly
@@ -485,27 +467,27 @@ TEST_F(PasswordCheckDelegateTest,
EXPECT_THAT(delegate().GetInsecureCredentials(),
UnorderedElementsAre(
ExpectCompromisedCredential(
- "example.com", "https://example.com/",
"https://example.com/.well-known/change-password",
kUsername1, base::Minutes(1), "1 minute ago",
{api::passwords_private::COMPROMISE_TYPE_LEAKED,
- api::passwords_private::COMPROMISE_TYPE_PHISHED}),
+ api::passwords_private::COMPROMISE_TYPE_PHISHED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED}),
ExpectCompromisedCredential(
- "example.org", "http://www.example.org/",
"http://www.example.org/.well-known/change-password",
kUsername1, base::Minutes(3), "3 minutes ago",
- {api::passwords_private::COMPROMISE_TYPE_PHISHED}),
+ {api::passwords_private::COMPROMISE_TYPE_PHISHED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED}),
ExpectCompromisedCredential(
- "example.org", "http://www.example.org/",
"http://www.example.org/.well-known/change-password",
kUsername2, base::Minutes(4), "4 minutes ago",
{api::passwords_private::COMPROMISE_TYPE_LEAKED,
- api::passwords_private::COMPROMISE_TYPE_PHISHED}),
+ api::passwords_private::COMPROMISE_TYPE_PHISHED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED}),
ExpectCompromisedCredential(
- "example.com", "https://example.com/",
"https://example.com/.well-known/change-password",
kUsername2, base::Minutes(2), "2 minutes ago",
- {api::passwords_private::COMPROMISE_TYPE_LEAKED})));
+ {api::passwords_private::COMPROMISE_TYPE_LEAKED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED})));
}
TEST_F(PasswordCheckDelegateTest, GetInsecureCredentialsInjectsAndroid) {
@@ -527,25 +509,22 @@ TEST_F(PasswordCheckDelegateTest, GetInsecureCredentialsInjectsAndroid) {
// Verify that the compromised credentials match what is stored in the
// password store.
- EXPECT_THAT(
- delegate().GetInsecureCredentials(),
- UnorderedElementsAre(
- ExpectCompromisedCredential(
- "Example App",
- "https://play.google.com/store/apps/details?id=com.example.app",
- "https://example.com/.well-known/change-password", kUsername2,
- base::Days(3), "3 days ago",
- {api::passwords_private::COMPROMISE_TYPE_PHISHED}),
- ExpectCompromisedCredential(
- "app.example.com",
- "https://play.google.com/store/apps/details?id=com.example.app",
- absl::nullopt, kUsername1, base::Days(4), "4 days ago",
- {api::passwords_private::COMPROMISE_TYPE_PHISHED}),
- ExpectCompromisedCredential(
- "example.com", "https://example.com/",
- "https://example.com/.well-known/change-password", kUsername1,
- base::Minutes(5), "5 minutes ago",
- {api::passwords_private::COMPROMISE_TYPE_LEAKED})));
+ EXPECT_THAT(delegate().GetInsecureCredentials(),
+ UnorderedElementsAre(
+ ExpectCompromisedCredential(
+ "https://example.com/.well-known/change-password",
+ kUsername2, base::Days(3), "3 days ago",
+ {api::passwords_private::COMPROMISE_TYPE_PHISHED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED}),
+ ExpectCompromisedCredential(
+ absl::nullopt, kUsername1, base::Days(4), "4 days ago",
+ {api::passwords_private::COMPROMISE_TYPE_PHISHED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED}),
+ ExpectCompromisedCredential(
+ "https://example.com/.well-known/change-password",
+ kUsername1, base::Minutes(5), "5 minutes ago",
+ {api::passwords_private::COMPROMISE_TYPE_LEAKED,
+ api::passwords_private::COMPROMISE_TYPE_REUSED})));
}
// Test that a change to compromised credential notifies observers.
@@ -681,68 +660,6 @@ TEST_F(PasswordCheckDelegateTest, UnmuteInsecureCredentialIdMismatch) {
EXPECT_FALSE(delegate().UnmuteInsecureCredential(credential));
}
-TEST_F(PasswordCheckDelegateTest, RecordChangePasswordFlowStarted) {
- // Create an insecure credential.
- PasswordForm form = MakeSavedPassword(kExampleCom, kUsername1);
- AddIssueToForm(&form, InsecureType::kLeaked);
- store().AddLogin(form);
- RunUntilIdle();
-
- PasswordUiEntry credential =
- std::move(delegate().GetInsecureCredentials().at(0));
- ASSERT_EQ(base::UTF16ToASCII(kUsername1), credential.username);
-
- EXPECT_CALL(
- password_change_success_tracker(),
- OnManualChangePasswordFlowStarted(
- GURL(*credential.change_password_url), credential.username,
- PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings));
-
- delegate().RecordChangePasswordFlowStarted(credential);
-}
-
-TEST_F(PasswordCheckDelegateTest,
- RecordChangePasswordFlowStartedForAppWithWebRealm) {
- // Create an insecure credential.
- PasswordForm form = MakeSavedAndroidPassword(kExampleApp, kUsername2,
- "Example App", kExampleCom);
- AddIssueToForm(&form, InsecureType::kLeaked);
- store().AddLogin(form);
- RunUntilIdle();
-
- PasswordUiEntry credential =
- std::move(delegate().GetInsecureCredentials().at(0));
- ASSERT_EQ(base::UTF16ToASCII(kUsername2), credential.username);
-
- EXPECT_CALL(
- password_change_success_tracker(),
- OnManualChangePasswordFlowStarted(
- GURL(*credential.change_password_url), credential.username,
- PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings));
-
- delegate().RecordChangePasswordFlowStarted(credential);
-}
-
-TEST_F(PasswordCheckDelegateTest,
- RecordChangePasswordFlowStartedForAppWithoutWebRealm) {
- // Create an insecure credential.
- PasswordForm form = MakeSavedAndroidPassword(kExampleApp, kUsername1, "", "");
- AddIssueToForm(&form, InsecureType::kLeaked);
- store().AddLogin(form);
- RunUntilIdle();
-
- PasswordUiEntry credential =
- std::move(delegate().GetInsecureCredentials().at(0));
- ASSERT_EQ(base::UTF16ToASCII(kUsername1), credential.username);
-
- // Since no password change link exists, we expect no call to the tracker.
- EXPECT_CALL(password_change_success_tracker(),
- OnManualChangePasswordFlowStarted)
- .Times(0);
-
- delegate().RecordChangePasswordFlowStarted(credential);
-}
-
// Tests that we don't create an entry in the database if there is no matching
// saved password.
TEST_F(PasswordCheckDelegateTest, OnLeakFoundDoesNotCreateCredential) {
@@ -916,21 +833,6 @@ TEST_F(PasswordCheckDelegateTest, GetPasswordCheckStatusCount) {
EXPECT_EQ(*status.total_number_of_passwords, 2);
}
-// Verifies that the case where the check is canceled is reported correctly.
-TEST_F(PasswordCheckDelegateTest, GetPasswordCheckStatusCanceled) {
- identity_test_env().MakeAccountAvailable(kTestEmail);
- store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1));
- RunUntilIdle();
-
- delegate().StartPasswordCheck();
- EXPECT_EQ(api::passwords_private::PASSWORD_CHECK_STATE_RUNNING,
- delegate().GetPasswordCheckStatus().state);
-
- delegate().StopPasswordCheck();
- EXPECT_EQ(api::passwords_private::PASSWORD_CHECK_STATE_CANCELED,
- delegate().GetPasswordCheckStatus().state);
-}
-
// Verifies that the case where the user is offline is reported correctly.
TEST_F(PasswordCheckDelegateTest, GetPasswordCheckStatusOffline) {
identity_test_env().MakeAccountAvailable(kTestEmail);
@@ -1085,24 +987,6 @@ TEST_F(PasswordCheckDelegateTest, OnCredentialDoneUpdatesProgress) {
EXPECT_EQ(0, *status->remaining_in_queue);
}
-// Tests that StopPasswordCheck() invokes pending callbacks before
-// initialization finishes.
-TEST_F(PasswordCheckDelegateTest,
- StopPasswordCheckRespondsCancelsBeforeInitialization) {
- MockStartPasswordCheckCallback callback1;
- MockStartPasswordCheckCallback callback2;
- delegate().StartPasswordCheck(callback1.Get());
- delegate().StartPasswordCheck(callback2.Get());
-
- EXPECT_CALL(callback1, Run(BulkLeakCheckService::State::kIdle));
- EXPECT_CALL(callback2, Run(BulkLeakCheckService::State::kIdle));
- delegate().StopPasswordCheck();
-
- Mock::VerifyAndClearExpectations(&callback1);
- Mock::VerifyAndClearExpectations(&callback2);
- RunUntilIdle();
-}
-
// Tests that pending callbacks get invoked once initialization finishes.
TEST_F(PasswordCheckDelegateTest,
StartPasswordCheckRunsCallbacksAfterInitialization) {
@@ -1117,7 +1001,7 @@ TEST_F(PasswordCheckDelegateTest,
// Use a local delegate instead of |delegate()| so that the Password Store can
// be set-up prior to constructing the object.
- password_manager::MockAffiliationService affiliation_service;
+ password_manager::FakeAffiliationService affiliation_service;
SavedPasswordsPresenter new_presenter(&affiliation_service, &store(),
/*account_store=*/nullptr);
PasswordCheckDelegate delegate = CreateDelegate(&new_presenter);
@@ -1162,10 +1046,6 @@ TEST_F(PasswordCheckDelegateTest, WellKnownChangePasswordUrl_androidrealm) {
// credentials.
TEST_F(PasswordCheckDelegateTest,
GetCredentialsWithReusedPasswordFillsFieldsCorrectly) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- password_manager::features::kPasswordManagerRedesign);
-
store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1, kWeakPassword1));
store().AddLogin(MakeSavedPassword(kExampleCom, kUsername2, kWeakPassword2));
store().AddLogin(MakeSavedAndroidPassword(
@@ -1186,34 +1066,23 @@ TEST_F(PasswordCheckDelegateTest,
Field(&api::passwords_private::PasswordUiEntryList::entries,
UnorderedElementsAre(
ExpectCredential(
- "example.com", "https://example.com/",
"https://example.com/.well-known/change-password",
kUsername2),
ExpectCredential(
- "Example App",
- "https://play.google.com/store/apps/"
- "details?id=com.example.app",
"https://example.com/.well-known/change-password",
kUsername1))),
Field(&api::passwords_private::PasswordUiEntryList::entries,
UnorderedElementsAre(
ExpectCredential(
- "example.com", "https://example.com/",
"https://example.com/.well-known/change-password",
kUsername1),
ExpectCredential(
- "Example App",
- "https://play.google.com/store/apps/"
- "details?id=com.example.app",
"https://example.com/.well-known/change-password",
kUsername2)))));
}
TEST_F(PasswordCheckDelegateTest,
GetCredentialsWithReusedPasswordAvoidsSingleReuse) {
- base::test::ScopedFeatureList scoped_feature_list(
- password_manager::features::kPasswordManagerRedesign);
-
store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1, kWeakPassword1));
store().AddLogin(MakeSavedPassword(kExampleApp, kUsername2, kWeakPassword1));
RunUntilIdle();
@@ -1230,3 +1099,4 @@ TEST_F(PasswordCheckDelegateTest,
}
} // namespace extensions
+ \ No newline at end of file
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc
index 181e9e81ac8..7ee1928a92f 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc
@@ -49,29 +49,6 @@ PasswordsPrivateRecordPasswordsPageAccessInSettingsFunction::Run() {
return RespondNow(NoArguments());
}
-// PasswordsPrivateChangeSavedPasswordFunction
-ResponseAction PasswordsPrivateChangeSavedPasswordFunction::Run() {
- if (!GetDelegate(browser_context())) {
- return RespondNow(Error(kNoDelegateError));
- }
-
- auto parameters =
- api::passwords_private::ChangeSavedPassword::Params::Create(args());
- EXTENSION_FUNCTION_VALIDATE(parameters);
-
- auto new_id = GetDelegate(browser_context())
- ->ChangeSavedPassword(parameters->id, parameters->params);
- if (new_id.has_value()) {
- return RespondNow(ArgumentList(
- api::passwords_private::ChangeSavedPassword::Results::Create(
- new_id.value())));
- }
- return RespondNow(Error(
- "Could not change the password. Either the password is empty, the user "
- "is not authenticated or no matching password could be found for the "
- "id."));
-}
-
// PasswordsPrivateChangeCredentialFunction
ResponseAction PasswordsPrivateChangeCredentialFunction::Run() {
if (!GetDelegate(browser_context())) {
@@ -208,19 +185,11 @@ ResponseAction PasswordsPrivateGetSavedPasswordListFunction::Run() {
return RespondNow(Error(kNoDelegateError));
}
- // GetList() can immediately call GotList() (which would Respond() before
- // RespondLater()). So we post a task to preserve order.
- base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
- FROM_HERE,
- base::BindOnce(&PasswordsPrivateGetSavedPasswordListFunction::GetList,
- this));
- return RespondLater();
-}
-
-void PasswordsPrivateGetSavedPasswordListFunction::GetList() {
GetDelegate(browser_context())
->GetSavedPasswordsList(base::BindOnce(
&PasswordsPrivateGetSavedPasswordListFunction::GotList, this));
+
+ return did_respond() ? AlreadyResponded() : RespondLater();
}
void PasswordsPrivateGetSavedPasswordListFunction::GotList(
@@ -246,19 +215,11 @@ ResponseAction PasswordsPrivateGetPasswordExceptionListFunction::Run() {
return RespondNow(Error(kNoDelegateError));
}
- // GetList() can immediately call GotList() (which would Respond() before
- // RespondLater()). So we post a task to preserve order.
- base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
- FROM_HERE,
- base::BindOnce(&PasswordsPrivateGetPasswordExceptionListFunction::GetList,
- this));
- return RespondLater();
-}
-
-void PasswordsPrivateGetPasswordExceptionListFunction::GetList() {
GetDelegate(browser_context())
->GetPasswordExceptionsList(base::BindOnce(
&PasswordsPrivateGetPasswordExceptionListFunction::GotList, this));
+
+ return did_respond() ? AlreadyResponded() : RespondLater();
}
void PasswordsPrivateGetPasswordExceptionListFunction::GotList(
@@ -282,6 +243,45 @@ ResponseAction PasswordsPrivateMovePasswordsToAccountFunction::Run() {
return RespondNow(NoArguments());
}
+// PasswordsPrivateFetchFamilyMembersFunction
+ResponseAction PasswordsPrivateFetchFamilyMembersFunction::Run() {
+ if (!GetDelegate(browser_context())) {
+ return RespondNow(Error(kNoDelegateError));
+ }
+
+ GetDelegate(browser_context())
+ ->FetchFamilyMembers(base::BindOnce(
+ &PasswordsPrivateFetchFamilyMembersFunction::FamilyFetchCompleted,
+ this));
+
+ // `FamilyFetchCompleted()` might respond before we reach this point.
+ return did_respond() ? AlreadyResponded() : RespondLater();
+}
+
+// PasswordsPrivateSharePasswordFunction
+ResponseAction PasswordsPrivateSharePasswordFunction::Run() {
+ if (!GetDelegate(browser_context())) {
+ return RespondNow(Error(kNoDelegateError));
+ }
+
+ // TODO(crbug/1445526): Respond with an error if arguments are not valid
+ // (password doesn't exist, auth validity expired, recipient doesn't have
+ // public key or user_id).
+
+ auto parameters =
+ api::passwords_private::SharePassword::Params::Create(args());
+ EXTENSION_FUNCTION_VALIDATE(parameters);
+ GetDelegate(browser_context())
+ ->SharePassword(parameters->id, parameters->recipients);
+ return RespondNow(NoArguments());
+}
+
+void PasswordsPrivateFetchFamilyMembersFunction::FamilyFetchCompleted(
+ const api::passwords_private::FamilyFetchResults& result) {
+ Respond(ArgumentList(
+ api::passwords_private::FetchFamilyMembers::Results::Create(result)));
+}
+
// PasswordsPrivateImportPasswordsFunction
ResponseAction PasswordsPrivateImportPasswordsFunction::Run() {
if (!GetDelegate(browser_context())) {
@@ -371,16 +371,6 @@ void PasswordsPrivateExportPasswordsFunction::ExportRequestCompleted(
Respond(Error(error));
}
-// PasswordsPrivateCancelExportPasswordsFunction
-ResponseAction PasswordsPrivateCancelExportPasswordsFunction::Run() {
- if (!GetDelegate(browser_context())) {
- return RespondNow(Error(kNoDelegateError));
- }
-
- GetDelegate(browser_context())->CancelExportPasswords();
- return RespondNow(NoArguments());
-}
-
// PasswordsPrivateRequestExportProgressStatusFunction
ResponseAction PasswordsPrivateRequestExportProgressStatusFunction::Run() {
if (!GetDelegate(browser_context())) {
@@ -491,25 +481,6 @@ ResponseAction PasswordsPrivateUnmuteInsecureCredentialFunction::Run() {
return RespondNow(NoArguments());
}
-// PasswordsPrivateRecordChangePasswordFlowStartedFunction:
-PasswordsPrivateRecordChangePasswordFlowStartedFunction::
- ~PasswordsPrivateRecordChangePasswordFlowStartedFunction() = default;
-
-ResponseAction PasswordsPrivateRecordChangePasswordFlowStartedFunction::Run() {
- if (!GetDelegate(browser_context())) {
- return RespondNow(Error(kNoDelegateError));
- }
-
- auto parameters =
- api::passwords_private::RecordChangePasswordFlowStarted::Params::Create(
- args());
- EXTENSION_FUNCTION_VALIDATE(parameters);
-
- GetDelegate(browser_context())
- ->RecordChangePasswordFlowStarted(parameters->credential);
- return RespondNow(NoArguments());
-}
-
// PasswordsPrivateStartPasswordCheckFunction:
PasswordsPrivateStartPasswordCheckFunction::
~PasswordsPrivateStartPasswordCheckFunction() = default;
@@ -535,19 +506,6 @@ void PasswordsPrivateStartPasswordCheckFunction::OnStarted(
: Error("Starting password check failed."));
}
-// PasswordsPrivateStopPasswordCheckFunction:
-PasswordsPrivateStopPasswordCheckFunction::
- ~PasswordsPrivateStopPasswordCheckFunction() = default;
-
-ResponseAction PasswordsPrivateStopPasswordCheckFunction::Run() {
- if (!GetDelegate(browser_context())) {
- return RespondNow(Error(kNoDelegateError));
- }
-
- GetDelegate(browser_context())->StopPasswordCheck();
- return RespondNow(NoArguments());
-}
-
// PasswordsPrivateGetPasswordCheckStatusFunction:
PasswordsPrivateGetPasswordCheckStatusFunction::
~PasswordsPrivateGetPasswordCheckStatusFunction() = default;
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h
index 97f445d33f8..66ff906714b 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h
@@ -29,18 +29,6 @@ class PasswordsPrivateRecordPasswordsPageAccessInSettingsFunction
ResponseAction Run() override;
};
-class PasswordsPrivateChangeSavedPasswordFunction : public ExtensionFunction {
- public:
- DECLARE_EXTENSION_FUNCTION("passwordsPrivate.changeSavedPassword",
- PASSWORDSPRIVATE_CHANGESAVEDPASSWORD)
-
- protected:
- ~PasswordsPrivateChangeSavedPasswordFunction() override = default;
-
- // ExtensionFunction overrides.
- ResponseAction Run() override;
-};
-
class PasswordsPrivateChangeCredentialFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("passwordsPrivate.changeCredential",
@@ -136,7 +124,6 @@ class PasswordsPrivateGetSavedPasswordListFunction : public ExtensionFunction {
ResponseAction Run() override;
private:
- void GetList();
void GotList(const PasswordsPrivateDelegate::UiEntries& entries);
};
@@ -165,7 +152,6 @@ class PasswordsPrivateGetPasswordExceptionListFunction
ResponseAction Run() override;
private:
- void GetList();
void GotList(const PasswordsPrivateDelegate::ExceptionEntries& entries);
};
@@ -182,6 +168,34 @@ class PasswordsPrivateMovePasswordsToAccountFunction
ResponseAction Run() override;
};
+class PasswordsPrivateFetchFamilyMembersFunction : public ExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("passwordsPrivate.fetchFamilyMembers",
+ PASSWORDSPRIVATE_FETCHFAMILYMEMBERS)
+
+ protected:
+ ~PasswordsPrivateFetchFamilyMembersFunction() override = default;
+
+ // ExtensionFunction overrides.
+ ResponseAction Run() override;
+
+ private:
+ void FamilyFetchCompleted(
+ const api::passwords_private::FamilyFetchResults& results);
+};
+
+class PasswordsPrivateSharePasswordFunction : public ExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("passwordsPrivate.sharePassword",
+ PASSWORDSPRIVATE_SHAREPASSWORD)
+
+ protected:
+ ~PasswordsPrivateSharePasswordFunction() override = default;
+
+ // ExtensionFunction overrides.
+ ResponseAction Run() override;
+};
+
class PasswordsPrivateImportPasswordsFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("passwordsPrivate.importPasswords",
@@ -240,18 +254,6 @@ class PasswordsPrivateExportPasswordsFunction : public ExtensionFunction {
void ExportRequestCompleted(const std::string& error);
};
-class PasswordsPrivateCancelExportPasswordsFunction : public ExtensionFunction {
- public:
- DECLARE_EXTENSION_FUNCTION("passwordsPrivate.cancelExportPasswords",
- PASSWORDSPRIVATE_CANCELEXPORTPASSWORDS)
-
- protected:
- ~PasswordsPrivateCancelExportPasswordsFunction() override = default;
-
- // ExtensionFunction overrides.
- ResponseAction Run() override;
-};
-
class PasswordsPrivateRequestExportProgressStatusFunction
: public ExtensionFunction {
public:
@@ -344,19 +346,6 @@ class PasswordsPrivateUnmuteInsecureCredentialFunction
ResponseAction Run() override;
};
-class PasswordsPrivateRecordChangePasswordFlowStartedFunction
- : public ExtensionFunction {
- public:
- DECLARE_EXTENSION_FUNCTION("passwordsPrivate.recordChangePasswordFlowStarted",
- PASSWORDSPRIVATE_RECORDCHANGEPASSWORDFLOWSTARTED)
-
- protected:
- ~PasswordsPrivateRecordChangePasswordFlowStartedFunction() override;
-
- // ExtensionFunction overrides.
- ResponseAction Run() override;
-};
-
class PasswordsPrivateStartPasswordCheckFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("passwordsPrivate.startPasswordCheck",
@@ -372,18 +361,6 @@ class PasswordsPrivateStartPasswordCheckFunction : public ExtensionFunction {
void OnStarted(password_manager::BulkLeakCheckService::State state);
};
-class PasswordsPrivateStopPasswordCheckFunction : public ExtensionFunction {
- public:
- DECLARE_EXTENSION_FUNCTION("passwordsPrivate.stopPasswordCheck",
- PASSWORDSPRIVATE_STOPPASSWORDCHECK)
-
- protected:
- ~PasswordsPrivateStopPasswordCheckFunction() override;
-
- // ExtensionFunction overrides.
- ResponseAction Run() override;
-};
-
class PasswordsPrivateGetPasswordCheckStatusFunction
: public ExtensionFunction {
public:
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc
index 2eb6374b579..adfe50b7d8a 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc
@@ -41,12 +41,7 @@ namespace {
class PasswordsPrivateApiTest : public ExtensionApiTest {
public:
- PasswordsPrivateApiTest() {
- scoped_feature_list_.InitWithFeatures(
- {password_manager::features::kPasswordManagerRedesign,
- password_manager::features::kPasswordsGrouping},
- {});
- }
+ PasswordsPrivateApiTest() = default;
PasswordsPrivateApiTest(const PasswordsPrivateApiTest&) = delete;
PasswordsPrivateApiTest& operator=(const PasswordsPrivateApiTest&) = delete;
@@ -80,6 +75,14 @@ class PasswordsPrivateApiTest : public ExtensionApiTest {
return test_delegate_->ImportPasswordsTriggered();
}
+ bool fetch_family_members_was_triggered() {
+ return test_delegate_->FetchFamilyMembersTriggered();
+ }
+
+ bool share_password_was_triggered() {
+ return test_delegate_->SharePasswordTriggered();
+ }
+
bool continue_import_was_triggered() {
return test_delegate_->ContinueImportTriggered();
}
@@ -92,18 +95,10 @@ class PasswordsPrivateApiTest : public ExtensionApiTest {
return test_delegate_->ExportPasswordsTriggered();
}
- bool cancelExportPasswordsWasTriggered() {
- return test_delegate_->CancelExportPasswordsTriggered();
- }
-
bool start_password_check_triggered() {
return test_delegate_->StartPasswordCheckTriggered();
}
- bool stop_password_check_triggered() {
- return test_delegate_->StopPasswordCheckTriggered();
- }
-
void set_start_password_check_state(
password_manager::BulkLeakCheckService::State state) {
test_delegate_->SetStartPasswordCheckState(state);
@@ -127,10 +122,6 @@ class PasswordsPrivateApiTest : public ExtensionApiTest {
test_delegate_->SetIsAccountStoreDefault(is_default);
}
- const std::string& last_change_flow_url() {
- return test_delegate_->last_change_flow_url();
- }
-
const std::vector<int>& last_moved_passwords() const {
return test_delegate_->last_moved_passwords();
}
@@ -148,7 +139,6 @@ class PasswordsPrivateApiTest : public ExtensionApiTest {
}
private:
- base::test::ScopedFeatureList scoped_feature_list_;
scoped_refptr<TestPasswordsPrivateDelegate> test_delegate_;
};
@@ -187,28 +177,6 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, AddPasswordWhenOperationFails) {
EXPECT_TRUE(RunPasswordsSubtest("addPasswordWhenOperationFails")) << message_;
}
-IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ChangeSavedPasswordSucceeds) {
- EXPECT_TRUE(RunPasswordsSubtest("changeSavedPasswordSucceeds")) << message_;
-}
-
-IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest,
- ChangeSavedPasswordWithIncorrectIdFails) {
- EXPECT_TRUE(RunPasswordsSubtest("changeSavedPasswordWithIncorrectIdFails"))
- << message_;
-}
-
-IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest,
- ChangeSavedPasswordWithEmptyPasswordFails) {
- EXPECT_TRUE(RunPasswordsSubtest("changeSavedPasswordWithEmptyPasswordFails"))
- << message_;
-}
-
-IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest,
- ChangeSavedPasswordWithNoteSucceeds) {
- EXPECT_TRUE(RunPasswordsSubtest("ChangeSavedPasswordWithNoteSucceeds"))
- << message_;
-}
-
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest,
ChangeCredentialChangePassword) {
EXPECT_TRUE(RunPasswordsSubtest("changeCredentialChangePassword"))
@@ -267,6 +235,18 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, GetPasswordExceptionList) {
EXPECT_TRUE(RunPasswordsSubtest("getPasswordExceptionList")) << message_;
}
+IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, FetchFamilyMembers) {
+ EXPECT_FALSE(fetch_family_members_was_triggered());
+ EXPECT_TRUE(RunPasswordsSubtest("fetchFamilyMembers")) << message_;
+ EXPECT_TRUE(fetch_family_members_was_triggered());
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, SharePassword) {
+ EXPECT_FALSE(share_password_was_triggered());
+ EXPECT_TRUE(RunPasswordsSubtest("sharePassword")) << message_;
+ EXPECT_TRUE(share_password_was_triggered());
+}
+
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ImportPasswords) {
EXPECT_FALSE(importPasswordsWasTriggered());
EXPECT_TRUE(RunPasswordsSubtest("importPasswords")) << message_;
@@ -291,12 +271,6 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ExportPasswords) {
EXPECT_TRUE(exportPasswordsWasTriggered());
}
-IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, CancelExportPasswords) {
- EXPECT_FALSE(cancelExportPasswordsWasTriggered());
- EXPECT_TRUE(RunPasswordsSubtest("cancelExportPasswords")) << message_;
- EXPECT_TRUE(cancelExportPasswordsWasTriggered());
-}
-
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, RequestExportProgressStatus) {
EXPECT_TRUE(RunPasswordsSubtest("requestExportProgressStatus")) << message_;
}
@@ -339,21 +313,6 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, UnmuteInsecureCredentialFails) {
EXPECT_TRUE(RunPasswordsSubtest("unmuteInsecureCredentialFails")) << message_;
}
-IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest,
- RecordChangePasswordFlowStarted) {
- EXPECT_TRUE(RunPasswordsSubtest("recordChangePasswordFlowStarted"))
- << message_;
- EXPECT_EQ(last_change_flow_url(),
- "https://example.com/.well-known/change-password");
-}
-
-IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest,
- RecordChangePasswordFlowStartedAppNoUrl) {
- EXPECT_TRUE(RunPasswordsSubtest("recordChangePasswordFlowStartedAppNoUrl"))
- << message_;
- EXPECT_EQ(last_change_flow_url(), "");
-}
-
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, StartPasswordCheck) {
set_start_password_check_state(
password_manager::BulkLeakCheckService::State::kRunning);
@@ -370,12 +329,6 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, StartPasswordCheckFailed) {
EXPECT_TRUE(start_password_check_triggered());
}
-IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, StopPasswordCheck) {
- EXPECT_FALSE(stop_password_check_triggered());
- EXPECT_TRUE(RunPasswordsSubtest("stopPasswordCheck")) << message_;
- EXPECT_TRUE(stop_password_check_triggered());
-}
-
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, GetPasswordCheckStatus) {
EXPECT_TRUE(RunPasswordsSubtest("getPasswordCheckStatus")) << message_;
}
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h
index 99c902c270c..5eced39ce00 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h
@@ -38,6 +38,11 @@ class PasswordsPrivateDelegate
using ImportResultsCallback =
base::OnceCallback<void(const api::passwords_private::ImportResults&)>;
+ using FetchFamilyResultsCallback = base::OnceCallback<void(
+ const api::passwords_private::FamilyFetchResults&)>;
+
+ using ShareRecipients = std::vector<api::passwords_private::RecipientInfo>;
+
using PlaintextPasswordCallback =
base::OnceCallback<void(absl::optional<std::u16string>)>;
@@ -87,15 +92,6 @@ class PasswordsPrivateDelegate
bool use_account_store,
content::WebContents* web_contents) = 0;
- // Changes the username and password corresponding to |ids|.
- // |ids|: The ids for the password entries being updated.
- // |params|: The struct which holds the new username, password and note.
- // Returns the ids if the change was successful (can be the same ids if the
- // username and the password didn't change), nullopt otherwise.
- virtual absl::optional<int> ChangeSavedPassword(
- int id,
- const api::passwords_private::ChangeSavedPasswordParams& params) = 0;
-
// Updates a credential. Not all attributes can be updated.
// |credential|: The credential to be updated. Matched to an existing
// credential by id.
@@ -154,6 +150,15 @@ class PasswordsPrivateDelegate
virtual void MovePasswordsToAccount(const std::vector<int>& ids,
content::WebContents* web_contents) = 0;
+ // Fetches family members of the current user for the password sharing flow.
+ // |callback|: Used to communicate the status of a request to fetch family
+ // members, as well as the data returned in the response.
+ virtual void FetchFamilyMembers(FetchFamilyResultsCallback callback) = 0;
+
+ // Sends sharing invitations for a credential with given |id| to the
+ // |recipients|.
+ virtual void SharePassword(int id, const ShareRecipients& recipients) = 0;
+
// Trigger the password import procedure, allowing the user to select a file
// containing passwords to import.
// |to_store|: destination store (Device or Account) for imported passwords.
@@ -186,9 +191,6 @@ class PasswordsPrivateDelegate
base::OnceCallback<void(const std::string&)> callback,
content::WebContents* web_contents) = 0;
- // Cancel any ongoing export.
- virtual void CancelExportPasswords() = 0;
-
// Get the most recent progress status.
virtual api::passwords_private::ExportProgressStatus
GetExportProgressStatus() = 0;
@@ -225,15 +227,9 @@ class PasswordsPrivateDelegate
virtual bool UnmuteInsecureCredential(
const api::passwords_private::PasswordUiEntry& credential) = 0;
- // Records that a change password flow was started for |credential|.
- virtual void RecordChangePasswordFlowStarted(
- const api::passwords_private::PasswordUiEntry& credential) = 0;
-
// Requests to start a check for insecure passwords. Invokes |callback|
// once a check is running or the request was stopped via StopPasswordCheck().
virtual void StartPasswordCheck(StartPasswordCheckCallback callback) = 0;
- // Stops a check for insecure passwords.
- virtual void StopPasswordCheck() = 0;
// Returns the current status of the password check.
virtual api::passwords_private::PasswordCheckStatus
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc
index 8543dddf70a..399c04f1457 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc
@@ -23,36 +23,23 @@ using content::BrowserContext;
PasswordsPrivateDelegateProxy::PasswordsPrivateDelegateProxy(
BrowserContext* browser_context)
- : browser_context_(browser_context) {
- if (base::FeatureList::IsEnabled(
- password_manager::features::kPasswordManagerRedesign)) {
- return;
- }
- scoped_instance_ = base::MakeRefCounted<PasswordsPrivateDelegateImpl>(
- static_cast<Profile*>(browser_context_));
-}
+ : browser_context_(browser_context) {}
PasswordsPrivateDelegateProxy::PasswordsPrivateDelegateProxy(
BrowserContext* browser_context,
scoped_refptr<PasswordsPrivateDelegate> delegate)
- : browser_context_(browser_context), scoped_instance_(std::move(delegate)) {
- weak_instance_ = scoped_instance_->AsWeakPtr();
+ : browser_context_(browser_context) {
+ weak_instance_ = delegate->AsWeakPtr();
}
PasswordsPrivateDelegateProxy::~PasswordsPrivateDelegateProxy() = default;
void PasswordsPrivateDelegateProxy::Shutdown() {
browser_context_ = nullptr;
weak_instance_ = nullptr;
- scoped_instance_ = nullptr;
}
scoped_refptr<PasswordsPrivateDelegate>
PasswordsPrivateDelegateProxy::GetOrCreateDelegate() {
- if (!base::FeatureList::IsEnabled(
- password_manager::features::kPasswordManagerRedesign)) {
- return scoped_instance_;
- }
-
if (weak_instance_) {
return scoped_refptr<PasswordsPrivateDelegate>(weak_instance_.get());
}
@@ -66,11 +53,7 @@ PasswordsPrivateDelegateProxy::GetOrCreateDelegate() {
scoped_refptr<PasswordsPrivateDelegate>
PasswordsPrivateDelegateProxy::GetDelegate() {
- if (base::FeatureList::IsEnabled(
- password_manager::features::kPasswordManagerRedesign)) {
- return scoped_refptr<PasswordsPrivateDelegate>(weak_instance_.get());
- }
- return scoped_instance_;
+ return scoped_refptr<PasswordsPrivateDelegate>(weak_instance_.get());
}
// static
@@ -109,9 +92,10 @@ PasswordsPrivateDelegateFactory::PasswordsPrivateDelegateFactory()
PasswordsPrivateDelegateFactory::~PasswordsPrivateDelegateFactory() = default;
-KeyedService* PasswordsPrivateDelegateFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+PasswordsPrivateDelegateFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const {
- return new PasswordsPrivateDelegateProxy(profile);
+ return std::make_unique<PasswordsPrivateDelegateProxy>(profile);
}
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h
index 2303b8ea9db..49e0e0f9056 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h
@@ -44,9 +44,6 @@ class PasswordsPrivateDelegateProxy : public KeyedService {
raw_ptr<content::BrowserContext> browser_context_ = nullptr;
base::WeakPtr<PasswordsPrivateDelegate> weak_instance_;
- // TODO(crbug.com/1412348): Remove this after the feature is enabled by
- // default.
- scoped_refptr<PasswordsPrivateDelegate> scoped_instance_;
};
// Factory for creating PasswordPrivateDelegates.
@@ -65,7 +62,7 @@ class PasswordsPrivateDelegateFactory : public ProfileKeyedServiceFactory {
~PasswordsPrivateDelegateFactory() override;
// BrowserContextKeyedServiceFactory implementation.
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
index 5c87121acf7..b1a28a5a678 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
@@ -23,6 +23,7 @@
#include "chrome/browser/password_manager/account_password_store_factory.h"
#include "chrome/browser/password_manager/affiliation_service_factory.h"
#include "chrome/browser/password_manager/chrome_password_manager_client.h"
+#include "chrome/browser/password_manager/password_sender_service_factory.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
@@ -40,16 +41,20 @@
#include "chrome/browser/web_applications/web_app_install_params.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/webauthn/passkey_model_factory.h"
+#include "chrome/common/channel_info.h"
#include "chrome/common/extensions/api/passwords_private.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/password_manager/core/browser/affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/features/password_features.h"
#include "components/password_manager/core/browser/password_access_authenticator.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_manager_features_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_sync_util.h"
+#include "components/password_manager/core/browser/sharing/password_sender_service.h"
+#include "components/password_manager/core/browser/sharing/recipients_fetcher_impl.h"
#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/prefs/pref_service.h"
@@ -58,6 +63,7 @@
#include "components/sync/service/sync_service.h"
#include "components/url_formatter/elide_url.h"
#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
@@ -86,6 +92,7 @@ namespace {
using password_manager::CredentialFacet;
using password_manager::CredentialUIEntry;
+using password_manager::FetchFamilyMembersRequestStatus;
// The error message returned to the UI when Chrome refuses to start multiple
// exports.
@@ -417,35 +424,6 @@ bool PasswordsPrivateDelegateImpl::AddPassword(
return success;
}
-absl::optional<int> PasswordsPrivateDelegateImpl::ChangeSavedPassword(
- int id,
- const api::passwords_private::ChangeSavedPasswordParams& params) {
- const CredentialUIEntry* original_credential =
- credential_id_generator_.TryGetKey(id);
- if (!original_credential) {
- return absl::nullopt;
- }
-
- CredentialUIEntry updated_credential = *original_credential;
- updated_credential.username = base::UTF8ToUTF16(params.username);
- updated_credential.password = base::UTF8ToUTF16(params.password);
- if (params.note) {
- updated_credential.note = base::UTF8ToUTF16(*params.note);
- }
- switch (saved_passwords_presenter_.EditSavedCredentials(*original_credential,
- updated_credential)) {
- case password_manager::SavedPasswordsPresenter::EditResult::kSuccess:
- case password_manager::SavedPasswordsPresenter::EditResult::kNothingChanged:
- break;
- case password_manager::SavedPasswordsPresenter::EditResult::kNotFound:
- case password_manager::SavedPasswordsPresenter::EditResult::kAlreadyExisits:
- case password_manager::SavedPasswordsPresenter::EditResult::kEmptyPassword:
- return absl::nullopt;
- }
-
- return credential_id_generator_.GenerateId(std::move(updated_credential));
-}
-
bool PasswordsPrivateDelegateImpl::ChangeCredential(
const api::passwords_private::PasswordUiEntry& credential) {
const CredentialUIEntry* original_credential =
@@ -581,6 +559,49 @@ void PasswordsPrivateDelegateImpl::OsReauthCall(
#endif
}
+void PasswordsPrivateDelegateImpl::OnFetchingFamilyMembersCompleted(
+ FetchFamilyResultsCallback callback,
+ std::vector<password_manager::RecipientInfo> family_members,
+ FetchFamilyMembersRequestStatus request_status) {
+ api::passwords_private::FamilyFetchResults results;
+ switch (request_status) {
+ case FetchFamilyMembersRequestStatus::kUnknown:
+ case FetchFamilyMembersRequestStatus::kNetworkError:
+ case FetchFamilyMembersRequestStatus::kPendingRequest:
+ results.status = api::passwords_private::FamilyFetchStatus::
+ FAMILY_FETCH_STATUS_UNKNOWN_ERROR;
+ break;
+ case FetchFamilyMembersRequestStatus::kSuccess:
+ results.status = api::passwords_private::FamilyFetchStatus::
+ FAMILY_FETCH_STATUS_SUCCESS;
+ break;
+ case FetchFamilyMembersRequestStatus::kNoFamily:
+ results.status = api::passwords_private::FamilyFetchStatus::
+ FAMILY_FETCH_STATUS_NO_MEMBERS;
+ }
+ if (request_status == FetchFamilyMembersRequestStatus::kSuccess) {
+ for (const password_manager::RecipientInfo& family_member :
+ family_members) {
+ api::passwords_private::RecipientInfo recipient_info;
+ recipient_info.user_id = family_member.user_id;
+ recipient_info.email = family_member.email;
+ recipient_info.display_name = family_member.user_name;
+ recipient_info.profile_image_url = family_member.profile_image_url;
+
+ if (!family_member.public_key.key.empty()) {
+ recipient_info.is_eligible = true;
+ api::passwords_private::PublicKey public_key;
+ public_key.value = family_member.public_key.key;
+ public_key.version = family_member.public_key.key_version;
+ recipient_info.public_key = std::move(public_key);
+ }
+
+ results.family_members.push_back(std::move(recipient_info));
+ }
+ }
+ std::move(callback).Run(results);
+}
+
void PasswordsPrivateDelegateImpl::OsReauthTimeoutCall() {
#if !BUILDFLAG(IS_LINUX)
PasswordsPrivateEventRouter* router =
@@ -611,17 +632,14 @@ void PasswordsPrivateDelegateImpl::SetCredentials(
CreatePasswordUiEntryFromCredentialUiEntry(std::move(credential)));
}
}
- if (base::FeatureList::IsEnabled(
- password_manager::features::kPasswordsGrouping)) {
- for (CredentialUIEntry& credential :
- saved_passwords_presenter_.GetBlockedSites()) {
- api::passwords_private::ExceptionEntry current_exception_entry;
- current_exception_entry.urls =
- CreateUrlCollectionFromCredential(credential);
- current_exception_entry.id =
- credential_id_generator_.GenerateId(std::move(credential));
- current_exceptions_.push_back(std::move(current_exception_entry));
- }
+ for (CredentialUIEntry& credential :
+ saved_passwords_presenter_.GetBlockedSites()) {
+ api::passwords_private::ExceptionEntry current_exception_entry;
+ current_exception_entry.urls =
+ CreateUrlCollectionFromCredential(credential);
+ current_exception_entry.id =
+ credential_id_generator_.GenerateId(std::move(credential));
+ current_exceptions_.push_back(std::move(current_exception_entry));
}
if (current_entries_initialized_) {
@@ -677,6 +695,49 @@ void PasswordsPrivateDelegateImpl::MovePasswordsToAccount(
kExplicitlyTriggeredForMultiplePasswordsInSettings);
}
+void PasswordsPrivateDelegateImpl::FetchFamilyMembers(
+ FetchFamilyResultsCallback callback) {
+ if (!sharing_password_recipients_fetcher_) {
+ sharing_password_recipients_fetcher_ =
+ std::make_unique<password_manager::RecipientsFetcherImpl>(
+ chrome::GetChannel(),
+ profile_->GetDefaultStoragePartition()
+ ->GetURLLoaderFactoryForBrowserProcess(),
+ IdentityManagerFactory::GetForProfile(profile_));
+ }
+ sharing_password_recipients_fetcher_->FetchFamilyMembers(base::BindOnce(
+ &PasswordsPrivateDelegateImpl::OnFetchingFamilyMembersCompleted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void PasswordsPrivateDelegateImpl::SharePassword(
+ int id,
+ const ShareRecipients& recipients) {
+ const CredentialUIEntry* entry = credential_id_generator_.TryGetKey(id);
+ if (!entry) {
+ return;
+ }
+
+ std::vector<password_manager::PasswordForm> corresponding_credentials =
+ saved_passwords_presenter_.GetCorrespondingPasswordForms(*entry);
+ if (corresponding_credentials.empty()) {
+ return;
+ }
+
+ password_manager::PasswordSenderService* password_sender_service =
+ PasswordSenderServiceFactory::GetForProfile(profile_);
+ for (const api::passwords_private::RecipientInfo& recipient_info :
+ recipients) {
+ CHECK(recipient_info.public_key.has_value());
+ password_manager::PublicKey public_key;
+ public_key.key = recipient_info.public_key.value().value;
+ public_key.key_version = recipient_info.public_key.value().version;
+ password_sender_service->SendPasswords(
+ corresponding_credentials, {.user_id = recipient_info.user_id,
+ .public_key = std::move(public_key)});
+ }
+}
+
void PasswordsPrivateDelegateImpl::ImportPasswords(
api::passwords_private::PasswordStoreSet to_store,
ImportResultsCallback results_callback,
@@ -742,10 +803,6 @@ void PasswordsPrivateDelegateImpl::ExportPasswords(
std::move(accepted_callback), web_contents));
}
-void PasswordsPrivateDelegateImpl::CancelExportPasswords() {
- password_manager_porter_->CancelExport();
-}
-
api::passwords_private::ExportProgressStatus
PasswordsPrivateDelegateImpl::GetExportProgressStatus() {
return ConvertStatus(password_manager_porter_->GetExportProgressStatus());
@@ -795,20 +852,11 @@ bool PasswordsPrivateDelegateImpl::UnmuteInsecureCredential(
return password_check_delegate_.UnmuteInsecureCredential(credential);
}
-void PasswordsPrivateDelegateImpl::RecordChangePasswordFlowStarted(
- const api::passwords_private::PasswordUiEntry& credential) {
- password_check_delegate_.RecordChangePasswordFlowStarted(credential);
-}
-
void PasswordsPrivateDelegateImpl::StartPasswordCheck(
StartPasswordCheckCallback callback) {
password_check_delegate_.StartPasswordCheck(std::move(callback));
}
-void PasswordsPrivateDelegateImpl::StopPasswordCheck() {
- password_check_delegate_.StopPasswordCheck();
-}
-
api::passwords_private::PasswordCheckStatus
PasswordsPrivateDelegateImpl::GetPasswordCheckStatus() {
return password_check_delegate_.GetPasswordCheckStatus();
@@ -1082,43 +1130,32 @@ api::passwords_private::PasswordUiEntry
PasswordsPrivateDelegateImpl::CreatePasswordUiEntryFromCredentialUiEntry(
CredentialUIEntry credential) {
api::passwords_private::PasswordUiEntry entry;
- if (base::FeatureList::IsEnabled(
- password_manager::features::kPasswordsGrouping)) {
- entry.affiliated_domains =
- std::vector<api::passwords_private::DomainInfo>();
- base::ranges::transform(
- credential.GetAffiliatedDomains(),
- std::back_inserter(entry.affiliated_domains.value()),
- [](const CredentialUIEntry::DomainInfo& domain) {
- api::passwords_private::DomainInfo domainInfo;
- domainInfo.name = domain.name;
- domainInfo.url = domain.url.spec();
- domainInfo.signon_realm = domain.signon_realm;
- return domainInfo;
- });
- }
+ base::ranges::transform(credential.GetAffiliatedDomains(),
+ std::back_inserter(entry.affiliated_domains),
+ [](const CredentialUIEntry::DomainInfo& domain) {
+ api::passwords_private::DomainInfo domain_info;
+ domain_info.name = domain.name;
+ domain_info.url = domain.url.spec();
+ domain_info.signon_realm = domain.signon_realm;
+ return domain_info;
+ });
entry.is_passkey = !credential.passkey_credential_id.empty();
- entry.urls = extensions::CreateUrlCollectionFromCredential(credential);
entry.username = base::UTF16ToUTF8(credential.username);
if (entry.is_passkey) {
entry.display_name = base::UTF16ToUTF8(credential.user_display_name);
}
entry.stored_in = extensions::StoreSetFromCredential(credential);
- entry.is_android_credential = password_manager::IsValidAndroidFacetURI(
- credential.GetFirstSignonRealm());
if (!credential.federation_origin.opaque()) {
std::u16string formatted_origin =
url_formatter::FormatOriginForSecurityDisplay(
credential.federation_origin,
url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
- if (base::FeatureList::IsEnabled(
- password_manager::features::kPasswordsGrouping)) {
- entry.federation_text = base::UTF16ToUTF8(formatted_origin);
- } else {
- entry.federation_text = l10n_util::GetStringFUTF8(
- IDS_PASSWORDS_VIA_FEDERATION, formatted_origin);
- }
+ entry.federation_text = base::UTF16ToUTF8(formatted_origin);
+ }
+ absl::optional<GURL> change_password_url = credential.GetChangePasswordURL();
+ if (change_password_url.has_value()) {
+ entry.change_password_url = change_password_url->spec();
}
entry.id = credential_id_generator_.GenerateId(std::move(credential));
return entry;
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
index c58c40cc112..fa365a225a6 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
@@ -28,6 +28,7 @@
#include "components/password_manager/core/browser/password_access_authenticator.h"
#include "components/password_manager/core/browser/password_account_storage_settings_watcher.h"
#include "components/password_manager/core/browser/reauth_purpose.h"
+#include "components/password_manager/core/browser/sharing/recipients_fetcher.h"
#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
#include "components/password_manager/core/browser/ui/export_progress_status.h"
#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
@@ -44,6 +45,10 @@ namespace web_app {
class WebAppInstallManager;
}
+namespace password_manager {
+class RecipientsFetcher;
+}
+
namespace extensions {
// Concrete PasswordsPrivateDelegate implementation.
@@ -71,9 +76,6 @@ class PasswordsPrivateDelegateImpl
const std::u16string& note,
bool use_account_store,
content::WebContents* web_contents) override;
- absl::optional<int> ChangeSavedPassword(
- int id,
- const api::passwords_private::ChangeSavedPasswordParams& params) override;
bool ChangeCredential(
const api::passwords_private::PasswordUiEntry& credential) override;
void RemoveCredential(
@@ -90,6 +92,8 @@ class PasswordsPrivateDelegateImpl
content::WebContents* web_contents) override;
void MovePasswordsToAccount(const std::vector<int>& ids,
content::WebContents* web_contents) override;
+ void FetchFamilyMembers(FetchFamilyResultsCallback callback) override;
+ void SharePassword(int id, const ShareRecipients& recipients) override;
void ImportPasswords(api::passwords_private::PasswordStoreSet to_store,
ImportResultsCallback results_callback,
content::WebContents* web_contents) override;
@@ -100,7 +104,6 @@ class PasswordsPrivateDelegateImpl
void ExportPasswords(
base::OnceCallback<void(const std::string&)> accepted_callback,
content::WebContents* web_contents) override;
- void CancelExportPasswords() override;
api::passwords_private::ExportProgressStatus GetExportProgressStatus()
override;
bool IsOptedInForAccountStorage() override;
@@ -115,10 +118,7 @@ class PasswordsPrivateDelegateImpl
const api::passwords_private::PasswordUiEntry& credential) override;
bool UnmuteInsecureCredential(
const api::passwords_private::PasswordUiEntry& credential) override;
- void RecordChangePasswordFlowStarted(
- const api::passwords_private::PasswordUiEntry& credential) override;
void StartPasswordCheck(StartPasswordCheckCallback callback) override;
- void StopPasswordCheck() override;
api::passwords_private::PasswordCheckStatus GetPasswordCheckStatus() override;
password_manager::InsecureCredentialsManager* GetInsecureCredentialsManager()
override;
@@ -147,6 +147,14 @@ class PasswordsPrivateDelegateImpl
std::unique_ptr<PasswordManagerPorterInterface> porter) {
password_manager_porter_ = std::move(porter);
}
+
+ void SetRecipientsFetcherForTesting(
+ std::unique_ptr<password_manager::RecipientsFetcher>
+ sharing_password_recipients_fetcher) {
+ sharing_password_recipients_fetcher_ =
+ std::move(sharing_password_recipients_fetcher);
+ }
+
#endif // defined(UNIT_TEST)
private:
@@ -215,6 +223,11 @@ class PasswordsPrivateDelegateImpl
password_manager::PasswordAccessAuthenticator::AuthResultCallback
callback);
+ void OnFetchingFamilyMembersCompleted(
+ FetchFamilyResultsCallback callback,
+ std::vector<password_manager::RecipientInfo> recipients_info,
+ password_manager::FetchFamilyMembersRequestStatus request_status);
+
// Records user action and emits histogram values for retrieving |entry|.
void EmitHistogramsForCredentialAccess(
const password_manager::CredentialUIEntry& entry,
@@ -284,6 +297,9 @@ class PasswordsPrivateDelegateImpl
web_app::WebAppInstallManagerObserver>
install_manager_observation_{this};
+ std::unique_ptr<password_manager::RecipientsFetcher>
+ sharing_password_recipients_fetcher_;
+
base::WeakPtrFactory<PasswordsPrivateDelegateImpl> weak_ptr_factory_{this};
};
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
index 50bb05d5bd1..424682ae852 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
@@ -10,7 +10,9 @@
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
+#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
+#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
@@ -28,7 +30,9 @@
#include "chrome/browser/password_manager/affiliation_service_factory.h"
#include "chrome/browser/password_manager/chrome_password_manager_client.h"
#include "chrome/browser/password_manager/password_manager_test_util.h"
+#include "chrome/browser/password_manager/password_sender_service_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
+#include "chrome/browser/ui/autofill/chrome_autofill_client.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
@@ -48,6 +52,7 @@
#include "components/keyed_service/core/keyed_service.h"
#include "components/password_manager/content/browser/password_manager_log_router_factory.h"
#include "components/password_manager/core/browser/affiliation/fake_affiliation_service.h"
+#include "components/password_manager/core/browser/features/password_features.h"
#include "components/password_manager/core/browser/insecure_credentials_table.h"
#include "components/password_manager/core/browser/mock_password_feature_manager.h"
#include "components/password_manager/core/browser/password_form.h"
@@ -55,11 +60,16 @@
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/reauth_purpose.h"
+#include "components/password_manager/core/browser/sharing/mock_password_sender_service.h"
+#include "components/password_manager/core/browser/sharing/password_sharing_recipients_downloader.h"
+#include "components/password_manager/core/browser/sharing/recipients_fetcher_impl.h"
#include "components/password_manager/core/browser/test_password_store.h"
#include "components/password_manager/core/browser/ui/import_results.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/signin/public/base/signin_metrics.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/sync/base/features.h"
+#include "components/sync/protocol/password_sharing_recipients.pb.h"
#include "components/sync/test/test_sync_service.h"
#include "components/webauthn/core/browser/test_passkey_model.h"
#include "content/public/browser/browser_context.h"
@@ -80,12 +90,21 @@
using MockReauthCallback = base::MockCallback<
password_manager::PasswordAccessAuthenticator::ReauthCallback>;
+using extensions::api::passwords_private::FamilyFetchResults;
+using extensions::api::passwords_private::RecipientInfo;
+using password_manager::PasswordForm;
+using password_manager::PasswordRecipient;
using password_manager::ReauthPurpose;
using password_manager::TestPasswordStore;
using ::testing::_;
+using ::testing::AllOf;
+using ::testing::ElementsAre;
using ::testing::Eq;
+using ::testing::Field;
+using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::Ne;
+using ::testing::Optional;
using ::testing::Return;
using ::testing::SizeIs;
using ::testing::StrictMock;
@@ -94,6 +113,16 @@ namespace extensions {
namespace {
constexpr char kHistogramName[] = "PasswordManager.AccessPasswordInSettings";
+constexpr char kSharingRecipientId1[] = "user id 1";
+constexpr char kSharingRecipientKeyValue1[] = "key 1";
+constexpr char kSharingRecipientKeyValue2[] = "key 2";
+constexpr char kSharingRecipientId2[] = "user id 2";
+constexpr char kSharingRecipientDisplayName1[] = "User One";
+constexpr char kSharingRecipientDisplayName2[] = "User Two";
+constexpr char kSharingRecipientEmail1[] = "user1@example.com";
+constexpr char kSharingRecipientEmail2[] = "user2@example.com";
+constexpr char kSharingRecipientProfileImageUrl1[] = "image1.example.com";
+constexpr char kSharingRecipientProfileImageUrl2[] = "image2.example.com";
using MockPlaintextPasswordCallback =
base::MockCallback<PasswordsPrivateDelegate::PlaintextPasswordCallback>;
@@ -111,7 +140,7 @@ class MockPasswordManagerPorter : public PasswordManagerPorterInterface {
MOCK_METHOD(void,
Import,
(content::WebContents * web_contents,
- password_manager::PasswordForm::Store to_store,
+ PasswordForm::Store to_store,
ImportResultsCallback results_callback),
(override));
MOCK_METHOD(void,
@@ -133,7 +162,7 @@ class FakePasswordManagerPorter : public PasswordManagerPorterInterface {
}
void Import(content::WebContents* web_contents,
- password_manager::PasswordForm::Store to_store,
+ PasswordForm::Store to_store,
ImportResultsCallback results_callback) override {
password_manager::ImportResults results;
results.status = import_results_status_;
@@ -199,7 +228,7 @@ class MockPasswordManagerClient : public ChromePasswordManagerClient {
private:
explicit MockPasswordManagerClient(content::WebContents* web_contents)
- : ChromePasswordManagerClient(web_contents, nullptr) {}
+ : ChromePasswordManagerClient(web_contents) {}
password_manager::MockPasswordFeatureManager mock_password_feature_manager_;
scoped_refptr<device_reauth::MockDeviceAuthenticator>
@@ -289,11 +318,10 @@ std::unique_ptr<KeyedService> BuildPasswordsPrivateEventRouter(
PasswordsPrivateEventRouter::Create(context));
}
-password_manager::PasswordForm CreateSampleForm(
- password_manager::PasswordForm::Store store =
- password_manager::PasswordForm::Store::kProfileStore,
+PasswordForm CreateSampleForm(
+ PasswordForm::Store store = PasswordForm::Store::kProfileStore,
const std::u16string& username = u"test@gmail.com") {
- password_manager::PasswordForm form;
+ PasswordForm form;
form.signon_realm = "https://abc1.com";
form.url = GURL("https://abc1.com");
form.username_value = username;
@@ -315,19 +343,19 @@ sync_pb::WebauthnCredentialSpecifics CreatePasskey() {
MATCHER_P(PasswordUiEntryDataEquals, expected, "") {
return testing::Value(expected.get().is_passkey, arg.is_passkey) &&
- testing::Value(expected.get().urls.link, arg.urls.link) &&
+ testing::Value(expected.get().affiliated_domains[0].signon_realm,
+ arg.affiliated_domains[0].signon_realm) &&
testing::Value(expected.get().username, arg.username) &&
testing::Value(expected.get().display_name, arg.display_name) &&
- testing::Value(expected.get().stored_in, arg.stored_in) &&
- testing::Value(expected.get().is_android_credential,
- arg.is_android_credential);
+ testing::Value(expected.get().stored_in, arg.stored_in);
}
} // namespace
class PasswordsPrivateDelegateImplTest : public WebAppTest {
public:
- PasswordsPrivateDelegateImplTest() = default;
+ PasswordsPrivateDelegateImplTest()
+ : WebAppTest(WebAppTest::WithTestUrlLoaderFactory()) {}
PasswordsPrivateDelegateImplTest(const PasswordsPrivateDelegateImplTest&) =
delete;
@@ -339,7 +367,7 @@ class PasswordsPrivateDelegateImplTest : public WebAppTest {
void SetUp() override;
// Sets up a testing password store and fills it with |forms|.
- void SetUpPasswordStores(std::vector<password_manager::PasswordForm> forms);
+ void SetUpPasswordStores(std::vector<PasswordForm> forms);
// Sets up a testing EventRouter with a production
// PasswordsPrivateEventRouter.
@@ -355,6 +383,16 @@ class PasswordsPrivateDelegateImplTest : public WebAppTest {
PasswordsPrivateDelegate::UiEntries GetCredentials(
PasswordsPrivateDelegate& delegate);
+ // Returns a test `WebContents` with an initialized Autofill client, which is
+ // needed for PasswordManager client to work properly.
+ std::unique_ptr<content::WebContents> CreateWebContents() {
+ std::unique_ptr<content::WebContents> web_contents =
+ content::WebContentsTester::CreateTestWebContents(profile(),
+ /*instance=*/nullptr);
+ autofill::ChromeAutofillClient::CreateForWebContents(web_contents.get());
+ return web_contents;
+ }
+
protected:
raw_ptr<extensions::TestEventRouter, DanglingUntriaged> event_router_ =
nullptr;
@@ -391,11 +429,17 @@ void PasswordsPrivateDelegateImplTest::SetUp() {
[](content::BrowserContext*) -> std::unique_ptr<KeyedService> {
return std::make_unique<webauthn::TestPasskeyModel>();
}));
+
+ PasswordSenderServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+ profile(), base::BindRepeating([](content::BrowserContext*)
+ -> std::unique_ptr<KeyedService> {
+ return std::make_unique<password_manager::MockPasswordSenderService>();
+ }));
}
void PasswordsPrivateDelegateImplTest::SetUpPasswordStores(
- std::vector<password_manager::PasswordForm> forms) {
- for (const password_manager::PasswordForm& form : forms) {
+ std::vector<PasswordForm> forms) {
+ for (const PasswordForm& form : forms) {
if (form.IsUsingAccountStore())
account_store_->AddLogin(form);
else if (form.IsUsingProfileStore())
@@ -450,10 +494,10 @@ TEST_F(PasswordsPrivateDelegateImplTest,
PasswordsDuplicatedInStoresAreRepresentedAsSingleEntity) {
auto delegate = CreateDelegate();
- password_manager::PasswordForm account_password =
- CreateSampleForm(password_manager::PasswordForm::Store::kAccountStore);
- password_manager::PasswordForm profile_password =
- CreateSampleForm(password_manager::PasswordForm::Store::kProfileStore);
+ PasswordForm account_password =
+ CreateSampleForm(PasswordForm::Store::kAccountStore);
+ PasswordForm profile_password =
+ CreateSampleForm(PasswordForm::Store::kProfileStore);
SetUpPasswordStores({account_password, profile_password});
@@ -485,16 +529,14 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetPasswordExceptionsList) {
TEST_F(PasswordsPrivateDelegateImplTest,
ExceptionsDuplicatedInStoresAreRepresentedAsSingleEntity) {
auto delegate = CreateDelegate();
- password_manager::PasswordForm account_exception;
+ PasswordForm account_exception;
account_exception.blocked_by_user = true;
account_exception.url = GURL("https://test.com");
- account_exception.in_store =
- password_manager::PasswordForm::Store::kAccountStore;
- password_manager::PasswordForm profile_exception;
+ account_exception.in_store = PasswordForm::Store::kAccountStore;
+ PasswordForm profile_exception;
profile_exception.url = GURL("https://test.com");
profile_exception.blocked_by_user = true;
- profile_exception.in_store =
- password_manager::PasswordForm::Store::kProfileStore;
+ profile_exception.in_store = PasswordForm::Store::kProfileStore;
SetUpPasswordStores({account_exception, profile_exception});
@@ -506,8 +548,7 @@ TEST_F(PasswordsPrivateDelegateImplTest,
}
TEST_F(PasswordsPrivateDelegateImplTest, AddPassword) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage)
@@ -537,13 +578,17 @@ TEST_F(PasswordsPrivateDelegateImplTest, AddPassword) {
// Check that adding passwords got reflected in the passwords list.
api::passwords_private::PasswordUiEntry expected_entry1;
- expected_entry1.urls.link = "https://example1.com/";
+ expected_entry1.affiliated_domains.emplace_back();
+ expected_entry1.affiliated_domains.back().signon_realm =
+ "https://example1.com/";
expected_entry1.username = "username1";
expected_entry1.note.emplace();
expected_entry1.stored_in =
api::passwords_private::PASSWORD_STORE_SET_ACCOUNT;
api::passwords_private::PasswordUiEntry expected_entry2;
- expected_entry2.urls.link = "http://example2.com/login";
+ expected_entry2.affiliated_domains.emplace_back();
+ expected_entry2.affiliated_domains.back().signon_realm =
+ "http://example2.com/";
expected_entry2.username = "";
expected_entry2.note = "note";
expected_entry2.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
@@ -555,8 +600,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, AddPassword) {
}
TEST_F(PasswordsPrivateDelegateImplTest, AddPasswordUpdatesDefaultStore) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
auto delegate = CreateDelegate();
@@ -574,8 +618,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, AddPasswordUpdatesDefaultStore) {
ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage)
.WillByDefault(Return(true));
EXPECT_CALL(*(client->GetPasswordFeatureManager()),
- SetDefaultPasswordStore(
- password_manager::PasswordForm::Store::kAccountStore));
+ SetDefaultPasswordStore(PasswordForm::Store::kAccountStore));
EXPECT_TRUE(
delegate->AddPassword("example2.com", u"username2", u"password2", u"",
/*use_account_store=*/true, web_contents.get()));
@@ -590,8 +633,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, AddPasswordUpdatesDefaultStore) {
TEST_F(PasswordsPrivateDelegateImplTest,
ImportPasswordsDoesNotUpdateDefaultStore) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
auto delegate = CreateDelegate();
@@ -613,8 +655,7 @@ TEST_F(PasswordsPrivateDelegateImplTest,
}
TEST_F(PasswordsPrivateDelegateImplTest, ImportPasswordsUpdatesDefaultStore) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
auto delegate = CreateDelegate();
@@ -628,8 +669,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, ImportPasswordsUpdatesDefaultStore) {
ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage)
.WillByDefault(Return(true));
EXPECT_CALL(*(client->GetPasswordFeatureManager()),
- SetDefaultPasswordStore(
- password_manager::PasswordForm::Store::kAccountStore));
+ SetDefaultPasswordStore(PasswordForm::Store::kAccountStore));
EXPECT_CALL(*mock_porter_ptr, Import).Times(1);
delegate->ImportPasswords(
api::passwords_private::PasswordStoreSet::PASSWORD_STORE_SET_ACCOUNT,
@@ -638,9 +678,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, ImportPasswordsUpdatesDefaultStore) {
TEST_F(PasswordsPrivateDelegateImplTest,
ImportPasswordsLogsImportResultsStatus) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(),
- /*instance=*/nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
auto delegate = CreateDelegate();
@@ -672,9 +710,7 @@ TEST_F(PasswordsPrivateDelegateImplTest,
}
TEST_F(PasswordsPrivateDelegateImplTest, TestReauthFailedOnImport) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(),
- /*instance=*/nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
@@ -717,9 +753,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestReauthFailedOnImport) {
TEST_F(PasswordsPrivateDelegateImplTest,
ContinueImportLogsImportResultsStatus) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(),
- /*instance=*/nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
scoped_refptr<PasswordsPrivateDelegateImpl> delegate = CreateDelegate();
@@ -760,120 +794,8 @@ TEST_F(PasswordsPrivateDelegateImplTest, ResetImporter) {
delegate->ResetImporter(/*delete_file=*/false);
}
-TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPassword) {
- password_manager::PasswordForm sample_form = CreateSampleForm();
- SetUpPasswordStores({sample_form});
- auto delegate = CreateDelegate();
- // Spin the loop to allow PasswordStore tasks posted on the creation of
- // |delegate| to be completed.
- base::RunLoop().RunUntilIdle();
-
- // Double check that the contents of the passwords list matches our
- // expectation.
- base::MockCallback<PasswordsPrivateDelegate::UiEntriesCallback> callback;
- EXPECT_CALL(callback, Run(SizeIs(1)))
- .WillOnce([&](const PasswordsPrivateDelegate::UiEntries& passwords) {
- EXPECT_EQ(sample_form.username_value,
- base::UTF8ToUTF16(passwords[0].username));
- });
- delegate->GetSavedPasswordsList(callback.Get());
- int sample_form_id = delegate->GetIdForCredential(
- password_manager::CredentialUIEntry(sample_form));
-
- api::passwords_private::ChangeSavedPasswordParams params;
- params.password = "new_pass";
- params.username = "new_user";
- params.note = "new note";
-
- sample_form.username_value = u"new_user";
- sample_form.password_value = u"new_pass";
- int new_form_id = delegate->GetIdForCredential(
- password_manager::CredentialUIEntry(sample_form));
-
- auto result = delegate->ChangeSavedPassword(sample_form_id, params);
- EXPECT_EQ(result, new_form_id);
-
- // Spin the loop to allow PasswordStore tasks posted when changing the
- // password to be completed.
- base::RunLoop().RunUntilIdle();
-
- // Check that the changing the password got reflected in the passwords list.
- // `note` field should not be filled when `GetSavedPasswordsList` is called.
- EXPECT_CALL(callback, Run(SizeIs(1)))
- .WillOnce([](const PasswordsPrivateDelegate::UiEntries& passwords) {
- EXPECT_THAT(passwords[0].username, Eq("new_user"));
- EXPECT_THAT(passwords[0].note, Eq(absl::nullopt));
- });
- delegate->GetSavedPasswordsList(callback.Get());
-}
-
-TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPasswordInBothStores) {
- password_manager::PasswordForm profile_form = CreateSampleForm();
- password_manager::PasswordForm account_form = profile_form;
- account_form.in_store = password_manager::PasswordForm::Store::kAccountStore;
- SetUpPasswordStores({profile_form, account_form});
-
- auto delegate = CreateDelegate();
- // Spin the loop to allow PasswordStore tasks posted on the creation of
- // |delegate| to be completed.
- base::RunLoop().RunUntilIdle();
-
- int profile_form_id = delegate->GetIdForCredential(
- password_manager::CredentialUIEntry(profile_form));
- int account_form_id = delegate->GetIdForCredential(
- password_manager::CredentialUIEntry(account_form));
-
- ASSERT_EQ(profile_form_id, account_form_id);
-
- api::passwords_private::ChangeSavedPasswordParams params;
- params.password = "new_pass";
- params.username = "new_user";
-
- profile_form.username_value = u"new_user";
- profile_form.password_value = u"new_pass";
- int new_profile_form_id = delegate->GetIdForCredential(
- password_manager::CredentialUIEntry(profile_form));
- account_form.username_value = u"new_user";
- account_form.password_value = u"new_pass";
- int new_account_form_id = delegate->GetIdForCredential(
- password_manager::CredentialUIEntry(account_form));
-
- ASSERT_EQ(new_profile_form_id, new_account_form_id);
-
- EXPECT_EQ(new_profile_form_id,
- delegate->ChangeSavedPassword(profile_form_id, params));
-}
-
-TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPasswordInAccountStore) {
- password_manager::PasswordForm profile_form = CreateSampleForm();
- profile_form.password_value = u"different_pass";
- password_manager::PasswordForm account_form = CreateSampleForm();
- account_form.in_store = password_manager::PasswordForm::Store::kAccountStore;
- SetUpPasswordStores({profile_form, account_form});
-
- auto delegate = CreateDelegate();
- // Spin the loop to allow PasswordStore tasks posted on the creation of
- // |delegate| to be completed.
- base::RunLoop().RunUntilIdle();
-
- int account_form_id = delegate->GetIdForCredential(
- password_manager::CredentialUIEntry(account_form));
-
- api::passwords_private::ChangeSavedPasswordParams params;
- params.password = "new_pass";
- params.username = "new_user";
-
- account_form.username_value = u"new_user";
- account_form.password_value = u"new_pass";
- int new_account_form_id = delegate->GetIdForCredential(
- password_manager::CredentialUIEntry(account_form));
-
- auto result = delegate->ChangeSavedPassword(account_form_id, params);
- EXPECT_THAT(result, new_account_form_id);
-}
-
TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_Password) {
- password_manager::PasswordForm sample_form = CreateSampleForm();
+ PasswordForm sample_form = CreateSampleForm();
SetUpPasswordStores({sample_form});
auto delegate = CreateDelegate();
// Spin the loop to allow PasswordStore tasks posted on the creation of
@@ -904,9 +826,9 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_Password) {
TEST_F(PasswordsPrivateDelegateImplTest,
ChangeCredential_PasswordInBothStores) {
- password_manager::PasswordForm profile_form = CreateSampleForm();
- password_manager::PasswordForm account_form = profile_form;
- account_form.in_store = password_manager::PasswordForm::Store::kAccountStore;
+ PasswordForm profile_form = CreateSampleForm();
+ PasswordForm account_form = profile_form;
+ account_form.in_store = PasswordForm::Store::kAccountStore;
SetUpPasswordStores({profile_form, account_form});
auto delegate = CreateDelegate();
@@ -938,10 +860,10 @@ TEST_F(PasswordsPrivateDelegateImplTest,
TEST_F(PasswordsPrivateDelegateImplTest,
ChangeCredential_PasswordInAccountStore) {
- password_manager::PasswordForm profile_form = CreateSampleForm();
+ PasswordForm profile_form = CreateSampleForm();
profile_form.password_value = u"different_pass";
- password_manager::PasswordForm account_form = CreateSampleForm();
- account_form.in_store = password_manager::PasswordForm::Store::kAccountStore;
+ PasswordForm account_form = CreateSampleForm();
+ account_form.in_store = PasswordForm::Store::kAccountStore;
SetUpPasswordStores({profile_form, account_form});
auto delegate = CreateDelegate();
@@ -988,8 +910,7 @@ TEST_F(PasswordsPrivateDelegateImplTest,
TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_Passkey) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
- {password_manager::features::kPasswordsGrouping,
- password_manager::features::kPasswordManagerPasskeys,
+ {password_manager::features::kPasswordManagerPasskeys,
syncer::kSyncWebauthnCredentials},
/*disabled_features=*/{});
@@ -1042,7 +963,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_NotFound) {
}
TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_EmptyPassword) {
- password_manager::PasswordForm sample_form = CreateSampleForm();
+ PasswordForm sample_form = CreateSampleForm();
SetUpPasswordStores({sample_form});
auto delegate = CreateDelegate();
// Spin the loop to allow PasswordStore tasks posted on the creation of
@@ -1060,7 +981,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_EmptyPassword) {
// Checking callback result of RequestPlaintextPassword with reason Copy.
// By implementation for Copy, callback will receive empty string.
TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResult) {
- password_manager::PasswordForm form = CreateSampleForm();
+ PasswordForm form = CreateSampleForm();
SetUpPasswordStores({form});
auto delegate = CreateDelegate();
@@ -1091,8 +1012,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResult) {
}
TEST_F(PasswordsPrivateDelegateImplTest, TestShouldReauthForOptIn) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage)
@@ -1108,8 +1028,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestShouldReauthForOptIn) {
TEST_F(PasswordsPrivateDelegateImplTest,
TestShouldNotReauthForOptOutAndShouldSetPref) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
password_manager::MockPasswordFeatureManager* feature_manager =
@@ -1188,7 +1107,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestPassedReauthOnView) {
TEST_F(PasswordsPrivateDelegateImplTest,
TestPassedReauthOnRequestCredentialsDetails) {
- password_manager::PasswordForm sample_form = CreateSampleForm();
+ PasswordForm sample_form = CreateSampleForm();
sample_form.notes.emplace_back(u"best note ever",
/*date_created=*/base::Time::Now());
SetUpPasswordStores({sample_form});
@@ -1347,8 +1266,7 @@ TEST_F(PasswordsPrivateDelegateImplTest,
}
TEST_F(PasswordsPrivateDelegateImplTest, IsAccountStoreDefault) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage)
@@ -1357,26 +1275,24 @@ TEST_F(PasswordsPrivateDelegateImplTest, IsAccountStoreDefault) {
auto delegate = CreateDelegate();
EXPECT_CALL(*(client->GetPasswordFeatureManager()), GetDefaultPasswordStore)
- .WillOnce(Return(password_manager::PasswordForm::Store::kAccountStore));
+ .WillOnce(Return(PasswordForm::Store::kAccountStore));
EXPECT_TRUE(delegate->IsAccountStoreDefault(web_contents.get()));
EXPECT_CALL(*(client->GetPasswordFeatureManager()), GetDefaultPasswordStore)
- .WillOnce(Return(password_manager::PasswordForm::Store::kProfileStore));
+ .WillOnce(Return(PasswordForm::Store::kProfileStore));
EXPECT_FALSE(delegate->IsAccountStoreDefault(web_contents.get()));
}
TEST_F(PasswordsPrivateDelegateImplTest, TestMovePasswordsToAccountStore) {
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage)
.WillByDefault(Return(true));
auto delegate = CreateDelegate();
- password_manager::PasswordForm form1 =
- CreateSampleForm(password_manager::PasswordForm::Store::kProfileStore);
- password_manager::PasswordForm form2 = form1;
+ PasswordForm form1 = CreateSampleForm(PasswordForm::Store::kProfileStore);
+ PasswordForm form2 = form1;
form2.username_value = u"different_username";
SetUpPasswordStores({form1, form2});
@@ -1396,28 +1312,6 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestMovePasswordsToAccountStore) {
1);
}
-TEST_F(PasswordsPrivateDelegateImplTest, AndroidCredential) {
- auto delegate = CreateDelegate();
-
- password_manager::PasswordForm android_form;
- android_form.signon_realm = "android://hash@example.com";
- android_form.username_value = u"test@gmail.com";
- android_form.in_store = password_manager::PasswordForm::Store::kProfileStore;
- SetUpPasswordStores({android_form});
-
- base::MockCallback<PasswordsPrivateDelegate::UiEntriesCallback> callback;
-
- api::passwords_private::PasswordUiEntry expected_entry;
- expected_entry.urls.link =
- "https://play.google.com/store/apps/details?id=example.com";
- expected_entry.username = "test@gmail.com";
- expected_entry.is_android_credential = true;
- expected_entry.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
- EXPECT_CALL(callback, Run(testing::ElementsAre(PasswordUiEntryDataEquals(
- testing::ByRef(expected_entry)))));
- delegate->GetSavedPasswordsList(callback.Get());
-}
-
TEST_F(PasswordsPrivateDelegateImplTest, VerifyCastingOfImportEntryStatus) {
static_assert(
static_cast<int>(api::passwords_private::ImportEntryStatus::
@@ -1552,8 +1446,7 @@ TEST_F(PasswordsPrivateDelegateImplTest,
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
password_manager::features::kBiometricAuthenticationForFilling);
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
client->SetDeviceAuthenticator(biometric_authenticator_);
@@ -1574,8 +1467,7 @@ TEST_F(PasswordsPrivateDelegateImplTest,
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
password_manager::features::kBiometricAuthenticationForFilling);
- std::unique_ptr<content::WebContents> web_contents =
- content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+ std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
auto* client =
MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get());
client->SetDeviceAuthenticator(biometric_authenticator_);
@@ -1641,16 +1533,12 @@ TEST_F(PasswordsPrivateDelegateImplTest, DISABLED_ShowAddShortcutDialog) {
}
TEST_F(PasswordsPrivateDelegateImplTest, GetCredentialGroups) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- password_manager::features::kPasswordsGrouping);
-
auto delegate = CreateDelegate();
- password_manager::PasswordForm password1 = CreateSampleForm(
- password_manager::PasswordForm::Store::kProfileStore, u"username1");
- password_manager::PasswordForm password2 = CreateSampleForm(
- password_manager::PasswordForm::Store::kProfileStore, u"username2");
+ PasswordForm password1 =
+ CreateSampleForm(PasswordForm::Store::kProfileStore, u"username1");
+ PasswordForm password2 =
+ CreateSampleForm(PasswordForm::Store::kProfileStore, u"username2");
SetUpPasswordStores({password1, password2});
@@ -1661,11 +1549,13 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetCredentialGroups) {
EXPECT_EQ("https://abc1.com/favicon.ico", groups[0].icon_url);
api::passwords_private::PasswordUiEntry expected_entry1;
- expected_entry1.urls.link = "https://abc1.com/";
+ expected_entry1.affiliated_domains.emplace_back();
+ expected_entry1.affiliated_domains.back().signon_realm = "https://abc1.com";
expected_entry1.username = "username1";
expected_entry1.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
api::passwords_private::PasswordUiEntry expected_entry2;
- expected_entry2.urls.link = "https://abc1.com/";
+ expected_entry2.affiliated_domains.emplace_back();
+ expected_entry2.affiliated_domains.back().signon_realm = "https://abc1.com";
expected_entry2.username = "username2";
expected_entry2.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
EXPECT_THAT(groups[0].entries,
@@ -1693,8 +1583,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, PasswordManagerAppInstalled) {
TEST_F(PasswordsPrivateDelegateImplTest, GetPasskeyInGroups) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
- {password_manager::features::kPasswordsGrouping,
- password_manager::features::kPasswordManagerPasskeys,
+ {password_manager::features::kPasswordManagerPasskeys,
syncer::kSyncWebauthnCredentials},
/*disabled_features=*/{});
@@ -1707,8 +1596,8 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetPasskeyInGroups) {
sync_pb::WebauthnCredentialSpecifics passkey = CreatePasskey();
passkey_model->AddNewPasskeyForTesting(passkey);
- password_manager::PasswordForm password = CreateSampleForm(
- password_manager::PasswordForm::Store::kProfileStore, u"username1");
+ PasswordForm password =
+ CreateSampleForm(PasswordForm::Store::kProfileStore, u"username1");
SetUpPasswordStores({password});
auto groups = delegate->GetCredentialGroups();
@@ -1718,12 +1607,14 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetPasskeyInGroups) {
EXPECT_EQ("https://abc1.com/favicon.ico", groups[0].icon_url);
api::passwords_private::PasswordUiEntry expected_entry1;
- expected_entry1.urls.link = "https://abc1.com/";
+ expected_entry1.affiliated_domains.emplace_back();
+ expected_entry1.affiliated_domains.back().signon_realm = "https://abc1.com";
expected_entry1.username = "username1";
expected_entry1.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
api::passwords_private::PasswordUiEntry expected_entry2;
expected_entry2.is_passkey = true;
- expected_entry2.urls.link = "https://abc1.com/";
+ expected_entry2.affiliated_domains.emplace_back();
+ expected_entry2.affiliated_domains.back().signon_realm = "https://abc1.com";
expected_entry2.username = passkey.user_name();
expected_entry2.display_name = passkey.user_display_name();
expected_entry2.stored_in =
@@ -1738,8 +1629,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, RemovePasskey) {
base::UserActionTester user_action_tester;
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
- {password_manager::features::kPasswordsGrouping,
- password_manager::features::kPasswordManagerPasskeys,
+ {password_manager::features::kPasswordManagerPasskeys,
syncer::kSyncWebauthnCredentials},
/*disabled_features=*/{});
@@ -1774,4 +1664,352 @@ TEST_F(PasswordsPrivateDelegateImplTest, RemovePasskey) {
1);
}
+TEST_F(PasswordsPrivateDelegateImplTest, SharePasswordWithTwoRecipients) {
+ auto delegate = CreateDelegate();
+ PasswordForm password = CreateSampleForm();
+ SetUpPasswordStores({password});
+
+ PasswordsPrivateDelegate::ShareRecipients recipients;
+ api::passwords_private::RecipientInfo recipient1;
+ api::passwords_private::PublicKey public_key1;
+ public_key1.value = kSharingRecipientKeyValue1;
+ recipient1.public_key = std::move(public_key1);
+ recipient1.user_id = kSharingRecipientId1;
+ recipient1.display_name = kSharingRecipientDisplayName1;
+ recipient1.email = kSharingRecipientEmail1;
+ recipient1.profile_image_url = kSharingRecipientProfileImageUrl1;
+ recipients.push_back(std::move(recipient1));
+
+ api::passwords_private::RecipientInfo recipient2;
+ api::passwords_private::PublicKey public_key2;
+ public_key2.value = kSharingRecipientKeyValue2;
+ recipient2.public_key = std::move(public_key2);
+ recipient2.user_id = kSharingRecipientId2;
+ recipient2.display_name = kSharingRecipientDisplayName2;
+ recipient2.email = kSharingRecipientEmail2;
+ recipient2.profile_image_url = kSharingRecipientProfileImageUrl2;
+ recipients.push_back(std::move(recipient2));
+
+ password_manager::MockPasswordSenderService* password_sender_service =
+ static_cast<password_manager::MockPasswordSenderService*>(
+ PasswordSenderServiceFactory::GetForProfile(profile()));
+
+ password_manager::PublicKey expected_public_key1, expected_public_key2;
+ expected_public_key1.key = kSharingRecipientKeyValue1;
+ expected_public_key2.key = kSharingRecipientKeyValue2;
+ // There are two recipients and hence, SendPasswords() should be called twice
+ // with the same credentials for each recipient.
+ EXPECT_CALL(
+ *password_sender_service,
+ SendPasswords(
+ ElementsAre(AllOf(
+ Field(&PasswordForm::username_value, password.username_value),
+ Field(&PasswordForm::password_value, password.password_value),
+ Field(&PasswordForm::signon_realm, password.signon_realm))),
+ AllOf(Field("user id", &PasswordRecipient::user_id,
+ kSharingRecipientId1),
+ Field("public key", &PasswordRecipient::public_key,
+ expected_public_key1))));
+ EXPECT_CALL(
+ *password_sender_service,
+ SendPasswords(
+ ElementsAre(AllOf(
+ Field(&PasswordForm::username_value, password.username_value),
+ Field(&PasswordForm::password_value, password.password_value),
+ Field(&PasswordForm::signon_realm, password.signon_realm))),
+ AllOf(Field("user id", &PasswordRecipient::user_id,
+ kSharingRecipientId2),
+ Field("public key", &PasswordRecipient::public_key,
+ expected_public_key2)))
+
+ );
+
+ delegate->SharePassword(/*id=*/0, recipients);
+}
+
+TEST_F(PasswordsPrivateDelegateImplTest,
+ ShareAllPasswordsRepresentedByUiEntry) {
+ auto delegate = CreateDelegate();
+ // `password1` and `password2` share the same username and password and their
+ // origins are PSL matches. They should be represented by the same ui entry.
+ // `password3` has a different username and hence is represented by a
+ // different ui entry.
+ PasswordForm password1 =
+ CreateSampleForm(PasswordForm::Store::kProfileStore, u"username1");
+ password1.signon_realm = "https://facebook.com";
+ password1.url = GURL("https://facebook.com");
+
+ PasswordForm password2 = password1;
+ password2.signon_realm = "https://m.facebook.com";
+ password2.url = GURL("https://m.facebook.com");
+
+ PasswordForm password3 =
+ CreateSampleForm(PasswordForm::Store::kProfileStore, u"username3");
+
+ SetUpPasswordStores({password1, password2, password3});
+
+ // Credentials should have been grouped in two groups.
+ PasswordsPrivateDelegate::CredentialsGroups groups =
+ delegate->GetCredentialGroups();
+ ASSERT_EQ(groups.size(), 2U);
+ // Find the id of the ui entry that represents both facebook.com and
+ // m.facebook.com
+ int id_with_two_affiliated_domains = -1;
+ for (const api::passwords_private::CredentialGroup& group : groups) {
+ for (const api::passwords_private::PasswordUiEntry& entry : group.entries) {
+ if (entry.affiliated_domains.size() == 2) {
+ id_with_two_affiliated_domains = entry.id;
+ break;
+ }
+ }
+ }
+ ASSERT_NE(-1, id_with_two_affiliated_domains);
+
+ PasswordsPrivateDelegate::ShareRecipients recipients;
+ api::passwords_private::RecipientInfo recipient;
+ api::passwords_private::PublicKey public_key;
+ public_key.value = kSharingRecipientKeyValue1;
+ recipient.public_key = std::move(public_key);
+ recipient.user_id = kSharingRecipientId1;
+ recipient.display_name = kSharingRecipientDisplayName1;
+ recipient.email = kSharingRecipientEmail1;
+ recipient.profile_image_url = kSharingRecipientProfileImageUrl1;
+ recipients.push_back(std::move(recipient));
+
+ password_manager::MockPasswordSenderService* password_sender_service =
+ static_cast<password_manager::MockPasswordSenderService*>(
+ PasswordSenderServiceFactory::GetForProfile(profile()));
+
+ password_manager::PublicKey expected_public_key;
+ expected_public_key.key = kSharingRecipientKeyValue1;
+ // There is one recipient and hence, SendPasswords() should be called only
+ // once with the two credentials represented by this ui entry.
+ EXPECT_CALL(
+ *password_sender_service,
+ SendPasswords(
+ UnorderedElementsAre(
+ Field(&PasswordForm::signon_realm, "https://facebook.com"),
+ Field(&PasswordForm::signon_realm, "https://m.facebook.com")),
+ AllOf(Field("user id", &PasswordRecipient::user_id,
+ kSharingRecipientId1),
+ Field("public key", &PasswordRecipient::public_key,
+ expected_public_key))))
+ .Times(1);
+
+ delegate->SharePassword(/*id=*/id_with_two_affiliated_domains, recipients);
+}
+
+TEST_F(PasswordsPrivateDelegateImplTest, ShareNonExistentPassword) {
+ auto delegate = CreateDelegate();
+
+ PasswordsPrivateDelegate::ShareRecipients recipients;
+ api::passwords_private::RecipientInfo recipient;
+ recipient.user_id = kSharingRecipientId1;
+ recipients.push_back(std::move(recipient));
+
+ password_manager::MockPasswordSenderService* password_sender_service =
+ static_cast<password_manager::MockPasswordSenderService*>(
+ PasswordSenderServiceFactory::GetForProfile(profile()));
+ EXPECT_CALL(*password_sender_service, SendPasswords).Times(0);
+
+ delegate->SharePassword(/*id=*/100, recipients);
+}
+
+class PasswordsPrivateDelegateImplFetchFamilyMembersTest
+ : public PasswordsPrivateDelegateImplTest {
+ public:
+ PasswordsPrivateDelegateImplFetchFamilyMembersTest() = default;
+
+ void SetUp() override {
+ PasswordsPrivateDelegateImplTest::SetUp();
+ delegate_ = CreateDelegate();
+ delegate_->SetRecipientsFetcherForTesting(
+ std::make_unique<password_manager::RecipientsFetcherImpl>(
+ version_info::Channel::DEFAULT,
+ profile_url_loader_factory().GetSafeWeakWrapper(),
+ identity_test_env_.identity_manager()));
+ identity_test_env_.MakePrimaryAccountAvailable("test@email.com",
+ signin::ConsentLevel::kSync);
+ identity_test_env_.SetAutomaticIssueOfAccessTokens(true);
+ }
+
+ void TearDown() override {
+ delegate_ = nullptr;
+ PasswordsPrivateDelegateImplTest::TearDown();
+ }
+
+ protected:
+ const std::string kTestUserId = "12345";
+ const std::string kTestUserName = "Theo Tester";
+ const std::string kTestEmail = "theo@example.com";
+ const std::string kTestProfileImageUrl =
+ "https://3837fjsdjaka.image.example.com";
+ const std::string kTestPublicKeyBase64 =
+ "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MTI=";
+ const uint32_t kTestPublicKeyVersion = 42;
+
+ void SetServerResponse(sync_pb::PasswordSharingRecipientsResponse::
+ PasswordSharingRecipientsResult result,
+ net::HttpStatusCode status = net::HTTP_OK,
+ bool recipient_has_public_key = false) {
+ sync_pb::PasswordSharingRecipientsResponse response;
+ response.set_result(result);
+ if (result == sync_pb::PasswordSharingRecipientsResponse::SUCCESS) {
+ sync_pb::UserInfo* user_info = response.add_recipients();
+ user_info->set_user_id(kTestUserId);
+ user_info->mutable_user_display_info()->set_display_name(kTestUserName);
+ user_info->mutable_user_display_info()->set_email(kTestEmail);
+ user_info->mutable_user_display_info()->set_profile_image_url(
+ kTestProfileImageUrl);
+ if (recipient_has_public_key) {
+ const password_manager::PublicKey kTestPublicKey = {
+ kTestPublicKeyBase64, kTestPublicKeyVersion};
+ user_info->mutable_cross_user_sharing_public_key()->CopyFrom(
+ kTestPublicKey.ToProto());
+ }
+ }
+ profile_url_loader_factory().AddResponse(
+ password_manager::PasswordSharingRecipientsDownloader::
+ GetPasswordSharingRecipientsURL(version_info::Channel::DEFAULT)
+ .spec(),
+ response.SerializeAsString(), status);
+ }
+
+ PasswordsPrivateDelegateImpl* delegate() { return delegate_.get(); }
+
+ private:
+ signin::IdentityTestEnvironment identity_test_env_;
+ scoped_refptr<PasswordsPrivateDelegateImpl> delegate_;
+};
+
+TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest,
+ FetchFamilyMembersSucceedsWithoutPublicKey) {
+ SetServerResponse(sync_pb::PasswordSharingRecipientsResponse::SUCCESS);
+
+ base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback>
+ callback;
+ EXPECT_CALL(
+ callback,
+ Run(AllOf(Field(&FamilyFetchResults::status,
+ api::passwords_private::FAMILY_FETCH_STATUS_SUCCESS),
+ Field(&FamilyFetchResults::family_members,
+ ElementsAre(AllOf(
+ Field(&RecipientInfo::user_id, kTestUserId),
+ Field(&RecipientInfo::display_name, kTestUserName),
+ Field(&RecipientInfo::email, kTestEmail),
+ Field(&RecipientInfo::is_eligible, false),
+ Field(&RecipientInfo::public_key, Eq(absl::nullopt)),
+ Field(&RecipientInfo::profile_image_url,
+ kTestProfileImageUrl)))))));
+
+ delegate()->FetchFamilyMembers(callback.Get());
+ task_environment()->RunUntilIdle();
+}
+
+TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest,
+ FetchFamilyMembersSucceedsWithPublicKey) {
+ SetServerResponse(sync_pb::PasswordSharingRecipientsResponse::SUCCESS,
+ net::HTTP_OK, /*recipient_has_public_key=*/true);
+
+ base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback>
+ callback;
+ EXPECT_CALL(
+ callback,
+ Run(AllOf(
+ Field(&FamilyFetchResults::status,
+ api::passwords_private::FAMILY_FETCH_STATUS_SUCCESS),
+ Field(&FamilyFetchResults::family_members,
+ ElementsAre(AllOf(
+ Field(&RecipientInfo::user_id, kTestUserId),
+ Field(&RecipientInfo::display_name, kTestUserName),
+ Field(&RecipientInfo::email, kTestEmail),
+ Field(&RecipientInfo::is_eligible, true),
+ Field(&RecipientInfo::public_key,
+ Optional(AllOf(
+ Field(&api::passwords_private::PublicKey::value,
+ kTestPublicKeyBase64),
+ Field(&api::passwords_private::PublicKey::version,
+ kTestPublicKeyVersion)))),
+ Field(&RecipientInfo::profile_image_url,
+ kTestProfileImageUrl)))))));
+
+ delegate()->FetchFamilyMembers(callback.Get());
+ task_environment()->RunUntilIdle();
+}
+
+TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest,
+ FetchFamilyMembersFailsWithUnknownError) {
+ SetServerResponse(sync_pb::PasswordSharingRecipientsResponse::UNKNOWN);
+
+ base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback>
+ callback;
+ EXPECT_CALL(
+ callback,
+ Run(AllOf(
+ Field(&FamilyFetchResults::status,
+ api::passwords_private::FAMILY_FETCH_STATUS_UNKNOWN_ERROR),
+ Field(&FamilyFetchResults::family_members, IsEmpty()))));
+
+ delegate()->FetchFamilyMembers(callback.Get());
+ task_environment()->RunUntilIdle();
+}
+
+TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest,
+ FetchFamilyMembersFailsWithNoFamilyMembersError) {
+ SetServerResponse(
+ sync_pb::PasswordSharingRecipientsResponse::NOT_FAMILY_MEMBER);
+
+ base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback>
+ callback;
+ EXPECT_CALL(
+ callback,
+ Run(AllOf(Field(&FamilyFetchResults::status,
+ api::passwords_private::FAMILY_FETCH_STATUS_NO_MEMBERS),
+ Field(&FamilyFetchResults::family_members, IsEmpty()))));
+
+ delegate()->FetchFamilyMembers(callback.Get());
+ task_environment()->RunUntilIdle();
+}
+
+TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest,
+ FetchFamilyMembersFailsWithAnotherRequestInFlight) {
+ base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback>
+ callback1;
+ delegate()->FetchFamilyMembers(callback1.Get());
+
+ base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback>
+ callback2;
+ EXPECT_CALL(
+ callback2,
+ Run(AllOf(
+ Field(&FamilyFetchResults::status,
+ api::passwords_private::FAMILY_FETCH_STATUS_UNKNOWN_ERROR),
+ Field(&FamilyFetchResults::family_members, IsEmpty()))));
+ delegate()->FetchFamilyMembers(callback2.Get());
+
+ task_environment()->RunUntilIdle();
+}
+
+TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest,
+ FetchFamilyMembersFailsWithNetworkError) {
+ profile_url_loader_factory().AddResponse(
+ password_manager::PasswordSharingRecipientsDownloader::
+ GetPasswordSharingRecipientsURL(version_info::Channel::DEFAULT)
+ .spec(),
+ /*content=*/std::string(), net::HTTP_INTERNAL_SERVER_ERROR);
+
+ base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback>
+ callback;
+ FamilyFetchResults family_fetch_results;
+ EXPECT_CALL(
+ callback,
+ Run(AllOf(
+ Field(&FamilyFetchResults::status,
+ api::passwords_private::FAMILY_FETCH_STATUS_UNKNOWN_ERROR),
+ Field(&FamilyFetchResults::family_members, IsEmpty()))));
+
+ delegate()->FetchFamilyMembers(callback.Get());
+ task_environment()->RunUntilIdle();
+}
+
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils.h
index e5dd4d1906a..c6914c2d038 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils.h
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils.h
@@ -7,7 +7,6 @@
#include <functional>
#include <string>
-#include <unordered_map>
#include "base/containers/flat_map.h"
#include "chrome/common/extensions/api/passwords_private.h"
@@ -67,10 +66,9 @@ class IdGenerator {
private:
// Maps credential key to id.
- std::unordered_map<std::string, int> key_to_id_;
+ base::flat_map<std::string, int> key_to_id_;
// Maps id to the credential.
- std::unordered_map<int, password_manager::CredentialUIEntry>
- id_to_credential_;
+ base::flat_map<int, password_manager::CredentialUIEntry> id_to_credential_;
int next_id_ = 0;
};
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc
index 4f0a24376f8..5e23d6b4fea 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc
@@ -28,7 +28,8 @@ bool IsOsReauthAllowedAsh(Profile* profile,
ash::ProfileHelper::Get()->GetUserByProfile(profile)->GetAccountId());
if (user_cannot_manually_enter_password)
return true;
-
+ // TODO (b/238606050): This code branch does not seem to be used now.
+ // Clean up the code, or add token as a parameter to this method.
ash::quick_unlock::QuickUnlockStorage* quick_unlock_storage =
ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile);
const ash::quick_unlock::AuthToken* auth_token =
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc b/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc
index 5921a127e35..3963eba8ef7 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc
@@ -26,9 +26,13 @@ constexpr size_t kNumMocks = 3;
api::passwords_private::PasswordUiEntry CreateEntry(int id) {
api::passwords_private::PasswordUiEntry entry;
- entry.urls.shown = "test" + base::NumberToString(id) + ".com";
- entry.urls.signon_realm = "http://" + entry.urls.shown + "/login";
- entry.urls.link = entry.urls.signon_realm;
+ entry.affiliated_domains.emplace_back();
+ entry.affiliated_domains.back().name =
+ "test" + base::NumberToString(id) + ".com";
+ entry.affiliated_domains.back().signon_realm =
+ "http://" + entry.affiliated_domains.back().name + "/login";
+ entry.affiliated_domains.back().url =
+ entry.affiliated_domains.back().signon_realm;
entry.username = "testName" + base::NumberToString(id);
entry.id = id;
entry.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
@@ -105,19 +109,6 @@ bool TestPasswordsPrivateDelegate::AddPassword(
return !url.empty() && !password.empty();
}
-absl::optional<int> TestPasswordsPrivateDelegate::ChangeSavedPassword(
- const int id,
- const api::passwords_private::ChangeSavedPasswordParams& params) {
- if (static_cast<size_t>(id) >= current_entries_.size()) {
- return absl::nullopt;
- }
-
- if (params.password.empty())
- return absl::nullopt;
-
- return id;
-}
-
bool TestPasswordsPrivateDelegate::ChangeCredential(
const api::passwords_private::PasswordUiEntry& credential) {
const auto existing = std::ranges::find_if(
@@ -233,6 +224,21 @@ void TestPasswordsPrivateDelegate::ContinueImport(
std::move(results_callback).Run(import_results_);
}
+void TestPasswordsPrivateDelegate::FetchFamilyMembers(
+ FetchFamilyResultsCallback callback) {
+ fetch_family_members_triggered_ = true;
+
+ family_fetch_results_.status =
+ api::passwords_private::FamilyFetchStatus::FAMILY_FETCH_STATUS_SUCCESS;
+ std::move(callback).Run(family_fetch_results_);
+}
+
+void TestPasswordsPrivateDelegate::SharePassword(
+ int id,
+ const ShareRecipients& recipients) {
+ share_password_triggered_ = true;
+}
+
void TestPasswordsPrivateDelegate::ResetImporter(bool delete_file) {
reset_importer_triggered_ = true;
}
@@ -246,10 +252,6 @@ void TestPasswordsPrivateDelegate::ExportPasswords(
std::move(callback).Run(std::string());
}
-void TestPasswordsPrivateDelegate::CancelExportPasswords() {
- cancel_export_passwords_triggered_ = true;
-}
-
api::passwords_private::ExportProgressStatus
TestPasswordsPrivateDelegate::GetExportProgressStatus() {
// The testing of password exporting itself should be handled via
@@ -272,10 +274,11 @@ std::vector<api::passwords_private::PasswordUiEntry>
TestPasswordsPrivateDelegate::GetInsecureCredentials() {
api::passwords_private::PasswordUiEntry leaked_credential;
leaked_credential.username = "alice";
- leaked_credential.urls.shown = "example.com";
- leaked_credential.urls.link = "https://example.com";
- leaked_credential.urls.signon_realm = "https://example.com";
- leaked_credential.is_android_credential = false;
+ leaked_credential.affiliated_domains.emplace_back();
+ leaked_credential.affiliated_domains.back().name = "example.com";
+ leaked_credential.affiliated_domains.back().url = "https://example.com";
+ leaked_credential.affiliated_domains.back().signon_realm =
+ "https://example.com";
leaked_credential.change_password_url = "https://example.com/change-password";
leaked_credential.compromised_info.emplace();
// Mar 03 2020 12:00:00 UTC
@@ -290,9 +293,9 @@ TestPasswordsPrivateDelegate::GetInsecureCredentials() {
api::passwords_private::PasswordUiEntry weak_credential;
weak_credential.username = "bob";
- weak_credential.urls.shown = "example.com";
- weak_credential.urls.link = "https://example.com";
- weak_credential.is_android_credential = false;
+ weak_credential.affiliated_domains.emplace_back();
+ weak_credential.affiliated_domains.back().name = "example.com";
+ weak_credential.affiliated_domains.back().url = "https://example.com";
weak_credential.change_password_url = "https://example.com/change-password";
weak_credential.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
weak_credential.compromised_info.emplace();
@@ -311,9 +314,9 @@ TestPasswordsPrivateDelegate::GetCredentialsWithReusedPassword() {
api::passwords_private::PasswordUiEntry credential_1;
credential_1.username = "bob";
- credential_1.urls.shown = "example.com";
- credential_1.urls.link = "https://example.com";
- credential_1.is_android_credential = false;
+ credential_1.affiliated_domains.emplace_back();
+ credential_1.affiliated_domains.back().name = "example.com";
+ credential_1.affiliated_domains.back().url = "https://example.com";
credential_1.change_password_url = "https://example.com/change-password";
credential_1.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
credential_1.compromised_info.emplace();
@@ -322,9 +325,9 @@ TestPasswordsPrivateDelegate::GetCredentialsWithReusedPassword() {
api::passwords_private::PasswordUiEntry credential_2;
credential_2.username = "angela";
- credential_2.urls.shown = "test.com";
- credential_2.urls.link = "https://test.com";
- credential_2.is_android_credential = false;
+ credential_2.affiliated_domains.emplace_back();
+ credential_2.affiliated_domains.back().name = "test.com";
+ credential_2.affiliated_domains.back().url = "https://test.com";
credential_2.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
credential_2.compromised_info.emplace();
credential_2.compromised_info->compromise_types = {
@@ -351,22 +354,12 @@ bool TestPasswordsPrivateDelegate::UnmuteInsecureCredential(
return IsCredentialPresentInInsecureCredentialsList(credential);
}
-void TestPasswordsPrivateDelegate::RecordChangePasswordFlowStarted(
- const api::passwords_private::PasswordUiEntry& credential) {
- last_change_flow_url_ =
- credential.change_password_url ? *credential.change_password_url : "";
-}
-
void TestPasswordsPrivateDelegate::StartPasswordCheck(
StartPasswordCheckCallback callback) {
start_password_check_triggered_ = true;
std::move(callback).Run(start_password_check_state_);
}
-void TestPasswordsPrivateDelegate::StopPasswordCheck() {
- stop_password_check_triggered_ = true;
-}
-
api::passwords_private::PasswordCheckStatus
TestPasswordsPrivateDelegate::GetPasswordCheckStatus() {
api::passwords_private::PasswordCheckStatus status;
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.h b/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.h
index b2061d34e50..25c39a8de01 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.h
+++ b/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.h
@@ -40,11 +40,6 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate {
const std::u16string& note,
bool use_account_store,
content::WebContents* web_contents) override;
- // Fake implementation of ChangeSavedPassword. This succeeds if the current
- // list of entries has the id and if the new password isn't empty.
- absl::optional<int> ChangeSavedPassword(
- const int id,
- const api::passwords_private::ChangeSavedPasswordParams& params) override;
bool ChangeCredential(
const api::passwords_private::PasswordUiEntry& credential) override;
void RemoveCredential(
@@ -62,6 +57,8 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate {
content::WebContents* web_contents) override;
void MovePasswordsToAccount(const std::vector<int>& ids,
content::WebContents* web_contents) override;
+ void FetchFamilyMembers(FetchFamilyResultsCallback callback) override;
+ void SharePassword(int id, const ShareRecipients& recipients) override;
void ImportPasswords(api::passwords_private::PasswordStoreSet to_store,
ImportResultsCallback results_callback,
content::WebContents* web_contents) override;
@@ -71,7 +68,6 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate {
void ResetImporter(bool delete_file) override;
void ExportPasswords(base::OnceCallback<void(const std::string&)> callback,
content::WebContents* web_contents) override;
- void CancelExportPasswords() override;
api::passwords_private::ExportProgressStatus GetExportProgressStatus()
override;
bool IsOptedInForAccountStorage() override;
@@ -89,12 +85,7 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate {
// delegate knows of a insecure credential with the same id.
bool UnmuteInsecureCredential(
const api::passwords_private::PasswordUiEntry& credential) override;
- // Fake implementation of `RecordChangePasswordFlowStarted`. Sets the url
- // returned by `last_change_flow_url()`.
- void RecordChangePasswordFlowStarted(
- const api::passwords_private::PasswordUiEntry& credential) override;
void StartPasswordCheck(StartPasswordCheckCallback callback) override;
- void StopPasswordCheck() override;
api::passwords_private::PasswordCheckStatus GetPasswordCheckStatus() override;
password_manager::InsecureCredentialsManager* GetInsecureCredentialsManager()
override;
@@ -116,22 +107,18 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate {
bool ContinueImportTriggered() const { return continue_import_triggered_; }
bool ResetImporterTriggered() const { return reset_importer_triggered_; }
bool ExportPasswordsTriggered() const { return export_passwords_triggered_; }
- bool CancelExportPasswordsTriggered() const {
- return cancel_export_passwords_triggered_;
+ bool FetchFamilyMembersTriggered() const {
+ return fetch_family_members_triggered_;
}
+ bool SharePasswordTriggered() const { return share_password_triggered_; }
bool StartPasswordCheckTriggered() const {
return start_password_check_triggered_;
}
- bool StopPasswordCheckTriggered() const {
- return stop_password_check_triggered_;
- }
void SetStartPasswordCheckState(
password_manager::BulkLeakCheckService::State state) {
start_password_check_state_ = state;
}
- const std::string& last_change_flow_url() { return last_change_flow_url_; }
-
const std::vector<int>& last_moved_passwords() const {
return last_moved_passwords_;
}
@@ -173,6 +160,8 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate {
api::passwords_private::ImportResults import_results_;
+ api::passwords_private::FamilyFetchResults family_fetch_results_;
+
// List of insecure credentials.
std::vector<api::passwords_private::PasswordUiEntry> insecure_credentials_;
raw_ptr<Profile, DanglingUntriaged> profile_ = nullptr;
@@ -180,23 +169,21 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate {
bool is_opted_in_for_account_storage_ = false;
bool is_account_store_default_ = false;
+ // Flags for detecting whether password sharing operations have been invoked.
+ bool fetch_family_members_triggered_ = false;
+ bool share_password_triggered_ = false;
+
// Flags for detecting whether import/export operations have been invoked.
bool import_passwords_triggered_ = false;
bool continue_import_triggered_ = false;
bool reset_importer_triggered_ = false;
bool export_passwords_triggered_ = false;
- bool cancel_export_passwords_triggered_ = false;
// Flags for detecting whether password check operations have been invoked.
bool start_password_check_triggered_ = false;
- bool stop_password_check_triggered_ = false;
password_manager::BulkLeakCheckService::State start_password_check_state_ =
password_manager::BulkLeakCheckService::State::kRunning;
- // Url of the last reported change password flow. Defaults to empty if
- // none has been registered.
- std::string last_change_flow_url_;
-
// Records the ids of the passwords that were last moved.
std::vector<int> last_moved_passwords_;
diff --git a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.cc b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.cc
index fa93ed64865..4d5278e8c53 100644
--- a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.cc
+++ b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.cc
@@ -12,11 +12,11 @@
namespace extensions {
// static
-PdfViewerPrivateEventRouter* PdfViewerPrivateEventRouter::Create(
- content::BrowserContext* context) {
+std::unique_ptr<PdfViewerPrivateEventRouter>
+PdfViewerPrivateEventRouter::Create(content::BrowserContext* context) {
DCHECK(context);
Profile* profile = Profile::FromBrowserContext(context);
- return new PdfViewerPrivateEventRouter(profile);
+ return std::make_unique<PdfViewerPrivateEventRouter>(profile);
}
PdfViewerPrivateEventRouter::PdfViewerPrivateEventRouter(Profile* profile)
diff --git a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.h b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.h
index 4035fac9170..1d4ffd7cf2f 100644
--- a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.h
+++ b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.h
@@ -20,7 +20,8 @@ namespace extensions {
class PdfViewerPrivateEventRouter : public KeyedService,
public EventRouter::Observer {
public:
- static PdfViewerPrivateEventRouter* Create(content::BrowserContext* context);
+ static std::unique_ptr<PdfViewerPrivateEventRouter> Create(
+ content::BrowserContext* context);
explicit PdfViewerPrivateEventRouter(Profile* profile);
diff --git a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.cc b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.cc
index 77bb8787bb8..3a1c61b2763 100644
--- a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.cc
+++ b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.cc
@@ -42,7 +42,8 @@ PdfViewerPrivateEventRouterFactory::PdfViewerPrivateEventRouterFactory()
PdfViewerPrivateEventRouterFactory::~PdfViewerPrivateEventRouterFactory() =
default;
-KeyedService* PdfViewerPrivateEventRouterFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+PdfViewerPrivateEventRouterFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
return PdfViewerPrivateEventRouter::Create(context);
}
diff --git a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h
index 97612812ac9..5cb6e8c7731 100644
--- a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h
+++ b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h
@@ -42,7 +42,7 @@ class PdfViewerPrivateEventRouterFactory : public ProfileKeyedServiceFactory {
~PdfViewerPrivateEventRouterFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
index 7e45a793576..4eb304afe20 100644
--- a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
+++ b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
@@ -21,6 +21,7 @@
#include "chromeos/crosapi/mojom/keystore_error.mojom-shared.h"
#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "net/base/net_errors.h"
#include "net/cert/asn1_util.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/x509_util.h"
diff --git a/chromium/chrome/browser/extensions/api/preference/preference_api.cc b/chromium/chrome/browser/extensions/api/preference/preference_api.cc
index 8f74c295315..aaf07e4ced3 100644
--- a/chromium/chrome/browser/extensions/api/preference/preference_api.cc
+++ b/chromium/chrome/browser/extensions/api/preference/preference_api.cc
@@ -41,6 +41,7 @@
#include "extensions/browser/extension_system_provider.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/pref_names.h"
+#include "extensions/common/api/types.h"
#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_id.h"
@@ -61,6 +62,8 @@ namespace extensions {
namespace {
+using extensions::api::types::ChromeSettingScope;
+
constexpr char kConversionErrorMessage[] =
"Internal error: Stored value for preference '*' cannot be converted "
"properly.";
@@ -79,6 +82,37 @@ constexpr char kIncognitoSpecific[] = "incognitoSpecific";
constexpr char kLevelOfControl[] = "levelOfControl";
constexpr char kValue[] = "value";
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+// Returns true if the get, set or clear requests for the preference associated
+// with `pref_path` should only be applied at browser level. Returns false if
+// the requests should be forwarded to Ash.
+// All preferences explicitly added to`crosapi::mojom::PrefPath` should be
+// handled by Ash. The only exception is the `crosapi::mojom::PrefPath::kProxy`
+// pref which, for secondary profiles only, is applied at browser scope.
+bool IsBrowserScopePrefOperation(crosapi::mojom::PrefPath pref_path,
+ Profile* profile) {
+ if (pref_path == crosapi::mojom::PrefPath::kUnknown) {
+ return true;
+ }
+ if (pref_path == crosapi::mojom::PrefPath::kProxy) {
+ if (!profile->IsMainProfile()) {
+ return true;
+ }
+ // TODO(acostinas,b/267719988) If the current version of Ash does not
+ // support syncing the proxy pref via the Prefs mojo service, the proxy pref
+ // can be set at browser scope only and it will be synced with Ash via the
+ // NetworkSettingsService mojo API.
+ static constexpr int kMinVersionProxyPref = 4;
+ const int version = chromeos::LacrosService::Get()
+ ->GetInterfaceVersion<crosapi::mojom::Prefs>();
+ if (version < kMinVersionProxyPref) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
// Transform the thirdPartyCookiesAllowed extension api to CookieControlsMode
// enum values.
class CookieControlsModeTransformer : public PrefTransformerInterface {
@@ -188,27 +222,9 @@ class PrivacySandboxTransformer : public PrefTransformerInterface {
}
};
-constexpr char kIncognitoPersistent[] = "incognito_persistent";
-constexpr char kIncognitoSessionOnly[] = "incognito_session_only";
-constexpr char kRegular[] = "regular";
-constexpr char kRegularOnly[] = "regular_only";
-
-// TODO(crbug.com/1366445): Consider using the ChromeSettingScope
-// enum instead of ExtensionPrefsScope. That way, we could remove
-// this function and the preceding string constants.
-bool StringToScope(const std::string& s, ExtensionPrefsScope* scope) {
- if (s == kRegular) {
- *scope = kExtensionPrefsScopeRegular;
- } else if (s == kRegularOnly) {
- *scope = kExtensionPrefsScopeRegularOnly;
- } else if (s == kIncognitoPersistent) {
- *scope = kExtensionPrefsScopeIncognitoPersistent;
- } else if (s == kIncognitoSessionOnly) {
- *scope = kExtensionPrefsScopeIncognitoSessionOnly;
- } else {
- return false;
- }
- return true;
+bool StringToScope(const std::string& s, ChromeSettingScope& scope) {
+ scope = extensions::api::types::ParseChromeSettingScope(s);
+ return scope != ChromeSettingScope::kNone;
}
} // namespace
@@ -232,7 +248,7 @@ PreferenceEventRouter::PreferenceEventRouter(Profile* profile)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
crosapi::mojom::PrefPath pref_path =
PrefMapping::GetInstance()->GetPrefPathForPrefName(pref.browser_pref);
- if (pref_path != crosapi::mojom::PrefPath::kUnknown &&
+ if (!IsBrowserScopePrefOperation(pref_path, profile) &&
ash_supports_crosapi_observers) {
// Extension-controlled pref with the real value to watch in ash.
// This base::Unretained() is safe because PreferenceEventRouter owns
@@ -527,19 +543,19 @@ void PreferenceAPI::OnContentSettingChanged(const std::string& extension_id,
ExtensionPrefs::Get(profile_)->UpdateExtensionPref(
extension_id, pref_names::kPrefIncognitoContentSettings,
base::Value(content_settings_store()->GetSettingsForExtension(
- extension_id, kExtensionPrefsScopeIncognitoPersistent)));
+ extension_id, ChromeSettingScope::kIncognitoPersistent)));
} else {
ExtensionPrefs::Get(profile_)->UpdateExtensionPref(
extension_id, pref_names::kPrefContentSettings,
base::Value(content_settings_store()->GetSettingsForExtension(
- extension_id, kExtensionPrefsScopeRegular)));
+ extension_id, ChromeSettingScope::kRegular)));
}
}
void PreferenceAPI::ClearIncognitoSessionOnlyContentSettings() {
for (const auto& id : ExtensionPrefs::Get(profile_)->GetExtensions()) {
content_settings_store()->ClearContentSettingsForExtension(
- id, kExtensionPrefsScopeIncognitoSessionOnly);
+ id, ChromeSettingScope::kIncognitoSessionOnly);
}
}
@@ -623,8 +639,12 @@ ExtensionFunction::ResponseAction GetPreferenceFunction::Run() {
cached_browser_pref_ = browser_pref;
crosapi::mojom::PrefPath pref_path =
PrefMapping::GetInstance()->GetPrefPathForPrefName(cached_browser_pref_);
- if (pref_path != crosapi::mojom::PrefPath::kUnknown) {
- if (!profile->IsMainProfile()) {
+ if (!IsBrowserScopePrefOperation(pref_path, profile)) {
+ // Exclude chrome.privacy.website.protectedContentID (mapped to
+ // kProtectedContentDefault) from secondary profile access
+ // (crbug.com/1450718).
+ if (!profile->IsMainProfile() &&
+ pref_path == crosapi::mojom::PrefPath::kProtectedContentDefault) {
return RespondNow(Error(kPrimaryProfileOnlyErrorMessage, pref_key));
}
// This pref should be read from ash.
@@ -749,15 +769,14 @@ ExtensionFunction::ResponseAction SetPreferenceFunction::Run() {
const base::Value* value = details.Find(kValue);
EXTENSION_FUNCTION_VALIDATE(value);
- ExtensionPrefsScope scope = kExtensionPrefsScopeRegular;
+ ChromeSettingScope scope = ChromeSettingScope::kRegular;
if (const std::string* scope_str = details.FindString(kScopeKey)) {
- EXTENSION_FUNCTION_VALIDATE(StringToScope(*scope_str, &scope));
+ EXTENSION_FUNCTION_VALIDATE(StringToScope(*scope_str, scope));
}
// Check incognito scope.
- bool incognito =
- (scope == kExtensionPrefsScopeIncognitoPersistent ||
- scope == kExtensionPrefsScopeIncognitoSessionOnly);
+ bool incognito = scope == ChromeSettingScope::kIncognitoPersistent ||
+ scope == ChromeSettingScope::kIncognitoSessionOnly;
if (incognito) {
// Regular profiles can't access incognito unless
// include_incognito_information is true.
@@ -775,7 +794,7 @@ ExtensionFunction::ResponseAction SetPreferenceFunction::Run() {
}
Profile* profile = Profile::FromBrowserContext(browser_context());
- if (scope == kExtensionPrefsScopeIncognitoSessionOnly &&
+ if (scope == ChromeSettingScope::kIncognitoSessionOnly &&
!profile->HasPrimaryOTRProfile()) {
return RespondNow(Error(extension_misc::kIncognitoSessionOnlyErrorMessage));
}
@@ -796,7 +815,7 @@ ExtensionFunction::ResponseAction SetPreferenceFunction::Run() {
crosapi::mojom::PrefPath pref_path =
PrefMapping::GetInstance()->GetPrefPathForPrefName(browser_pref);
chromeos::LacrosService* lacros_service;
- if (pref_path != crosapi::mojom::PrefPath::kUnknown) {
+ if (!IsBrowserScopePrefOperation(pref_path, profile)) {
if (!profile->IsMainProfile()) {
return RespondNow(Error(kPrimaryProfileOnlyErrorMessage, pref_key));
}
@@ -899,7 +918,7 @@ ExtensionFunction::ResponseAction SetPreferenceFunction::Run() {
prefs_helper->SetExtensionControlledPref(extension_id(), browser_pref, scope,
browser_pref_value->Clone());
#if BUILDFLAG(IS_CHROMEOS_LACROS)
- if (pref_path != crosapi::mojom::PrefPath::kUnknown &&
+ if (!IsBrowserScopePrefOperation(pref_path, profile) &&
prefs_helper->DoesExtensionControlPref(extension_id(), browser_pref,
nullptr)) {
lacros_service->GetRemote<crosapi::mojom::Prefs>()->SetPref(
@@ -928,15 +947,14 @@ ExtensionFunction::ResponseAction ClearPreferenceFunction::Run() {
std::string pref_key = args()[0].GetString();
const base::Value::Dict& details = args()[1].GetDict();
- ExtensionPrefsScope scope = kExtensionPrefsScopeRegular;
+ ChromeSettingScope scope = ChromeSettingScope::kRegular;
if (const std::string* scope_str = details.FindString(kScopeKey)) {
- EXTENSION_FUNCTION_VALIDATE(StringToScope(*scope_str, &scope));
+ EXTENSION_FUNCTION_VALIDATE(StringToScope(*scope_str, scope));
}
// Check incognito scope.
- bool incognito =
- (scope == kExtensionPrefsScopeIncognitoPersistent ||
- scope == kExtensionPrefsScopeIncognitoSessionOnly);
+ bool incognito = scope == ChromeSettingScope::kIncognitoPersistent ||
+ scope == ChromeSettingScope::kIncognitoSessionOnly;
if (incognito) {
// We don't check incognito permissions here, as an extension should be
// always allowed to clear its own settings.
@@ -964,8 +982,8 @@ ExtensionFunction::ResponseAction ClearPreferenceFunction::Run() {
crosapi::mojom::PrefPath pref_path =
PrefMapping::GetInstance()->GetPrefPathForPrefName(browser_pref);
chromeos::LacrosService* lacros_service;
- if (pref_path != crosapi::mojom::PrefPath::kUnknown) {
- Profile* profile = Profile::FromBrowserContext(browser_context());
+ Profile* profile = Profile::FromBrowserContext(browser_context());
+ if (!IsBrowserScopePrefOperation(pref_path, profile)) {
if (!profile->IsMainProfile()) {
return RespondNow(Error(kPrimaryProfileOnlyErrorMessage, pref_key));
}
@@ -1015,14 +1033,13 @@ ExtensionFunction::ResponseAction ClearPreferenceFunction::Run() {
extension_id(), prefs::kSafeBrowsingEnhanced, scope);
}
#if BUILDFLAG(IS_CHROMEOS_LACROS)
- if (pref_path != crosapi::mojom::PrefPath::kUnknown &&
+ if (!IsBrowserScopePrefOperation(pref_path, profile) &&
did_just_control_pref) {
// This is an ash pref and we need to update ash because the extension that
// just cleared the pref used to control it. Now, either another extension
// of lower precedence controls the pref (in which case we update the pref
// to that value), or no other extension has set the pref (in which case
// we can clear the value set by extensions in ash).
- Profile* profile = Profile::FromBrowserContext(browser_context());
PrefService* pref_service =
extensions::preference_helpers::GetProfilePrefService(profile,
incognito);
diff --git a/chromium/chrome/browser/extensions/api/preference/preference_api.h b/chromium/chrome/browser/extensions/api/preference/preference_api.h
index a1d0c204081..abee3457484 100644
--- a/chromium/chrome/browser/extensions/api/preference/preference_api.h
+++ b/chromium/chrome/browser/extensions/api/preference/preference_api.h
@@ -18,6 +18,7 @@
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_function.h"
+#include "extensions/common/api/types.h"
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chromeos/crosapi/mojom/prefs.mojom-shared.h"
@@ -96,6 +97,7 @@ class PreferenceAPI : public BrowserContextKeyedAPI,
public EventRouter::Observer,
public ContentSettingsStore::Observer {
public:
+ using ChromeSettingScope = extensions::api::types::ChromeSettingScope;
explicit PreferenceAPI(content::BrowserContext* context);
PreferenceAPI(const PreferenceAPI&) = delete;
diff --git a/chromium/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc b/chromium/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc
index 0ffff000462..b90472f5495 100644
--- a/chromium/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc
@@ -7,22 +7,31 @@
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/task/single_thread_task_runner.h"
+#include "base/test/test_future.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/pref_names.h"
-#include "chromeos/crosapi/mojom/prefs.mojom-test-utils.h"
+#include "chromeos/crosapi/mojom/prefs.mojom-shared.h"
#include "chromeos/crosapi/mojom/prefs.mojom.h"
+#include "chromeos/lacros/crosapi_pref_observer.h"
#include "chromeos/lacros/lacros_service.h"
#include "chromeos/lacros/lacros_test_helper.h"
#include "chromeos/startup/browser_params_proxy.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/prefs/pref_service.h"
+#include "components/proxy_config/proxy_config_dictionary.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
#include "content/public/browser/notification_service.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
@@ -30,8 +39,26 @@
#include "mojo/public/cpp/bindings/remote_set.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace {
+
using ContextType = extensions::ExtensionBrowserTest::ContextType;
+void SetPref(crosapi::mojom::PrefPath path, base::Value value) {
+ base::test::TestFuture<void> future;
+ chromeos::LacrosService::Get()->GetRemote<crosapi::mojom::Prefs>()->SetPref(
+ path, std::move(value), future.GetCallback());
+ ASSERT_TRUE(future.Wait());
+}
+
+absl::optional<base::Value> GetPref(crosapi::mojom::PrefPath path) {
+ base::test::TestFuture<absl::optional<base::Value>> future;
+ chromeos::LacrosService::Get()->GetRemote<crosapi::mojom::Prefs>()->GetPref(
+ path, future.GetCallback());
+ return future.Take();
+}
+
+} // namespace
+
// Tests for extension-controlled prefs, where an extension in lacros sets a
// pref where the underlying feature lives in ash.
class ExtensionPreferenceApiLacrosBrowserTest
@@ -56,6 +83,13 @@ class ExtensionPreferenceApiLacrosBrowserTest
EXPECT_TRUE(pref->IsExtensionControlled());
EXPECT_TRUE(
prefs->GetBoolean(prefs::kLacrosAccessibilitySpokenFeedbackEnabled));
+
+ const PrefService::Preference* proxy_pref =
+ prefs->FindPreference(proxy_config::prefs::kProxy);
+ ASSERT_TRUE(proxy_pref);
+ EXPECT_TRUE(proxy_pref->IsExtensionControlled());
+ EXPECT_EQ(ProxyConfigDictionary::CreateDirect(),
+ proxy_pref->GetValue()->GetDict());
}
void CheckPreferencesCleared() {
@@ -66,6 +100,29 @@ class ExtensionPreferenceApiLacrosBrowserTest
EXPECT_FALSE(pref->IsExtensionControlled());
EXPECT_FALSE(
prefs->GetBoolean(prefs::kLacrosAccessibilitySpokenFeedbackEnabled));
+
+ const PrefService::Preference* proxy_pref =
+ prefs->FindPreference(proxy_config::prefs::kProxy);
+ ASSERT_TRUE(proxy_pref);
+ EXPECT_FALSE(proxy_pref->IsExtensionControlled());
+ EXPECT_EQ(ProxyConfigDictionary::CreateSystem(),
+ proxy_pref->GetValue()->GetDict());
+ }
+
+ void SetUp() override {
+ // When the test changes the value of
+ // chrome.accessibilityFeatures.autoclick in Ash, the pref value change is
+ // observed by AccessibilityController and will trigger popping up a dialog
+ // in Ash with the prompt about confirmation of disabling autoclick. The
+ // dialog is not closed when the test is torn down in Lacros, and will
+ // affect other tests running after it if the test runs with shared Ash.
+ // Therefore, we start a unique Ash to run with this test suite to avoid
+ // the test isolation issue.
+ StartUniqueAshChrome(
+ {}, {}, {},
+ "crbug.com/1435317 Switch to shared ash when autoclick disable "
+ "confirmation dialog issue is fixed");
+ ExtensionApiTest::SetUp();
}
void SetUpOnMainThread() override {
@@ -107,6 +164,13 @@ class ExtensionPreferenceApiLacrosBrowserTest
return true;
}
+ bool IsLacrosServiceSyncingProxyPref() {
+ static constexpr int kMinVersionProxyPolicy = 4;
+ const int version = chromeos::LacrosService::Get()
+ ->GetInterfaceVersion<crosapi::mojom::Prefs>();
+ return version >= kMinVersionProxyPolicy;
+ }
+
bool DoesAshSupportObservers() {
// Versions of ash without this capability cannot create observers for prefs
// writing to the ash standalone browser prefstore.
@@ -130,14 +194,9 @@ INSTANTIATE_TEST_SUITE_P(ServiceWorker,
::testing::Values(ContextType::kServiceWorker));
IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, Lacros) {
- absl::optional<::base::Value> out_value;
- crosapi::mojom::PrefsAsyncWaiter async_waiter(
- chromeos::LacrosService::Get()->GetRemote<crosapi::mojom::Prefs>().get());
-
// At start, the value in ash should not be set.
- async_waiter.GetPref(
- crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled,
- &out_value);
+ absl::optional<base::Value> out_value =
+ GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled);
EXPECT_FALSE(out_value.value().GetBool());
extensions::ExtensionId test_extension_id;
@@ -160,11 +219,14 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, Lacros) {
CheckPreferencesSet();
// In ash, the value should now be set.
- async_waiter.GetPref(
- crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled,
- &out_value);
+ out_value =
+ GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled);
EXPECT_TRUE(out_value.value().GetBool());
-
+ if (IsLacrosServiceSyncingProxyPref()) {
+ out_value = GetPref(crosapi::mojom::PrefPath::kProxy);
+ EXPECT_EQ(out_value.value().GetDict(),
+ ProxyConfigDictionary::CreateDirect());
+ }
// The settings should not be reset when the extension is reloaded.
{
ExtensionTestMessageListener listener("ready", ReplyBehavior::kWillReply);
@@ -186,10 +248,15 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, Lacros) {
// When the extension in uninstalled, the pref in lacros should be the
// default value (false). This only works if Ash correctly implements
// extension-controlled pref observers.
- async_waiter.GetPref(
- crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled,
- &out_value);
+ out_value =
+ GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled);
EXPECT_FALSE(out_value.value().GetBool());
+
+ if (IsLacrosServiceSyncingProxyPref()) {
+ out_value = GetPref(crosapi::mojom::PrefPath::kProxy);
+ EXPECT_EQ(out_value.value().GetDict(),
+ ProxyConfigDictionary::CreateSystem());
+ }
}
{
@@ -201,6 +268,69 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, Lacros) {
CheckPreferencesCleared();
}
+IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest,
+ LacrosSecondaryProfile) {
+ // At start, the value in ash should not be set.
+ absl::optional<base::Value> out_value =
+ GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled);
+ EXPECT_FALSE(out_value.value().GetBool());
+
+ // Create a secondary profile.
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ Profile& secondary_profile = profiles::testing::CreateProfileSync(
+ profile_manager, profile_manager->GenerateNextProfileDirectoryPath());
+ ASSERT_FALSE(secondary_profile.IsMainProfile());
+
+ // Load the testing extension in secondary profile.
+ extensions::ResultCatcher catcher;
+ ExtensionTestMessageListener listener_1("ready", ReplyBehavior::kWillReply);
+ extensions::ChromeTestExtensionLoader loader(&secondary_profile);
+ base::FilePath extension_path =
+ test_data_dir_.AppendASCII("preference/lacros_secondary_profile_read");
+ scoped_refptr<const extensions::Extension> extension =
+ loader.LoadExtension(extension_path);
+ ASSERT_TRUE(extension);
+ EXPECT_TRUE(listener_1.WaitUntilSatisfied());
+
+ // Run the test to verify that testing extension running in secondary
+ // profile reads the default values of the Prefs correctly.
+ listener_1.Reply("run test default value");
+ EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+
+ // Set the pref value in ash.
+ SetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled,
+ base::Value(true));
+
+ // Verify the value is set in ash side.
+ out_value =
+ GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled);
+ EXPECT_TRUE(out_value.value().GetBool());
+
+ // Reload the testing extension in the secondary profile.
+ ExtensionTestMessageListener listener_2("ready", ReplyBehavior::kWillReply);
+ extensions::TestExtensionRegistryObserver observer(
+ extensions::ExtensionRegistry::Get(&secondary_profile), extension->id());
+ extensions::ExtensionService* extension_service =
+ extensions::ExtensionSystem::Get(&secondary_profile)->extension_service();
+ extension_service->ReloadExtension(extension->id());
+ observer.WaitForExtensionLoaded();
+ EXPECT_TRUE(listener_2.WaitUntilSatisfied());
+
+ // Run the test to verify that testing extension running in secondary
+ // profile reads the changed value of the accessibilityFeatures correctly.
+ listener_2.Reply("run test changed value");
+ EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+
+ // Since lacros browser tests shared the same ash instance, we need to restore
+ // the modified pref in ash to default before exiting the test, so that
+ // it won't affect other lacros browser tests.
+ SetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled,
+ base::Value(false));
+ out_value =
+ GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled);
+ EXPECT_FALSE(out_value.value().GetBool());
+}
+
IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, OnChange) {
if (!DoesAshSupportObservers()) {
LOG(WARNING) << "Ash does not support observers, skipping the test.";
@@ -222,6 +352,144 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest,
<< message_;
}
+base::Value::Dict GetAshProxyPrefValue() {
+ absl::optional<::base::Value> out_value =
+ GetPref(crosapi::mojom::PrefPath::kProxy);
+ return out_value.value().GetDict().Clone();
+}
+
+scoped_refptr<const extensions::Extension> InstallExtensionForProfile(
+ Profile* profile,
+ const base::FilePath& path) {
+ extensions::ResultCatcher catcher;
+ ExtensionTestMessageListener listener("ready", ReplyBehavior::kWillReply);
+ scoped_refptr<const extensions::Extension> extension =
+ extensions::ChromeTestExtensionLoader(profile).LoadExtension(path);
+ EXPECT_TRUE(listener.WaitUntilSatisfied());
+ // Run the tests.
+ listener.Reply("run test");
+ EXPECT_TRUE(catcher.GetNextResult());
+ return extension;
+}
+
+void ExpectThatProxyIsControlledByExtension(Profile* profile) {
+ const PrefService::Preference* pref =
+ profile->GetPrefs()->FindPreference(proxy_config::prefs::kProxy);
+ EXPECT_TRUE(pref->IsExtensionControlled());
+ EXPECT_EQ(ProxyConfigDictionary::CreateDirect(), pref->GetValue()->GetDict());
+}
+
+void ExpectThatProxyHasDefaultValue(Profile* profile) {
+ const PrefService::Preference* pref =
+ profile->GetPrefs()->FindPreference(proxy_config::prefs::kProxy);
+ EXPECT_FALSE(pref->IsExtensionControlled());
+ EXPECT_EQ(ProxyConfigDictionary::CreateSystem(), pref->GetValue()->GetDict());
+}
+
+// Secondary profiles should apply extension set proxy at browser level, but not
+// in Ash.
+IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest,
+ SecondaryProfilePrefs) {
+ if (!IsServiceAvailable()) {
+ return;
+ }
+ if (!IsLacrosServiceSyncingProxyPref()) {
+ GTEST_SKIP() << "Skipping test because the current version of Ash does not "
+ "support getting the proxy preference from a Lacros "
+ "extension via the preferences service";
+ }
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ base::FilePath path_profile =
+ profile_manager->GenerateNextProfileDirectoryPath();
+ Profile& secondary_profile =
+ profiles::testing::CreateProfileSync(profile_manager, path_profile);
+ scoped_refptr<const extensions::Extension> extension =
+ InstallExtensionForProfile(
+ &secondary_profile,
+ test_data_dir_.AppendASCII("preference/lacros_secondary_profile"));
+ // Verify that the proxy is set by the extension for the secondary profile.
+ ExpectThatProxyIsControlledByExtension(&secondary_profile);
+ // The proxy should not be set in the primary profile and Ash.
+ ExpectThatProxyHasDefaultValue(profile());
+ EXPECT_EQ(GetAshProxyPrefValue(), ProxyConfigDictionary::CreateSystem());
+}
+
+// Clearing an extension set proxy in a secondary profile should not clear the
+// extension set proxy in the primary profile and Ash (if the primary profile
+// has an extension which controls the proxy). The test setup:
+// - Create a secondary profile;
+// - Install an extension which controls the proxy pref in the primary profile;
+// - Install an extension which controls the proxy pref in the secondary
+// profile;
+// - Verify that both profiles have extension controlled proxy prefs;
+// - Uninstall the proxy controlling extension in the secondary profile;
+// - Verify that the secondary profile does not have an extension set proxy;
+// - Verify that the primary profile and Ash still have an extension set proxy.
+// This test can be extended to other prefs for which the primary profile
+// controls the value in Ash but secondary profiles only control the pref
+// value at browser level.
+IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest,
+ SecondaryProfilePrefsClearPref) {
+ if (!IsServiceAvailable()) {
+ return;
+ }
+ if (!IsLacrosServiceSyncingProxyPref()) {
+ GTEST_SKIP() << "Skipping test because the current version of Ash does not "
+ "support getting the proxy preference from a Lacros "
+ "extension via the preferences service";
+ }
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ base::FilePath path_profile =
+ profile_manager->GenerateNextProfileDirectoryPath();
+ Profile& secondary_profile =
+ profiles::testing::CreateProfileSync(profile_manager, path_profile);
+
+ scoped_refptr<const extensions::Extension> extension_primary =
+ InstallExtensionForProfile(
+ profile(),
+ test_data_dir_.AppendASCII("preference/lacros_secondary_profile"));
+
+ scoped_refptr<const extensions::Extension> extension_secondary =
+ InstallExtensionForProfile(
+ &secondary_profile,
+ test_data_dir_.AppendASCII("preference/lacros_secondary_profile"));
+
+ ExpectThatProxyIsControlledByExtension(&secondary_profile);
+ ExpectThatProxyIsControlledByExtension(profile());
+
+ // Uninstall the extension in the secondary profile and test that Ash is still
+ // returning the pref set by the extension running in the Lacros primary
+ // profile.
+ {
+ extensions::TestExtensionRegistryObserver observer(
+ extensions::ExtensionRegistry::Get(&secondary_profile),
+ extension_secondary->id());
+ auto* service_ = extensions::ExtensionSystem::Get(&secondary_profile)
+ ->extension_service();
+ service_->UninstallExtension(extension_secondary->id(),
+ extensions::UNINSTALL_REASON_FOR_TESTING,
+ NULL);
+ observer.WaitForExtensionUninstalled();
+ }
+
+ ExpectThatProxyHasDefaultValue(&secondary_profile);
+ ExpectThatProxyIsControlledByExtension(profile());
+ EXPECT_EQ(GetAshProxyPrefValue(), ProxyConfigDictionary::CreateDirect());
+
+ // Uninstall the extension in the primary profile.
+ {
+ extensions::TestExtensionRegistryObserver observer(
+ extensions::ExtensionRegistry::Get(profile()), extension_primary->id());
+ auto* service_ =
+ extensions::ExtensionSystem::Get(profile())->extension_service();
+ service_->UninstallExtension(extension_primary->id(),
+ extensions::UNINSTALL_REASON_FOR_TESTING,
+ NULL);
+ observer.WaitForExtensionUninstalled();
+ }
+ EXPECT_EQ(GetAshProxyPrefValue(), ProxyConfigDictionary::CreateSystem());
+}
+
// An implementation of the `crosapi::mojom::Prefs` mojo service which returns
// null when fetching a pref value. Used for testing the Preference API against
// Ash-Lacros version skew where Ash does not recognize the Lacros extension
diff --git a/chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc b/chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc
index 78e39d8bad4..8ca9d8be440 100644
--- a/chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc
@@ -17,10 +17,12 @@
#include "extensions/browser/api/content_settings/content_settings_service.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_prefs_helper.h"
+#include "extensions/common/api/types.h"
#include "extensions/common/extension.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::Value;
+using extensions::api::types::ChromeSettingScope;
namespace extensions {
@@ -95,7 +97,7 @@ void ExtensionControlledPrefsTest::InstallExtensionControlledPref(
base::Value value) {
EnsureExtensionInstalled(extension);
prefs_helper_.SetExtensionControlledPref(
- extension->id(), key, kExtensionPrefsScopeRegular, std::move(value));
+ extension->id(), key, ChromeSettingScope::kRegular, std::move(value));
}
void ExtensionControlledPrefsTest::InstallExtensionControlledPrefIncognito(
@@ -104,7 +106,7 @@ void ExtensionControlledPrefsTest::InstallExtensionControlledPrefIncognito(
base::Value value) {
EnsureExtensionInstalled(extension);
prefs_helper_.SetExtensionControlledPref(
- extension->id(), key, kExtensionPrefsScopeIncognitoPersistent,
+ extension->id(), key, ChromeSettingScope::kIncognitoPersistent,
std::move(value));
}
@@ -114,7 +116,7 @@ void ExtensionControlledPrefsTest::
base::Value value) {
EnsureExtensionInstalled(extension);
prefs_helper_.SetExtensionControlledPref(
- extension->id(), key, kExtensionPrefsScopeIncognitoSessionOnly,
+ extension->id(), key, ChromeSettingScope::kIncognitoSessionOnly,
std::move(value));
}
@@ -246,7 +248,7 @@ class ControlledPrefsUninstallExtension : public ExtensionControlledPrefsTest {
ContentSettingsPattern::FromString("http://[*.]example.com");
store->SetExtensionContentSetting(
extension1()->id(), pattern, pattern, ContentSettingsType::IMAGES,
- CONTENT_SETTING_BLOCK, kExtensionPrefsScopeRegular);
+ CONTENT_SETTING_BLOCK, ChromeSettingScope::kRegular);
UninstallExtension(extension1()->id());
}
diff --git a/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc b/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc
index 55f11fa80f9..fd399146cb9 100644
--- a/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc
@@ -693,7 +693,9 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiEventPageTest,
// This check is not done in the Standard test so we can test if the granular
// Privacy Sandbox APIs are turned off, when |kPrivacySandboxApisEnabled| is
// turned off, in isolation of controlling them directly.
-IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiTest, PrivacySandboxMigration) {
+// TODO(crbug.com/1470295): Test is flaky on all platforms.
+IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiTest,
+ DISABLED_PrivacySandboxMigration) {
PrefService* prefs = profile_->GetPrefs();
prefs->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, true);
prefs->SetBoolean(prefs::kPrivacySandboxM1FledgeEnabled, true);
diff --git a/chromium/chrome/browser/extensions/api/preference/preference_helpers.h b/chromium/chrome/browser/extensions/api/preference/preference_helpers.h
index 2ab0bcfdf64..2e4c116866c 100644
--- a/chromium/chrome/browser/extensions/api/preference/preference_helpers.h
+++ b/chromium/chrome/browser/extensions/api/preference/preference_helpers.h
@@ -10,7 +10,6 @@
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "extensions/browser/extension_event_histogram_value.h"
-#include "extensions/browser/extension_prefs_scope.h"
#include "extensions/common/mojom/api_permission_id.mojom-shared.h"
#include "extensions/common/permissions/permission_set.h"
diff --git a/chromium/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc b/chromium/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc
index 382bd6d92ba..e4852e7ed53 100644
--- a/chromium/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc
@@ -419,12 +419,12 @@ IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersAsyncSuccess) {
IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersTwoExtensions) {
ResultCatcher catcher;
- std::string extension_id_1;
+ ExtensionId extension_id_1;
InitializePrinterProviderTestExtension("printer_provider/request_printers",
"OK", &extension_id_1);
ASSERT_FALSE(extension_id_1.empty());
- std::string extension_id_2;
+ ExtensionId extension_id_2;
InitializePrinterProviderTestExtension(
"printer_provider/request_printers_second", "OK", &extension_id_2);
ASSERT_FALSE(extension_id_2.empty());
@@ -475,12 +475,12 @@ IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest,
GetPrintersTwoExtensionsBothUnloaded) {
ResultCatcher catcher;
- std::string extension_id_1;
+ ExtensionId extension_id_1;
InitializePrinterProviderTestExtension("printer_provider/request_printers",
"IGNORE_CALLBACK", &extension_id_1);
ASSERT_FALSE(extension_id_1.empty());
- std::string extension_id_2;
+ ExtensionId extension_id_2;
InitializePrinterProviderTestExtension(
"printer_provider/request_printers_second", "IGNORE_CALLBACK",
&extension_id_2);
@@ -505,12 +505,12 @@ IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest,
GetPrintersTwoExtensionsOneFails) {
ResultCatcher catcher;
- std::string extension_id_1;
+ ExtensionId extension_id_1;
InitializePrinterProviderTestExtension("printer_provider/request_printers",
"NOT_ARRAY", &extension_id_1);
ASSERT_FALSE(extension_id_1.empty());
- std::string extension_id_2;
+ ExtensionId extension_id_2;
InitializePrinterProviderTestExtension(
"printer_provider/request_printers_second", "OK", &extension_id_2);
ASSERT_FALSE(extension_id_2.empty());
@@ -547,12 +547,12 @@ IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest,
GetPrintersTwoExtensionsOneWithNoListener) {
ResultCatcher catcher;
- std::string extension_id_1;
+ ExtensionId extension_id_1;
InitializePrinterProviderTestExtension("printer_provider/request_printers",
"NO_LISTENER", &extension_id_1);
ASSERT_FALSE(extension_id_1.empty());
- std::string extension_id_2;
+ ExtensionId extension_id_2;
InitializePrinterProviderTestExtension(
"printer_provider/request_printers_second", "OK", &extension_id_2);
ASSERT_FALSE(extension_id_2.empty());
diff --git a/chromium/chrome/browser/extensions/api/printing/fake_print_job_controller_ash.h b/chromium/chrome/browser/extensions/api/printing/fake_print_job_controller_ash.h
index 9558b5e5258..64779176f0d 100644
--- a/chromium/chrome/browser/extensions/api/printing/fake_print_job_controller_ash.h
+++ b/chromium/chrome/browser/extensions/api/printing/fake_print_job_controller_ash.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/containers/flat_map.h"
+#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ash/printing/cups_print_job_manager.h"
#include "chrome/browser/extensions/api/printing/print_job_controller.h"
@@ -47,8 +48,8 @@ class FakePrintJobControllerAsh : public PrintJobController,
std::unique_ptr<printing::PrintSettings> settings);
// Not owned by FakePrintJobControllerAsh.
- ash::TestCupsPrintJobManager* const print_job_manager_;
- ash::CupsPrintersManager* const printers_manager_;
+ const raw_ptr<ash::TestCupsPrintJobManager> print_job_manager_;
+ const raw_ptr<ash::CupsPrintersManager> printers_manager_;
// Stores ongoing print jobs as a mapping from job id to CupsPrintJob.
base::flat_map<std::string, std::unique_ptr<ash::CupsPrintJob>> jobs_;
diff --git a/chromium/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc b/chromium/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc
index 7c74e3a140a..53b228cd56e 100644
--- a/chromium/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc
@@ -37,6 +37,7 @@
#include "extensions/browser/event_router_factory.h"
#include "extensions/browser/test_event_router.h"
#include "extensions/common/extension_builder.h"
+#include "extensions/common/extension_id.h"
#include "printing/backend/print_backend.h"
#include "printing/backend/test_print_backend.h"
#include "printing/mojom/print.mojom.h"
@@ -88,7 +89,7 @@ class PrintingEventObserver : public TestEventRouter::EventObserver {
}
}
- const std::string& extension_id() const { return extension_id_; }
+ const ExtensionId& extension_id() const { return extension_id_; }
const base::Value& event_args() const { return event_args_; }
@@ -100,7 +101,7 @@ class PrintingEventObserver : public TestEventRouter::EventObserver {
const std::string event_name_;
// The extension id passed for the last observed event.
- std::string extension_id_;
+ ExtensionId extension_id_;
// The arguments passed for the last observed event.
base::Value event_args_;
@@ -204,10 +205,10 @@ ConstructPrinterCapabilities() {
capabilities.duplex_modes.push_back(printing::mojom::DuplexMode::kSimplex);
capabilities.copies_max = 5;
capabilities.dpis.emplace_back(kHorizontalDpi, kVerticalDpi);
- printing::PrinterSemanticCapsAndDefaults::Paper paper;
- paper.vendor_id = kMediaSizeVendorId;
- paper.size_um = gfx::Size(kMediaSizeWidth, kMediaSizeHeight);
- capabilities.papers.push_back(paper);
+ printing::PrinterSemanticCapsAndDefaults::Paper paper(
+ /*display_name=*/"", kMediaSizeVendorId,
+ {kMediaSizeWidth, kMediaSizeHeight});
+ capabilities.papers.push_back(std::move(paper));
capabilities.collate_capable = true;
return capabilities;
}
diff --git a/chromium/chrome/browser/extensions/api/printing/printing_api_utils.cc b/chromium/chrome/browser/extensions/api/printing/printing_api_utils.cc
index 326699bf56f..bdd84766925 100644
--- a/chromium/chrome/browser/extensions/api/printing/printing_api_utils.cc
+++ b/chromium/chrome/browser/extensions/api/printing/printing_api_utils.cc
@@ -305,7 +305,7 @@ bool CheckSettingsAndCapabilitiesCompatibility(
capabilities.papers,
[&requested_media](
const printing::PrinterSemanticCapsAndDefaults::Paper& paper) {
- return paper.size_um == requested_media.size_microns;
+ return paper.IsSizeWithinBounds(requested_media.size_microns);
});
}
diff --git a/chromium/chrome/browser/extensions/api/printing/printing_api_utils_unittest.cc b/chromium/chrome/browser/extensions/api/printing/printing_api_utils_unittest.cc
index e3992329609..2982d3e640e 100644
--- a/chromium/chrome/browser/extensions/api/printing/printing_api_utils_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/printing/printing_api_utils_unittest.cc
@@ -32,6 +32,7 @@ constexpr int kHorizontalDpi = 300;
constexpr int kVerticalDpi = 400;
constexpr int kMediaSizeWidth = 210000;
constexpr int kMediaSizeHeight = 297000;
+constexpr int kCustomMediaSizeMin = 2540;
constexpr char kMediaSizeVendorId[] = "iso_a4_210x297mm";
constexpr char kVendorItemId[] = "finishings";
constexpr char kVendorItemValue[] = "trim";
@@ -151,14 +152,29 @@ printing::PrinterSemanticCapsAndDefaults ConstructPrinterCapabilities() {
capabilities.duplex_modes.push_back(printing::mojom::DuplexMode::kLongEdge);
capabilities.copies_max = kCopies;
capabilities.dpis.push_back(gfx::Size(kHorizontalDpi, kVerticalDpi));
- printing::PrinterSemanticCapsAndDefaults::Paper paper;
- paper.vendor_id = kMediaSizeVendorId;
- paper.size_um = gfx::Size(kMediaSizeWidth, kMediaSizeHeight);
+ printing::PrinterSemanticCapsAndDefaults::Paper paper(
+ /*display_name=*/"", kMediaSizeVendorId,
+ gfx::Size(kMediaSizeWidth, kMediaSizeHeight));
capabilities.papers.push_back(paper);
capabilities.collate_capable = true;
return capabilities;
}
+printing::PrinterSemanticCapsAndDefaults
+ConstructPrinterCapabilitiesWithCustomSize() {
+ printing::PrinterSemanticCapsAndDefaults capabilities =
+ ConstructPrinterCapabilities();
+ // Reset our papers and create a new paper with a custom size range.
+ capabilities.papers.clear();
+ printing::PrinterSemanticCapsAndDefaults::Paper paper(
+ /*display_name=*/"", kMediaSizeVendorId,
+ gfx::Size(kMediaSizeWidth, kCustomMediaSizeMin),
+ /*printable_area_um=*/gfx::Rect(), kMediaSizeHeight);
+ capabilities.papers.push_back(paper);
+
+ return capabilities;
+}
+
} // namespace
TEST(PrintingApiUtilsTest, GetDefaultPrinterRules) {
@@ -287,6 +303,41 @@ TEST(PrintingApiUtilsTest,
CheckSettingsAndCapabilitiesCompatibility(*settings, capabilities));
}
+TEST(PrintingApiUtilsTest,
+ CheckSettingsAndCapabilitiesCompatibilityCustomMediaSize) {
+ std::unique_ptr<printing::PrintSettings> settings = ConstructPrintSettings();
+ printing::PrinterSemanticCapsAndDefaults capabilities =
+ ConstructPrinterCapabilitiesWithCustomSize();
+ EXPECT_TRUE(
+ CheckSettingsAndCapabilitiesCompatibility(*settings, capabilities));
+}
+
+TEST(PrintingApiUtilsTest,
+ CheckSettingsAndCapabilitiesCompatibilityCustomMediaSizeLongWidth) {
+ std::unique_ptr<printing::PrintSettings> settings = ConstructPrintSettings();
+ // Update the requested media so the width is wider than our custom size.
+ printing::PrintSettings::RequestedMedia media = settings->requested_media();
+ media.size_microns.set_width(kMediaSizeWidth + 1);
+ settings->set_requested_media(media);
+ printing::PrinterSemanticCapsAndDefaults capabilities =
+ ConstructPrinterCapabilitiesWithCustomSize();
+ EXPECT_FALSE(
+ CheckSettingsAndCapabilitiesCompatibility(*settings, capabilities));
+}
+
+TEST(PrintingApiUtilsTest,
+ CheckSettingsAndCapabilitiesCompatibilityCustomMediaSizeShortHeight) {
+ std::unique_ptr<printing::PrintSettings> settings = ConstructPrintSettings();
+ // Update the requested media so the length is shorter than our custom size.
+ printing::PrintSettings::RequestedMedia media = settings->requested_media();
+ media.size_microns.set_height(kCustomMediaSizeMin - 1);
+ settings->set_requested_media(media);
+ printing::PrinterSemanticCapsAndDefaults capabilities =
+ ConstructPrinterCapabilitiesWithCustomSize();
+ EXPECT_FALSE(
+ CheckSettingsAndCapabilitiesCompatibility(*settings, capabilities));
+}
+
TEST(PrintingApiUtilsTest, CheckSettingsAndCapabilitiesCompatibility_Collate) {
std::unique_ptr<printing::PrintSettings> settings = ConstructPrintSettings();
printing::PrinterSemanticCapsAndDefaults capabilities =
diff --git a/chromium/chrome/browser/extensions/api/printing/printing_apitest.cc b/chromium/chrome/browser/extensions/api/printing/printing_apitest.cc
index 9f5bdc0364c..544f7370dab 100644
--- a/chromium/chrome/browser/extensions/api/printing/printing_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/printing/printing_apitest.cc
@@ -61,10 +61,10 @@ ConstructPrinterCapabilities() {
capabilities->duplex_modes.push_back(printing::mojom::DuplexMode::kSimplex);
capabilities->copies_max = 2;
capabilities->dpis.emplace_back(kHorizontalDpi, kVerticalDpi);
- printing::PrinterSemanticCapsAndDefaults::Paper paper;
- paper.vendor_id = kMediaSizeVendorId;
- paper.size_um = gfx::Size(kMediaSizeWidth, kMediaSizeHeight);
- capabilities->papers.push_back(paper);
+ printing::PrinterSemanticCapsAndDefaults::Paper paper(
+ /*display_name=*/"", kMediaSizeVendorId,
+ {kMediaSizeWidth, kMediaSizeHeight});
+ capabilities->papers.push_back(std::move(paper));
capabilities->collate_capable = true;
return capabilities;
}
diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc b/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc
index 5ecb6201024..0d43a09f5d0 100644
--- a/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc
+++ b/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc
@@ -72,13 +72,10 @@ void ProxyEventRouter::OnPACScriptError(EventRouterForwarder* event_router,
base::Value::Dict dict;
dict.Set(kProxyEventFatalKey, false);
dict.Set(kProxyEventErrorKey, net::ErrorToString(net::ERR_PAC_SCRIPT_FAILED));
- std::string error_msg;
+ std::string error_msg = base::UTF16ToUTF8(error);
if (line_number != -1) {
- base::SStringPrintf(&error_msg,
- "line: %d: %s",
- line_number, base::UTF16ToUTF8(error).c_str());
- } else {
- error_msg = base::UTF16ToUTF8(error);
+ error_msg =
+ base::StringPrintf("line: %d: %s", line_number, error_msg.c_str());
}
dict.Set(kProxyEventDetailsKey, error_msg);
args.Append(base::Value(std::move(dict)));
diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc b/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc
index 26e38d794e3..0ce8aed91a4 100644
--- a/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc
@@ -34,6 +34,15 @@ const char kNoServer[] = "";
const char kNoBypass[] = "";
const char kNoPac[] = "";
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+bool IsLacrosServiceSyncingProxyPref() {
+ static constexpr int kMinVersionProxyPolicy = 4;
+ const int version = chromeos::LacrosService::Get()
+ ->GetInterfaceVersion<crosapi::mojom::Prefs>();
+ return version >= kMinVersionProxyPolicy;
+}
+#endif
+
} // namespace
class ProxySettingsApiTest : public ExtensionApiTest {
@@ -50,10 +59,22 @@ class ProxySettingsApiTest : public ExtensionApiTest {
// used for all tests in the target. Setting a proxy will prevent other
// tests which require a direct connection to complete successfully.
auto* lacros_service = chromeos::LacrosService::Get();
- if (lacros_service &&
- lacros_service->IsAvailable<crosapi::mojom::NetworkSettingsService>()) {
- lacros_service->GetRemote<crosapi::mojom::NetworkSettingsService>()
- ->ClearExtensionProxy();
+ if (!lacros_service) {
+ ExtensionApiTest::TearDownOnMainThread();
+ return;
+ }
+ if (IsLacrosServiceSyncingProxyPref()) {
+ if (lacros_service->IsAvailable<crosapi::mojom::Prefs>()) {
+ lacros_service->GetRemote<crosapi::mojom::Prefs>()
+ ->ClearExtensionControlledPref(crosapi::mojom::PrefPath::kProxy,
+ base::DoNothing());
+ }
+ } else {
+ if (lacros_service
+ ->IsAvailable<crosapi::mojom::NetworkSettingsService>()) {
+ lacros_service->GetRemote<crosapi::mojom::NetworkSettingsService>()
+ ->ClearExtensionProxy();
+ }
}
ExtensionApiTest::TearDownOnMainThread();
}
diff --git a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc
index 97f35f6a112..82f7ff830fb 100644
--- a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc
@@ -24,11 +24,13 @@
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/ash/components/login/auth/public/authentication_error.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
+#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/event_router.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace extensions {
@@ -201,11 +203,31 @@ Profile* GetActiveProfile(content::BrowserContext* browser_context) {
}
AuthToken* GetActiveProfileAuthToken(content::BrowserContext* browser_context) {
+ CHECK(!ash::features::ShouldUseAuthSessionStorage());
return ash::quick_unlock::QuickUnlockFactory::GetForProfile(
GetActiveProfile(browser_context))
->GetAuthToken();
}
+absl::optional<std::string> CheckTokenValidity(
+ content::BrowserContext* browser_context,
+ const std::string& token) {
+ if (ash::features::ShouldUseAuthSessionStorage()) {
+ if (!ash::AuthSessionStorage::Get()->IsValid(token)) {
+ return kAuthTokenExpired;
+ }
+ } else {
+ AuthToken* auth_token = GetActiveProfileAuthToken(browser_context);
+ if (!auth_token) {
+ return kAuthTokenExpired;
+ }
+ if (token != auth_token->Identifier()) {
+ return kAuthTokenInvalid;
+ }
+ }
+ return absl::nullopt;
+}
+
} // namespace
// quickUnlockPrivate.getAuthToken
@@ -260,11 +282,11 @@ ExtensionFunction::ResponseAction
QuickUnlockPrivateSetLockScreenEnabledFunction::Run() {
auto params =
quick_unlock_private::SetLockScreenEnabled::Params::Create(args());
- AuthToken* auth_token = GetActiveProfileAuthToken(browser_context());
- if (!auth_token)
- return RespondNow(Error(kAuthTokenExpired));
- if (params->token != auth_token->Identifier())
- return RespondNow(Error(kAuthTokenInvalid));
+ absl::optional<std::string> error =
+ CheckTokenValidity(browser_context(), params->token);
+ if (error.has_value()) {
+ return RespondNow(Error(error.value()));
+ }
GetActiveProfile(browser_context())
->GetPrefs()
@@ -288,11 +310,11 @@ QuickUnlockPrivateSetPinAutosubmitEnabledFunction::Run() {
auto params =
quick_unlock_private::SetPinAutosubmitEnabled::Params::Create(args());
- AuthToken* auth_token = GetActiveProfileAuthToken(browser_context());
- if (!auth_token)
- return RespondNow(Error(kAuthTokenExpired));
- if (params->token != auth_token->Identifier())
- return RespondNow(Error(kAuthTokenInvalid));
+ absl::optional<std::string> error =
+ CheckTokenValidity(browser_context(), params->token);
+ if (error.has_value()) {
+ return RespondNow(Error(error.value()));
+ }
Profile* profile = GetActiveProfile(browser_context());
user_manager::User* user =
@@ -324,10 +346,6 @@ QuickUnlockPrivateCanAuthenticatePinFunction::
ExtensionFunction::ResponseAction
QuickUnlockPrivateCanAuthenticatePinFunction::Run() {
- AuthToken* auth_token = GetActiveProfileAuthToken(browser_context());
- if (!auth_token)
- return RespondNow(Error(kAuthTokenExpired));
-
Profile* profile = GetActiveProfile(browser_context());
user_manager::User* user =
ash::ProfileHelper::Get()->GetUserByProfile(profile);
@@ -490,11 +508,11 @@ ExtensionFunction::ResponseAction QuickUnlockPrivateSetModesFunction::Run() {
if (params_->modes.size() > 1)
return RespondNow(Error(kMultipleModesNotSupported));
- AuthToken* auth_token = GetActiveProfileAuthToken(browser_context());
- if (!auth_token)
- return RespondNow(Error(kAuthTokenExpired));
- if (params_->token != auth_token->Identifier())
- return RespondNow(Error(kAuthTokenInvalid));
+ absl::optional<std::string> error =
+ CheckTokenValidity(browser_context(), params_->token);
+ if (error.has_value()) {
+ return RespondNow(Error(error.value()));
+ }
// Verify every credential is valid based on policies.
PrefService* pref_service = GetActiveProfile(browser_context())->GetPrefs();
diff --git a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc
index 72b99b2dd5c..ef287bff9e0 100644
--- a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc
@@ -7,6 +7,7 @@
#include "chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.h"
#include <memory>
+#include <utility>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
@@ -44,6 +45,8 @@
#include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
#include "chromeos/ash/components/login/auth/fake_extended_authenticator.h"
#include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h"
+#include "chromeos/ash/components/osauth/impl/auth_parts_impl.h"
+#include "chromeos/ash/components/osauth/impl/auth_session_storage_impl.h"
#include "chromeos/ash/services/device_sync/public/cpp/fake_device_sync_client.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
#include "chromeos/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
@@ -202,6 +205,10 @@ class QuickUnlockPrivateUnitTest
fake_user_manager_ = fake_user_manager.get();
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
std::move(fake_user_manager));
+ auth_parts_ = ash::AuthPartsImpl::CreateTestInstance();
+ auth_parts_->SetAuthSessionStorage(
+ std::make_unique<ash::AuthSessionStorageImpl>(
+ ash::UserDataAuthClient::Get()));
ExtensionApiUnittest::SetUp();
@@ -262,8 +269,14 @@ class QuickUnlockPrivateUnitTest
ash::AuthFactorsConfiguration());
}
- token_ = ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile)
- ->CreateAuthToken(auth_token_user_context_);
+ if (ash::features::ShouldUseAuthSessionStorage()) {
+ token_ = ash::AuthSessionStorage::Get()->Store(
+ std::make_unique<ash::UserContext>(auth_token_user_context_));
+ } else {
+ token_ = ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile)
+ ->CreateAuthToken(auth_token_user_context_);
+ }
+
base::RunLoop().RunUntilIdle();
return profile;
@@ -639,7 +652,8 @@ class QuickUnlockPrivateUnitTest
}
base::test::ScopedFeatureList feature_list_;
- raw_ptr<sync_preferences::TestingPrefServiceSyncable, ExperimentalAsh>
+ raw_ptr<sync_preferences::TestingPrefServiceSyncable,
+ DanglingUntriaged | ExperimentalAsh>
test_pref_service_;
private:
@@ -677,6 +691,7 @@ class QuickUnlockPrivateUnitTest
expect_modes_changed_ = false;
}
+ std::unique_ptr<ash::AuthPartsImpl> auth_parts_;
raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_ =
nullptr;
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
@@ -695,8 +710,12 @@ TEST_P(QuickUnlockPrivateUnitTest, GetAuthTokenValid) {
ash::quick_unlock::QuickUnlockStorage* quick_unlock_storage =
ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile());
- EXPECT_EQ(token_info->token,
- quick_unlock_storage->GetAuthToken()->Identifier());
+ if (ash::features::ShouldUseAuthSessionStorage()) {
+ EXPECT_TRUE(ash::AuthSessionStorage::Get()->IsValid(token_info->token));
+ } else {
+ EXPECT_EQ(token_info->token,
+ quick_unlock_storage->GetAuthToken()->Identifier());
+ }
EXPECT_EQ(token_info->lifetime_seconds,
ash::quick_unlock::AuthToken::kTokenExpiration.InSeconds());
}
diff --git a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc
index bf66ae0ca12..f9c9848c791 100644
--- a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc
+++ b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc
@@ -14,11 +14,14 @@
#include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
#include "chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/quick_unlock_private.h"
#include "chromeos/ash/components/login/auth/auth_performer.h"
#include "chromeos/ash/components/login/auth/extended_authenticator.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
+#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
+#include "components/user_manager/known_user.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -75,8 +78,15 @@ void LegacyQuickUnlockPrivateGetAuthTokenHelper::OnAuthSuccess(
QuickUnlockStorage* quick_unlock_storage =
ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile_);
quick_unlock_storage->MarkStrongAuth();
- token_info->token = quick_unlock_storage->CreateAuthToken(user_context);
- token_info->lifetime_seconds = AuthToken::kTokenExpiration.InSeconds();
+ if (ash::features::ShouldUseAuthSessionStorage()) {
+ token_info->token = ash::AuthSessionStorage::Get()->Store(
+ std::make_unique<ash::UserContext>(user_context));
+ // TODO(b/238606050): Determine authsession lifetime.
+ token_info->lifetime_seconds = AuthToken::kTokenExpiration.InSeconds();
+ } else {
+ token_info->token = quick_unlock_storage->CreateAuthToken(user_context);
+ token_info->lifetime_seconds = AuthToken::kTokenExpiration.InSeconds();
+ }
// The user has successfully authenticated, so we should reset pin/fingerprint
// attempt counts.
@@ -93,7 +103,8 @@ QuickUnlockPrivateGetAuthTokenHelper::QuickUnlockPrivateGetAuthTokenHelper(
std::string password)
: profile_(profile),
password_(std::move(password)),
- auth_performer_(ash::UserDataAuthClient::Get()) {}
+ auth_performer_(ash::UserDataAuthClient::Get()),
+ auth_factor_editor_(ash::UserDataAuthClient::Get()) {}
QuickUnlockPrivateGetAuthTokenHelper::~QuickUnlockPrivateGetAuthTokenHelper() =
default;
@@ -137,8 +148,9 @@ void QuickUnlockPrivateGetAuthTokenHelper::OnAuthSessionStarted(
return;
}
- const cryptohome::AuthFactor* password_factor =
- user_context->GetAuthFactorsData().FindOnlinePasswordFactor();
+ const auto* password_factor =
+ user_context->GetAuthFactorsData().FindFactorByType(
+ cryptohome::AuthFactorType::kPassword);
if (!password_factor) {
LOG(ERROR) << "Could not find password key";
std::move(callback).Run(
@@ -188,6 +200,14 @@ void QuickUnlockPrivateGetAuthTokenHelper::OnAuthFactorsConfiguration(
return;
}
+ // The user context stored in quick_unlock storage must have a device ID, so
+ // we retrieve and set it here.
+ user_manager::KnownUser known_user{g_browser_process->local_state()};
+ std::string device_id = known_user.GetDeviceId(user_context->GetAccountId());
+ LOG_IF(WARNING, device_id.empty())
+ << "Missing DeviceID for auth factor edits";
+ user_context->SetDeviceId(std::move(device_id));
+
QuickUnlockStorage* quick_unlock_storage =
ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile_);
quick_unlock_storage->MarkStrongAuth();
@@ -197,9 +217,16 @@ void QuickUnlockPrivateGetAuthTokenHelper::OnAuthFactorsConfiguration(
quick_unlock_storage->fingerprint_storage()->ResetUnlockAttemptCount();
TokenInfo token_info;
- token_info.token =
- quick_unlock_storage->CreateAuthToken(std::move(*user_context));
- token_info.lifetime_seconds = AuthToken::kTokenExpiration.InSeconds();
+ if (ash::features::ShouldUseAuthSessionStorage()) {
+ token_info.token =
+ ash::AuthSessionStorage::Get()->Store(std::move(user_context));
+ // TODO(b/238606050): Determine authsession lifetime.
+ token_info.lifetime_seconds = AuthToken::kTokenExpiration.InSeconds();
+ } else {
+ token_info.token =
+ quick_unlock_storage->CreateAuthToken(std::move(*user_context));
+ token_info.lifetime_seconds = AuthToken::kTokenExpiration.InSeconds();
+ }
std::move(callback).Run(std::move(token_info), absl::nullopt);
}
diff --git a/chromium/chrome/browser/extensions/api/reading_list/OWNERS b/chromium/chrome/browser/extensions/api/reading_list/OWNERS
new file mode 100644
index 00000000000..a3823270d04
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/OWNERS
@@ -0,0 +1,2 @@
+dljames@chromium.org
+dpenning@chromium.org
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.cc
new file mode 100644
index 00000000000..6fad2e28c28
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.cc
@@ -0,0 +1,266 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/reading_list/reading_list_api.h"
+
+#include "base/containers/flat_set.h"
+#include "base/time/time.h"
+#include "chrome/browser/extensions/api/reading_list/reading_list_api_constants.h"
+#include "chrome/browser/extensions/api/reading_list/reading_list_util.h"
+#include "chrome/browser/reading_list/reading_list_model_factory.h"
+#include "chrome/common/extensions/api/reading_list.h"
+#include "components/reading_list/core/reading_list_entry.h"
+#include "components/reading_list/core/reading_list_model.h"
+#include "components/reading_list/core/reading_list_model_observer.h"
+#include "extensions/browser/extension_function.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////// ReadingListAddEntryFunction //////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+ReadingListAddEntryFunction::ReadingListAddEntryFunction() = default;
+ReadingListAddEntryFunction::~ReadingListAddEntryFunction() = default;
+
+ExtensionFunction::ResponseAction ReadingListAddEntryFunction::Run() {
+ auto params = api::reading_list::AddEntry::Params::Create(args());
+ EXTENSION_FUNCTION_VALIDATE(params);
+
+ title_ = std::move(params->entry.title);
+ url_ = GURL(params->entry.url);
+ has_been_read_ = params->entry.has_been_read;
+
+ if (!url_.is_valid()) {
+ return RespondNow(Error(reading_list_api_constants::kInvalidURLError));
+ }
+
+ reading_list_model_ =
+ ReadingListModelFactory::GetForBrowserContext(browser_context());
+
+ if (!reading_list_model_->loaded()) {
+ reading_list_observation_.Observe(reading_list_model_);
+ AddRef();
+ return RespondLater();
+ }
+
+ auto response = AddEntryToReadingList();
+ return RespondNow(std::move(response));
+}
+
+void ReadingListAddEntryFunction::ReadingListModelLoaded(
+ const ReadingListModel* model) {
+ reading_list_observation_.Reset();
+ auto response = AddEntryToReadingList();
+ Respond(std::move(response));
+ Release(); // Balanced in Run().
+}
+
+ExtensionFunction::ResponseValue
+ReadingListAddEntryFunction::AddEntryToReadingList() {
+ if (!reading_list_model_->IsUrlSupported(url_)) {
+ return Error(reading_list_api_constants::kNotSupportedURLError);
+ }
+
+ if (reading_list_model_->GetEntryByURL(url_)) {
+ return Error(reading_list_api_constants::kDuplicateURLError);
+ }
+
+ reading_list_model_->AddOrReplaceEntry(
+ url_, title_, reading_list::EntrySource::ADDED_VIA_EXTENSION,
+ /*estimated_read_time=*/base::TimeDelta());
+ reading_list_model_->SetReadStatusIfExists(url_, has_been_read_);
+
+ return NoArguments();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////// ReadingListRemoveEntryFunction /////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+ReadingListRemoveEntryFunction::ReadingListRemoveEntryFunction() = default;
+ReadingListRemoveEntryFunction::~ReadingListRemoveEntryFunction() = default;
+
+ExtensionFunction::ResponseAction ReadingListRemoveEntryFunction::Run() {
+ auto params = api::reading_list::RemoveEntry::Params::Create(args());
+ EXTENSION_FUNCTION_VALIDATE(params);
+
+ url_ = GURL(params->info.url);
+ if (!url_.is_valid()) {
+ return RespondNow(Error(reading_list_api_constants::kInvalidURLError));
+ }
+
+ reading_list_model_ =
+ ReadingListModelFactory::GetForBrowserContext(browser_context());
+
+ if (!reading_list_model_->loaded()) {
+ reading_list_observation_.Observe(reading_list_model_);
+ AddRef();
+ return RespondLater();
+ }
+
+ auto response = RemoveEntryFromReadingList();
+ return RespondNow(std::move(response));
+}
+
+void ReadingListRemoveEntryFunction::ReadingListModelLoaded(
+ const ReadingListModel* model) {
+ reading_list_observation_.Reset();
+ auto response = RemoveEntryFromReadingList();
+ Respond(std::move(response));
+ Release(); // Balanced in Run().
+}
+
+ExtensionFunction::ResponseValue
+ReadingListRemoveEntryFunction::RemoveEntryFromReadingList() {
+ if (!reading_list_model_->IsUrlSupported(url_)) {
+ return Error(reading_list_api_constants::kNotSupportedURLError);
+ }
+
+ if (!reading_list_model_->GetEntryByURL(url_)) {
+ return Error(reading_list_api_constants::kURLNotFoundError);
+ }
+
+ reading_list_model_->RemoveEntryByURL(url_);
+
+ return NoArguments();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////// ReadingListUpdateEntryFunction /////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+ReadingListUpdateEntryFunction::ReadingListUpdateEntryFunction() = default;
+ReadingListUpdateEntryFunction::~ReadingListUpdateEntryFunction() = default;
+
+ExtensionFunction::ResponseAction ReadingListUpdateEntryFunction::Run() {
+ auto params = api::reading_list::UpdateEntry::Params::Create(args());
+ EXTENSION_FUNCTION_VALIDATE(params);
+
+ title_ = params->info.title;
+ has_been_read_ = params->info.has_been_read;
+
+ if (!title_.has_value() && !has_been_read_.has_value()) {
+ return RespondNow(Error(reading_list_api_constants::kNoUpdateProvided));
+ }
+
+ url_ = GURL(params->info.url);
+ if (!url_.is_valid()) {
+ return RespondNow(Error(reading_list_api_constants::kInvalidURLError));
+ }
+
+ reading_list_model_ =
+ ReadingListModelFactory::GetForBrowserContext(browser_context());
+
+ if (!reading_list_model_->loaded()) {
+ reading_list_observation_.Observe(reading_list_model_);
+ AddRef();
+ return RespondLater();
+ }
+
+ auto response = UpdateEntriesInTheReadingList();
+ return RespondNow(std::move(response));
+}
+
+void ReadingListUpdateEntryFunction::ReadingListModelLoaded(
+ const ReadingListModel* model) {
+ reading_list_observation_.Reset();
+ auto response = UpdateEntriesInTheReadingList();
+ Respond(std::move(response));
+ Release(); // Balanced in Run().
+}
+
+ExtensionFunction::ResponseValue
+ReadingListUpdateEntryFunction::UpdateEntriesInTheReadingList() {
+ if (!reading_list_model_->IsUrlSupported(url_)) {
+ return Error(reading_list_api_constants::kNotSupportedURLError);
+ }
+
+ if (!reading_list_model_->GetEntryByURL(url_)) {
+ return Error(reading_list_api_constants::kURLNotFoundError);
+ }
+
+ if (title_.has_value()) {
+ reading_list_model_->SetEntryTitleIfExists(url_, title_.value());
+ }
+
+ if (has_been_read_.has_value()) {
+ reading_list_model_->SetReadStatusIfExists(url_, has_been_read_.value());
+ }
+
+ return NoArguments();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////// ReadingListQueryFunction ///////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+ReadingListQueryFunction::ReadingListQueryFunction() = default;
+ReadingListQueryFunction::~ReadingListQueryFunction() = default;
+
+ExtensionFunction::ResponseAction ReadingListQueryFunction::Run() {
+ auto params = api::reading_list::Query::Params::Create(args());
+ EXTENSION_FUNCTION_VALIDATE(params);
+
+ if (params->info.url.has_value()) {
+ url_ = GURL(params->info.url.value());
+ if (!url_->is_valid()) {
+ return RespondNow(Error(reading_list_api_constants::kInvalidURLError));
+ }
+ }
+
+ title_ = params->info.title;
+ has_been_read_ = params->info.has_been_read;
+
+ reading_list_model_ =
+ ReadingListModelFactory::GetForBrowserContext(browser_context());
+
+ if (!reading_list_model_->loaded()) {
+ reading_list_observation_.Observe(reading_list_model_);
+ AddRef();
+ return RespondLater();
+ }
+
+ auto response = MatchEntries();
+ return RespondNow(std::move(response));
+}
+
+void ReadingListQueryFunction::ReadingListModelLoaded(
+ const ReadingListModel* model) {
+ reading_list_observation_.Reset();
+ auto response = MatchEntries();
+ Respond(std::move(response));
+ Release(); // Balanced in Run().
+}
+
+ExtensionFunction::ResponseValue ReadingListQueryFunction::MatchEntries() {
+ if (url_.has_value() && !reading_list_model_->IsUrlSupported(url_.value())) {
+ return Error(reading_list_api_constants::kNotSupportedURLError);
+ }
+
+ base::flat_set<GURL> urls = reading_list_model_->GetKeys();
+ std::vector<api::reading_list::ReadingListEntry> matching_entries;
+
+ for (const auto& url : urls) {
+ scoped_refptr<const ReadingListEntry> entry =
+ reading_list_model_->GetEntryByURL(url);
+
+ if (url_.has_value() && entry->URL() != url_) {
+ continue;
+ }
+ if (title_.has_value() && entry->Title() != title_) {
+ continue;
+ }
+ if (has_been_read_.has_value() && entry->IsRead() != has_been_read_) {
+ continue;
+ }
+ matching_entries.emplace_back(reading_list_util::ParseEntry(*entry));
+ }
+
+ return ArgumentList(
+ api::reading_list::Query::Results::Create(std::move(matching_entries)));
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.h b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.h
new file mode 100644
index 00000000000..74288697206
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.h
@@ -0,0 +1,133 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_H_
+
+#include "base/scoped_observation.h"
+#include "components/reading_list/core/reading_list_model.h"
+#include "components/reading_list/core/reading_list_model_observer.h"
+#include "extensions/browser/extension_function.h"
+
+namespace extensions {
+
+class ReadingListAddEntryFunction : public ExtensionFunction,
+ public ReadingListModelObserver {
+ public:
+ DECLARE_EXTENSION_FUNCTION("readingList.addEntry", READINGLIST_ADDENTRY)
+
+ ReadingListAddEntryFunction();
+ ReadingListAddEntryFunction(const ReadingListAddEntryFunction&) = delete;
+ ReadingListAddEntryFunction& operator=(const ReadingListAddEntryFunction&) =
+ delete;
+
+ // ExtensionFunction:
+ ResponseAction Run() override;
+
+ private:
+ ~ReadingListAddEntryFunction() override;
+
+ ResponseValue AddEntryToReadingList();
+
+ // ReadingListModelObserver:
+ void ReadingListModelLoaded(const ReadingListModel* model) override;
+
+ base::ScopedObservation<ReadingListModel, ReadingListModelObserver>
+ reading_list_observation_{this};
+ raw_ptr<ReadingListModel> reading_list_model_;
+ GURL url_;
+ std::string title_;
+ bool has_been_read_;
+};
+
+class ReadingListRemoveEntryFunction : public ExtensionFunction,
+ public ReadingListModelObserver {
+ public:
+ DECLARE_EXTENSION_FUNCTION("readingList.removeEntry", READINGLIST_REMOVEENTRY)
+
+ ReadingListRemoveEntryFunction();
+ ReadingListRemoveEntryFunction(const ReadingListRemoveEntryFunction&) =
+ delete;
+ ReadingListRemoveEntryFunction& operator=(
+ const ReadingListRemoveEntryFunction&) = delete;
+
+ // ExtensionFunction:
+ ResponseAction Run() override;
+
+ private:
+ ~ReadingListRemoveEntryFunction() override;
+
+ ResponseValue RemoveEntryFromReadingList();
+
+ // ReadingListModelObserver:
+ void ReadingListModelLoaded(const ReadingListModel* model) override;
+
+ base::ScopedObservation<ReadingListModel, ReadingListModelObserver>
+ reading_list_observation_{this};
+ raw_ptr<ReadingListModel> reading_list_model_;
+ GURL url_;
+};
+
+class ReadingListUpdateEntryFunction : public ExtensionFunction,
+ public ReadingListModelObserver {
+ public:
+ DECLARE_EXTENSION_FUNCTION("readingList.updateEntry", READINGLIST_UPDATEENTRY)
+
+ ReadingListUpdateEntryFunction();
+ ReadingListUpdateEntryFunction(const ReadingListUpdateEntryFunction&) =
+ delete;
+ ReadingListUpdateEntryFunction& operator=(
+ const ReadingListUpdateEntryFunction&) = delete;
+
+ // ExtensionFunction:
+ ResponseAction Run() override;
+
+ private:
+ ~ReadingListUpdateEntryFunction() override;
+
+ ResponseValue UpdateEntriesInTheReadingList();
+
+ // ReadingListModelObserver:
+ void ReadingListModelLoaded(const ReadingListModel* model) override;
+
+ base::ScopedObservation<ReadingListModel, ReadingListModelObserver>
+ reading_list_observation_{this};
+ raw_ptr<ReadingListModel> reading_list_model_;
+ GURL url_;
+ absl::optional<std::string> title_;
+ absl::optional<bool> has_been_read_;
+};
+
+class ReadingListQueryFunction : public ExtensionFunction,
+ public ReadingListModelObserver {
+ public:
+ DECLARE_EXTENSION_FUNCTION("readingList.query", READINGLIST_QUERY)
+
+ ReadingListQueryFunction();
+ ReadingListQueryFunction(const ReadingListQueryFunction&) = delete;
+ ReadingListQueryFunction& operator=(const ReadingListQueryFunction&) = delete;
+
+ // ExtensionFunction:
+ ResponseAction Run() override;
+
+ private:
+ ~ReadingListQueryFunction() override;
+
+ // Returns the entries that match the provided features.
+ ResponseValue MatchEntries();
+
+ // ReadingListModelObserver:
+ void ReadingListModelLoaded(const ReadingListModel* model) override;
+
+ base::ScopedObservation<ReadingListModel, ReadingListModelObserver>
+ reading_list_observation_{this};
+ raw_ptr<ReadingListModel> reading_list_model_;
+ absl::optional<GURL> url_;
+ absl::optional<std::string> title_;
+ absl::optional<bool> has_been_read_;
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_H_
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.cc
new file mode 100644
index 00000000000..f96e06c941b
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.cc
@@ -0,0 +1,17 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/reading_list/reading_list_api_constants.h"
+
+namespace extensions::reading_list_api_constants {
+
+// Error messages.
+const char kInvalidURLError[] = "URL is not valid.";
+const char kNotSupportedURLError[] = "URL is not supported.";
+const char kDuplicateURLError[] = "Duplicate URL.";
+const char kURLNotFoundError[] = "URL not found.";
+const char kNoUpdateProvided[] =
+ "At least one of `title` or `hasBeenRead` must be provided.";
+
+} // namespace extensions::reading_list_api_constants
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.h b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.h
new file mode 100644
index 00000000000..6a384f89d10
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.h
@@ -0,0 +1,20 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_CONSTANTS_H_
+#define CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_CONSTANTS_H_
+
+// Constants used for the Reading List API.
+namespace extensions::reading_list_api_constants {
+
+// Error messages.
+extern const char kInvalidURLError[];
+extern const char kNotSupportedURLError[];
+extern const char kDuplicateURLError[];
+extern const char kURLNotFoundError[];
+extern const char kNoUpdateProvided[];
+
+} // namespace extensions::reading_list_api_constants
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_CONSTANTS_H_
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_unittest.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_unittest.cc
new file mode 100644
index 00000000000..de1b24b9173
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_unittest.cc
@@ -0,0 +1,538 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/reading_list/reading_list_api.h"
+
+#include <memory>
+
+#include "base/test/values_test_util.h"
+#include "base/time/time.h"
+#include "chrome/browser/extensions/api/reading_list/reading_list_api_constants.h"
+#include "chrome/browser/extensions/api/reading_list/reading_list_event_router.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/reading_list/reading_list_model_factory.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/extensions/api/reading_list.h"
+#include "chrome/test/base/test_browser_window.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/reading_list/core/reading_list_entry.h"
+#include "components/reading_list/core/reading_list_model.h"
+#include "components/reading_list/core/reading_list_test_utils.h"
+#include "components/version_info/channel.h"
+#include "content/public/browser/browser_context.h"
+#include "extensions/browser/api_test_utils.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/event_router_factory.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/test_event_router_observer.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/features/feature_channel.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+namespace {
+
+// Create an extension with "readingList" permission.
+scoped_refptr<const Extension> CreateReadingListExtension() {
+ return ExtensionBuilder("Extension with readingList permission")
+ .AddPermission("readingList")
+ .Build();
+}
+
+void AddReadingListEntry(ReadingListModel* reading_list_model,
+ const GURL& url,
+ const std::string& title,
+ bool has_been_read) {
+ reading_list_model->AddOrReplaceEntry(
+ url, title, reading_list::EntrySource::ADDED_VIA_CURRENT_APP,
+ base::TimeDelta());
+ reading_list_model->SetReadStatusIfExists(url, has_been_read);
+}
+
+std::unique_ptr<KeyedService> BuildReadingListEventRouter(
+ content::BrowserContext* context) {
+ return std::make_unique<ReadingListEventRouter>(context);
+}
+
+std::unique_ptr<KeyedService> BuildEventRouter(
+ content::BrowserContext* context) {
+ return std::make_unique<extensions::EventRouter>(
+ context, ExtensionPrefs::Get(context));
+}
+
+} // namespace
+
+class ReadingListApiUnitTest : public ExtensionServiceTestBase {
+ public:
+ ReadingListApiUnitTest() = default;
+ ReadingListApiUnitTest(const ReadingListApiUnitTest&) = delete;
+ ReadingListApiUnitTest& operator=(const ReadingListApiUnitTest&) = delete;
+ ~ReadingListApiUnitTest() override = default;
+
+ protected:
+ Browser* browser() { return browser_.get(); }
+ TestBrowserWindow* browser_window() { return browser_window_.get(); }
+
+ private:
+ void SetUp() override;
+ void TearDown() override;
+
+ std::unique_ptr<TestBrowserWindow> browser_window_;
+ std::unique_ptr<Browser> browser_;
+ ScopedCurrentChannel channel_{version_info::Channel::UNKNOWN};
+};
+
+void ReadingListApiUnitTest::SetUp() {
+ ExtensionServiceTestBase::SetUp();
+ InitializeEmptyExtensionService();
+
+ // Create a browser window.
+ browser_window_ = std::make_unique<TestBrowserWindow>();
+ Browser::CreateParams params(profile(), /*user_gesture*/ true);
+ params.type = Browser::TYPE_NORMAL;
+ params.window = browser_window_.get();
+ browser_ = std::unique_ptr<Browser>(Browser::Create(params));
+
+ ReadingListEventRouter::GetFactoryInstance()->SetTestingFactory(
+ browser_context(), base::BindRepeating(&BuildReadingListEventRouter));
+
+ EventRouterFactory::GetInstance()->SetTestingFactory(
+ browser_context(), base::BindRepeating(&BuildEventRouter));
+
+ // We need to call ReadingListEventRouterFactory::GetForProfile() in order to
+ // instantiate the keyed service, since it's not created by default in unit
+ // tests.
+ ReadingListEventRouter::Get(browser_context());
+}
+
+void ReadingListApiUnitTest::TearDown() {
+ browser_->tab_strip_model()->CloseAllTabs();
+ browser_.reset();
+ browser_window_.reset();
+ ExtensionServiceTestBase::TearDown();
+}
+
+// Test that it is possible to add a unique URL.
+TEST_F(ReadingListApiUnitTest, AddUniqueURL) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ static constexpr char kArgs[] =
+ R"([{
+ "url": "https://www.example.com",
+ "title": "example of title",
+ "hasBeenRead": false
+ }])";
+ auto function = base::MakeRefCounted<ReadingListAddEntryFunction>();
+ function->set_extension(extension);
+ ReadingListModel* reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ // Add the entry.
+ api_test_utils::RunFunction(function.get(), kArgs, profile(),
+ api_test_utils::FunctionMode::kNone);
+
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ // Verify the features of the entry.
+ GURL url = GURL("https://www.example.com");
+ auto entry = reading_list_model->GetEntryByURL(url);
+ EXPECT_EQ(entry->URL(), url);
+ EXPECT_EQ(entry->Title(), "example of title");
+ EXPECT_FALSE(entry->IsRead());
+}
+
+// Test that it is possible to add an already read entry.
+TEST_F(ReadingListApiUnitTest, AddEntryThatHasBeenRead) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ static constexpr char kArgs[] =
+ R"([{
+ "url": "https://www.example.com",
+ "title": "example of title",
+ "hasBeenRead": true
+ }])";
+ auto function = base::MakeRefCounted<ReadingListAddEntryFunction>();
+ function->set_extension(extension);
+ ReadingListModel* reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ // Add the entry.
+ api_test_utils::RunFunction(function.get(), kArgs, profile(),
+ api_test_utils::FunctionMode::kNone);
+
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ // Verify the features of the entry.
+ GURL url = GURL("https://www.example.com");
+ auto entry = reading_list_model->GetEntryByURL(url);
+ EXPECT_EQ(entry->URL(), url);
+ EXPECT_EQ(entry->Title(), "example of title");
+ EXPECT_TRUE(entry->IsRead());
+}
+
+// Test that adding a duplicate URL generates an error.
+TEST_F(ReadingListApiUnitTest, AddDuplicateURL) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ static constexpr char kArgs[] =
+ R"([{
+ "url": "https://www.example.com",
+ "title": "example of title",
+ "hasBeenRead": false
+ }])";
+ auto function = base::MakeRefCounted<ReadingListAddEntryFunction>();
+ function->set_extension(extension);
+ ReadingListModel* reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ // Add the entry.
+ api_test_utils::RunFunction(function.get(), kArgs, profile(),
+ api_test_utils::FunctionMode::kNone);
+
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ // Verify the features of the entry.
+ GURL url = GURL("https://www.example.com");
+ auto entry = reading_list_model->GetEntryByURL(url);
+ EXPECT_EQ(entry->URL(), url);
+ EXPECT_EQ(entry->Title(), "example of title");
+ EXPECT_FALSE(entry->IsRead());
+
+ // Try to add a duplicate URL and expect an error.
+ function = base::MakeRefCounted<ReadingListAddEntryFunction>();
+ function->set_extension(extension);
+ std::string error = api_test_utils::RunFunctionAndReturnError(
+ function.get(), kArgs, profile(), api_test_utils::FunctionMode::kNone);
+ EXPECT_EQ(error, reading_list_api_constants::kDuplicateURLError);
+
+ // Review that the URL added earlier still exists and there is only 1 entry in
+ // the Reading List.
+ EXPECT_EQ(reading_list_model->size(), 1u);
+ entry = reading_list_model->GetEntryByURL(url);
+ EXPECT_EQ(entry->URL(), url);
+ EXPECT_EQ(entry->Title(), "example of title");
+ EXPECT_FALSE(entry->IsRead());
+}
+
+// Test that it is possible to remove a URL.
+TEST_F(ReadingListApiUnitTest, RemoveURL) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ ReadingListModel* reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ ReadingListLoadObserver(reading_list_model).Wait();
+
+ AddReadingListEntry(reading_list_model, GURL("https://www.example.com"),
+ "example of title", /*has_been_read=*/false);
+
+ // Verify that the entry has been added.
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ // Remove the URL that was added before.
+ auto remove_function = base::MakeRefCounted<ReadingListRemoveEntryFunction>();
+ remove_function->set_extension(extension);
+ static constexpr char kArgs[] =
+ R"([{
+ "url": "https://www.example.com"
+ }])";
+ api_test_utils::RunFunction(remove_function.get(), kArgs, profile(),
+ api_test_utils::FunctionMode::kNone);
+
+ // Verify the size of the reading list model.
+ EXPECT_EQ(reading_list_model->size(), 0u);
+}
+
+// Test that trying to remove a URL that is not in the Reading List, generates
+// an error.
+TEST_F(ReadingListApiUnitTest, RemoveNonExistentURL) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ static constexpr char kArgs[] =
+ R"([{
+ "url": "https://www.example.com"
+ }])";
+ auto function = base::MakeRefCounted<ReadingListRemoveEntryFunction>();
+ function->set_extension(extension);
+
+ // Remove the entry.
+ std::string error = api_test_utils::RunFunctionAndReturnError(
+ function.get(), kArgs, profile(), api_test_utils::FunctionMode::kNone);
+ EXPECT_EQ(error, reading_list_api_constants::kURLNotFoundError);
+}
+
+// Test that it is possible to update the features of an entry.
+TEST_F(ReadingListApiUnitTest, UpdateEntryFeatures) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ ReadingListModel* reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ ReadingListLoadObserver(reading_list_model).Wait();
+
+ AddReadingListEntry(reading_list_model, GURL("https://www.example.com"),
+ "example of title", /*has_been_read=*/false);
+
+ // Verify that the entry has been added.
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ // Update the entry that was added before.
+ auto update_function = base::MakeRefCounted<ReadingListUpdateEntryFunction>();
+ update_function->set_extension(extension);
+ static constexpr char kArgs[] =
+ R"([{
+ "url": "https://www.example.com",
+ "title": "Title",
+ "hasBeenRead": true
+ }])";
+ api_test_utils::RunFunction(update_function.get(), kArgs, profile(),
+ api_test_utils::FunctionMode::kNone);
+
+ // Verify that the size of the reading list model is still the same.
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ // Verify the features of the entry.
+ GURL url = GURL("https://www.example.com");
+ auto entry = reading_list_model->GetEntryByURL(url);
+ EXPECT_EQ(entry->URL(), url);
+ EXPECT_EQ(entry->Title(), "Title");
+ EXPECT_TRUE(entry->IsRead());
+}
+
+// Test that trying to update an entry by providing only the URL, generates an
+// error.
+TEST_F(ReadingListApiUnitTest, UpdateEntryOnlyWithTheURL) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ ReadingListModel* reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ ReadingListLoadObserver(reading_list_model).Wait();
+
+ AddReadingListEntry(reading_list_model, GURL("https://www.example.com"),
+ "example of title", /*has_been_read=*/false);
+
+ // Verify that the entry has been added.
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ // Update the entry that was added before.
+ auto update_function = base::MakeRefCounted<ReadingListUpdateEntryFunction>();
+ update_function->set_extension(extension);
+ static constexpr char kArgs[] =
+ R"([{
+ "url": "https://www.example.com",
+ }])";
+ std::string error = api_test_utils::RunFunctionAndReturnError(
+ update_function.get(), kArgs, profile(),
+ api_test_utils::FunctionMode::kNone);
+ EXPECT_EQ(error, reading_list_api_constants::kNoUpdateProvided);
+
+ // Verify that the size of the reading list model is still the same.
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ // Verify the features of the entry.
+ GURL url = GURL("https://www.example.com");
+ auto entry = reading_list_model->GetEntryByURL(url);
+ EXPECT_EQ(entry->URL(), url);
+ EXPECT_EQ(entry->Title(), "example of title");
+ EXPECT_FALSE(entry->IsRead());
+}
+
+// Test that it is possible to retrieve all the entries.
+TEST_F(ReadingListApiUnitTest, RetrieveAllEntries) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ ReadingListModel* reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ ReadingListLoadObserver(reading_list_model).Wait();
+
+ AddReadingListEntry(reading_list_model, GURL("https://www.example.com"),
+ "example of title", /*has_been_read=*/false);
+ AddReadingListEntry(reading_list_model, GURL("https://www.example2.com"),
+ "Title #2", /*has_been_read=*/false);
+
+ // Verify that the entries have been added.
+ EXPECT_EQ(reading_list_model->size(), 2u);
+
+ // Retrieve all the entries in the Reading List.
+ auto update_function = base::MakeRefCounted<ReadingListQueryFunction>();
+ update_function->set_extension(extension);
+ static constexpr char kArgs[] = "[{}]";
+
+ auto entries = api_test_utils::RunFunctionAndReturnSingleResult(
+ update_function.get(), kArgs, profile(),
+ api_test_utils::FunctionMode::kNone);
+
+ // Verify that all the entries were retrieved.
+ EXPECT_EQ(entries.value().GetList().size(), 2u);
+
+ // Verify that the size of the reading list model is still the same.
+ EXPECT_EQ(reading_list_model->size(), 2u);
+}
+
+// Test that it is possible to retrieve entries with certain features.
+TEST_F(ReadingListApiUnitTest, RetrieveCertainEntries) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ ReadingListModel* reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ ReadingListLoadObserver(reading_list_model).Wait();
+
+ AddReadingListEntry(reading_list_model, GURL("https://www.example.com"),
+ "example of title", /*has_been_read=*/false);
+ AddReadingListEntry(reading_list_model, GURL("https://www.example2.com"),
+ "Example", /*has_been_read=*/false);
+ AddReadingListEntry(reading_list_model, GURL("https://www.example3.com"),
+ "Example", /*has_been_read=*/false);
+
+ // Verify that the entries have been added.
+ EXPECT_EQ(reading_list_model->size(), 3u);
+
+ // Retrieve entries whose title is "Example".
+ auto update_function = base::MakeRefCounted<ReadingListQueryFunction>();
+ update_function->set_extension(extension);
+ static constexpr char kArgs[] =
+ R"([{
+ "title": "Example"
+ }])";
+ auto entries = api_test_utils::RunFunctionAndReturnSingleResult(
+ update_function.get(), kArgs, profile(),
+ api_test_utils::FunctionMode::kNone);
+
+ // Verify only 2 entries were retrieved: example_2 and example_3.
+ ASSERT_EQ(entries.value().GetList().size(), 2u);
+
+ scoped_refptr<const ReadingListEntry> e2 =
+ reading_list_model->GetEntryByURL(GURL("https://www.example2.com"));
+ scoped_refptr<const ReadingListEntry> e3 =
+ reading_list_model->GetEntryByURL(GURL("https://www.example3.com"));
+
+ // Expect that the first entry is equivalent to `e2`.
+ absl::optional<api::reading_list::ReadingListEntry> entry1_actual =
+ api::reading_list::ReadingListEntry::FromValue(entries->GetList()[0]);
+ int64_t e2_update_in_milliseconds =
+ base::Microseconds(e2->UpdateTime()).InMilliseconds();
+ int64_t e2_creation_in_milliseconds =
+ base::Microseconds(e2->CreationTime()).InMilliseconds();
+ EXPECT_EQ(entry1_actual->url, e2->URL().spec());
+ EXPECT_EQ(entry1_actual->title, e2->Title());
+ EXPECT_EQ(entry1_actual->has_been_read, e2->IsRead());
+ EXPECT_EQ(entry1_actual->last_update_time, e2_update_in_milliseconds);
+ EXPECT_EQ(entry1_actual->creation_time, e2_creation_in_milliseconds);
+
+ // Expect that the second entry is equivalent to `e3`.
+ absl::optional<api::reading_list::ReadingListEntry> entry2_actual =
+ api::reading_list::ReadingListEntry::FromValue(entries->GetList()[1]);
+ int64_t e3_update_in_milliseconds =
+ base::Microseconds(e3->UpdateTime()).InMilliseconds();
+ int64_t e3_creation_in_milliseconds =
+ base::Microseconds(e3->CreationTime()).InMilliseconds();
+ EXPECT_EQ(entry2_actual->url, e3->URL().spec());
+ EXPECT_EQ(entry2_actual->title, e3->Title());
+ EXPECT_EQ(entry2_actual->has_been_read, e3->IsRead());
+ EXPECT_EQ(entry2_actual->last_update_time, e3_update_in_milliseconds);
+ EXPECT_EQ(entry2_actual->creation_time, e3_creation_in_milliseconds);
+
+ EXPECT_EQ(reading_list_model->size(), 3u);
+}
+
+// Test that a query can return no matching entries.
+TEST_F(ReadingListApiUnitTest, NoEntriesRetrieved) {
+ scoped_refptr<const Extension> extension = CreateReadingListExtension();
+
+ ReadingListModel* reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ ReadingListLoadObserver(reading_list_model).Wait();
+
+ AddReadingListEntry(reading_list_model, GURL("https://www.example.com"),
+ "example of title", /*has_been_read=*/false);
+
+ // Query for an entry.
+ auto update_function = base::MakeRefCounted<ReadingListQueryFunction>();
+ update_function->set_extension(extension);
+ static constexpr char kArgs[] =
+ R"([{
+ "url": "https://www.example.com",
+ "title": "Title",
+ "hasBeenRead": false
+ }])";
+ auto entries = api_test_utils::RunFunctionAndReturnSingleResult(
+ update_function.get(), kArgs, profile(),
+ api_test_utils::FunctionMode::kNone);
+
+ EXPECT_EQ(entries.value().GetList().size(), 0u);
+}
+
+// Test that adding an entry generates an event.
+TEST_F(ReadingListApiUnitTest, ReadingListOnEntryAdded) {
+ TestEventRouterObserver event_observer(EventRouter::Get(browser_context()));
+
+ ReadingListModel* const reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ ReadingListLoadObserver(reading_list_model).Wait();
+
+ AddReadingListEntry(reading_list_model, GURL("https://www.example.com"),
+ "example of title", /*has_been_read=*/false);
+
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ EXPECT_TRUE(base::Contains(event_observer.events(),
+ api::reading_list::OnEntryAdded::kEventName));
+}
+
+// Test that removing an entry generates an event.
+TEST_F(ReadingListApiUnitTest, ReadingListOnEntryRemoved) {
+ ReadingListModel* const reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ ReadingListLoadObserver(reading_list_model).Wait();
+
+ const GURL url = GURL("https://www.example.com");
+
+ AddReadingListEntry(reading_list_model, url, "example of title",
+ /*has_been_read=*/false);
+ EXPECT_EQ(reading_list_model->size(), 1u);
+
+ TestEventRouterObserver event_observer(EventRouter::Get(browser_context()));
+
+ reading_list_model->RemoveEntryByURL(url);
+ EXPECT_EQ(reading_list_model->size(), 0u);
+
+ EXPECT_EQ(event_observer.events().size(), 1u);
+ EXPECT_TRUE(base::Contains(event_observer.events(),
+ api::reading_list::OnEntryRemoved::kEventName));
+}
+
+// Test that updating an entry generates an event.
+TEST_F(ReadingListApiUnitTest, ReadingListOnEntryUpdated) {
+ ReadingListModel* const reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+
+ ReadingListLoadObserver(reading_list_model).Wait();
+
+ const GURL url = GURL("https://www.example.com");
+
+ AddReadingListEntry(reading_list_model, url, "example of title",
+ /*has_been_read=*/false);
+ EXPECT_EQ(reading_list_model->size(), 1u);
+ EXPECT_EQ(reading_list_model->GetEntryByURL(url)->Title(),
+ "example of title");
+
+ TestEventRouterObserver event_observer(EventRouter::Get(browser_context()));
+
+ reading_list_model->SetEntryTitleIfExists(url, "New title");
+ EXPECT_EQ(reading_list_model->GetEntryByURL(url)->Title(), "New title");
+
+ EXPECT_EQ(event_observer.events().size(), 1u);
+ EXPECT_TRUE(base::Contains(event_observer.events(),
+ api::reading_list::OnEntryUpdated::kEventName));
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_apitest.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_apitest.cc
new file mode 100644
index 00000000000..4bc8e8034bb
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_apitest.cc
@@ -0,0 +1,67 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/reading_list/reading_list_model_factory.h"
+#include "chrome/common/extensions/api/reading_list.h"
+#include "components/reading_list/core/reading_list_model.h"
+#include "content/public/test/browser_test.h"
+#include "extensions/browser/test_event_router_observer.h"
+
+namespace extensions {
+
+namespace {
+
+using ReadingListApiTest = ExtensionApiTest;
+
+IN_PROC_BROWSER_TEST_F(ReadingListApiTest, TestReadingListWorks) {
+ ASSERT_TRUE(RunExtensionTest("reading_list")) << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(ReadingListApiTest,
+ TestReadingListEventsAcrossProfiles) {
+ // The EventRouter is shared between on- and off-the-record profiles, so
+ // this observer will catch events for each.
+ TestEventRouterObserver event_observer(EventRouter::Get(profile()));
+
+ // Add a Reading List entry in a normal browser.
+ ReadingListModel* const reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(profile());
+ reading_list_model->AddOrReplaceEntry(
+ GURL("https://www.example.com"), "example of title",
+ reading_list::EntrySource::ADDED_VIA_CURRENT_APP, base::TimeDelta());
+
+ ASSERT_TRUE(base::Contains(event_observer.events(),
+ api::reading_list::OnEntryAdded::kEventName));
+ Event* normal_event = event_observer.events()
+ .at(api::reading_list::OnEntryAdded::kEventName)
+ .get();
+ EXPECT_EQ(normal_event->restrict_to_browser_context, profile());
+
+ event_observer.ClearEvents();
+ ASSERT_FALSE(base::Contains(event_observer.events(),
+ api::reading_list::OnEntryAdded::kEventName));
+
+ const Browser* const incognito_browser = CreateIncognitoBrowser(profile());
+
+ // Add a Reading List entry in an incognito browser.
+ ReadingListModel* const incognito_reading_list_model =
+ ReadingListModelFactory::GetForBrowserContext(
+ incognito_browser->profile());
+ incognito_reading_list_model->AddOrReplaceEntry(
+ GURL("https://www.example.com"), "example of title",
+ reading_list::EntrySource::ADDED_VIA_CURRENT_APP, base::TimeDelta());
+
+ ASSERT_TRUE(base::Contains(event_observer.events(),
+ api::reading_list::OnEntryAdded::kEventName));
+ Event* incognito_event = event_observer.events()
+ .at(api::reading_list::OnEntryAdded::kEventName)
+ .get();
+ EXPECT_EQ(incognito_event->restrict_to_browser_context,
+ incognito_browser->profile());
+}
+
+} // namespace
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.cc
new file mode 100644
index 00000000000..32d1fa3935d
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.cc
@@ -0,0 +1,143 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/reading_list/reading_list_event_router.h"
+
+#include "base/no_destructor.h"
+#include "chrome/browser/extensions/api/reading_list/reading_list_util.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+#include "chrome/browser/reading_list/reading_list_model_factory.h"
+#include "chrome/common/extensions/api/reading_list.h"
+#include "components/reading_list/core/reading_list_entry.h"
+#include "components/reading_list/core/reading_list_model.h"
+#include "content/public/browser/browser_context.h"
+#include "extensions/browser/event_router_factory.h"
+
+namespace extensions {
+
+namespace {
+
+// The factory responsible for creating the per-profile event router for the
+// ReadingList API.
+class ReadingListEventRouterFactory : public ProfileKeyedServiceFactory {
+ public:
+ ReadingListEventRouterFactory();
+ ReadingListEventRouterFactory(const ReadingListEventRouterFactory&) = delete;
+ ReadingListEventRouterFactory& operator=(
+ const ReadingListEventRouterFactory&) = delete;
+ ~ReadingListEventRouterFactory() override = default;
+
+ // Given a browser context, returns the corresponding ReadingListEventRouter.
+ ReadingListEventRouter* GetForProfile(content::BrowserContext* context);
+
+ private:
+ // BrowserContextKeyedServiceFactory:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* profile) const override;
+ bool ServiceIsCreatedWithBrowserContext() const override;
+ bool ServiceIsNULLWhileTesting() const override;
+};
+
+ReadingListEventRouterFactory::ReadingListEventRouterFactory()
+ : ProfileKeyedServiceFactory(
+ "ReadingListEventRouter",
+ ProfileSelections::BuildForRegularAndIncognito()) {
+ DependsOn(EventRouterFactory::GetInstance());
+ DependsOn(ReadingListModelFactory::GetInstance());
+}
+
+ReadingListEventRouter* ReadingListEventRouterFactory::GetForProfile(
+ content::BrowserContext* context) {
+ return static_cast<ReadingListEventRouter*>(
+ GetServiceForBrowserContext(context, /*create=*/true));
+}
+
+KeyedService* ReadingListEventRouterFactory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ return new ReadingListEventRouter(context);
+}
+
+bool ReadingListEventRouterFactory::ServiceIsCreatedWithBrowserContext() const {
+ return true;
+}
+
+// Since there is a dependency on `EventRouter` that is null by default in unit
+// tests, this service needs to be null as well. If we want to enable it in a
+// specific test we need to override the factories for both `EventRouter` and
+// this factory to enforce the service creation.
+bool ReadingListEventRouterFactory::ServiceIsNULLWhileTesting() const {
+ return true;
+}
+
+} // namespace
+
+ReadingListEventRouter::ReadingListEventRouter(
+ content::BrowserContext* browser_context)
+ : reading_list_model_(
+ ReadingListModelFactory::GetForBrowserContext(browser_context)),
+ profile_(Profile::FromBrowserContext(browser_context)),
+ event_router_(EventRouter::Get(browser_context)) {
+ reading_list_observation_.Observe(reading_list_model_);
+}
+
+ReadingListEventRouter::~ReadingListEventRouter() = default;
+
+// static
+ReadingListEventRouter* ReadingListEventRouter::Get(
+ content::BrowserContext* browser_context) {
+ return static_cast<ReadingListEventRouterFactory*>(GetFactoryInstance())
+ ->GetForProfile(browser_context);
+}
+
+// static
+ProfileKeyedServiceFactory* ReadingListEventRouter::GetFactoryInstance() {
+ static base::NoDestructor<ReadingListEventRouterFactory> factory;
+ return factory.get();
+}
+
+void ReadingListEventRouter::ReadingListDidAddEntry(
+ const ReadingListModel* model,
+ const GURL& url,
+ reading_list::EntrySource source) {
+ auto args(api::reading_list::OnEntryAdded::Create(
+ reading_list_util::ParseEntry(*model->GetEntryByURL(url))));
+
+ DispatchEvent(events::READING_LIST_ON_ENTRY_ADDED,
+ api::reading_list::OnEntryAdded::kEventName, std::move(args));
+}
+
+void ReadingListEventRouter::ReadingListWillRemoveEntry(
+ const ReadingListModel* model,
+ const GURL& url) {
+ auto args(api::reading_list::OnEntryRemoved::Create(
+ reading_list_util::ParseEntry(*model->GetEntryByURL(url))));
+
+ // Even though we dispatch the event in ReadingListWillRemoveEntry() (i.e.,
+ // the entry is still in the model at this point), we can safely dispatch it
+ // as `onEntryRemoved` (past tense) to the extension. The entry is removed
+ // synchronously after this, so there's no way the extension could still
+ // see the entry in the list.
+ DispatchEvent(events::READING_LIST_ON_ENTRY_REMOVED,
+ api::reading_list::OnEntryRemoved::kEventName, std::move(args));
+}
+
+void ReadingListEventRouter::ReadingListDidUpdateEntry(
+ const ReadingListModel* model,
+ const GURL& url) {
+ auto args(api::reading_list::OnEntryUpdated::Create(
+ reading_list_util::ParseEntry(*model->GetEntryByURL(url))));
+
+ DispatchEvent(events::READING_LIST_ON_ENTRY_UPDATED,
+ api::reading_list::OnEntryUpdated::kEventName, std::move(args));
+}
+
+void ReadingListEventRouter::DispatchEvent(
+ events::HistogramValue histogram_value,
+ const std::string& event_name,
+ base::Value::List args) {
+ event_router_->BroadcastEvent(std::make_unique<Event>(
+ histogram_value, event_name, std::move(args), profile_));
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.h b/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.h
new file mode 100644
index 00000000000..20e8e8427f8
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.h
@@ -0,0 +1,67 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_EVENT_ROUTER_H_
+#define CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_EVENT_ROUTER_H_
+
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/reading_list/core/reading_list_entry.h"
+#include "components/reading_list/core/reading_list_model_observer.h"
+#include "content/public/browser/browser_context.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_event_histogram_value.h"
+
+class ProfileKeyedServiceFactory;
+
+namespace extensions {
+
+// The ReadingListEventRouter listens for reading list events and notifies
+// observers of the changes.
+class ReadingListEventRouter : public KeyedService,
+ public ReadingListModelObserver {
+ public:
+ explicit ReadingListEventRouter(content::BrowserContext* browser_context);
+ ReadingListEventRouter(const ReadingListEventRouter&) = delete;
+ ReadingListEventRouter& operator=(const ReadingListEventRouter&) = delete;
+ ~ReadingListEventRouter() override;
+
+ static ReadingListEventRouter* Get(content::BrowserContext* browser_context);
+
+ static ProfileKeyedServiceFactory* GetFactoryInstance();
+
+ private:
+ // ReadingListModelObserver:
+ void ReadingListModelLoaded(const ReadingListModel* model) override{};
+ void ReadingListDidAddEntry(const ReadingListModel* model,
+ const GURL& url,
+ reading_list::EntrySource source) override;
+ void ReadingListWillRemoveEntry(const ReadingListModel* model,
+ const GURL& url) override;
+ void ReadingListDidUpdateEntry(const ReadingListModel* model,
+ const GURL& url) override;
+
+ void DispatchEvent(events::HistogramValue histogram_value,
+ const std::string& event_name,
+ base::Value::List args);
+
+ base::ScopedObservation<ReadingListModel, ReadingListModelObserver>
+ reading_list_observation_{this};
+
+ // Guaranteed to outlive this object since it is declared as a KeyedService
+ // dependency.
+ raw_ptr<ReadingListModel> reading_list_model_;
+
+ // Guaranteed to outlive this object since it is declared as a KeyedService
+ // dependency.
+ raw_ptr<Profile> const profile_;
+
+ // Notifies observers of events associated with the profile. Guaranteed to
+ // outlive this object since it is declared as a KeyedService dependency.
+ raw_ptr<EventRouter> const event_router_;
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_EVENT_ROUTER_H_
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.cc
new file mode 100644
index 00000000000..7c4db3edbf9
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.cc
@@ -0,0 +1,24 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/reading_list/reading_list_util.h"
+#include "base/time/time.h"
+
+namespace extensions::reading_list_util {
+
+api::reading_list::ReadingListEntry ParseEntry(const ReadingListEntry& entry) {
+ api::reading_list::ReadingListEntry reading_list_entry;
+
+ reading_list_entry.url = entry.URL().spec();
+ reading_list_entry.title = entry.Title();
+ reading_list_entry.has_been_read = entry.IsRead();
+ reading_list_entry.last_update_time =
+ base::Microseconds(entry.UpdateTime()).InMilliseconds();
+ reading_list_entry.creation_time =
+ base::Microseconds(entry.CreationTime()).InMilliseconds();
+
+ return reading_list_entry;
+}
+
+} // namespace extensions::reading_list_util
diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.h b/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.h
new file mode 100644
index 00000000000..ad50c345379
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.h
@@ -0,0 +1,18 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_UTIL_H_
+#define CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_UTIL_H_
+
+#include "chrome/common/extensions/api/reading_list.h"
+#include "components/reading_list/core/reading_list_entry.h"
+
+namespace extensions::reading_list_util {
+
+// Converts from ReadingListEntry to api::reading_list::ReadingListEntry.
+api::reading_list::ReadingListEntry ParseEntry(const ReadingListEntry& entry);
+
+} // namespace extensions::reading_list_util
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_UTIL_H_
diff --git a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.cc b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.cc
index 4c807188e08..6b2aaa005f3 100644
--- a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.cc
+++ b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.cc
@@ -48,9 +48,10 @@ SafeBrowsingPrivateEventRouterFactory::SafeBrowsingPrivateEventRouterFactory()
SafeBrowsingPrivateEventRouterFactory::
~SafeBrowsingPrivateEventRouterFactory() = default;
-KeyedService* SafeBrowsingPrivateEventRouterFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+SafeBrowsingPrivateEventRouterFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
- return new SafeBrowsingPrivateEventRouter(context);
+ return std::make_unique<SafeBrowsingPrivateEventRouter>(context);
}
bool SafeBrowsingPrivateEventRouterFactory::ServiceIsCreatedWithBrowserContext()
diff --git a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h
index 4c85cb71b32..f8cfef70def 100644
--- a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h
+++ b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h
@@ -43,7 +43,7 @@ class SafeBrowsingPrivateEventRouterFactory
~SafeBrowsingPrivateEventRouterFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc
index ecd466ce7ec..2756fbec578 100644
--- a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc
+++ b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc
@@ -101,6 +101,11 @@ safe_browsing_private::ReferrerChainEntry ReferrerToReferrerChainEntry(
entry.navigation_initiation = safe_browsing_private::
NAVIGATION_INITIATION_COPY_PASTE_USER_INITIATED;
break;
+ case safe_browsing::
+ ReferrerChainEntry_NavigationInitiation_NOTIFICATION_INITIATED:
+ entry.navigation_initiation =
+ safe_browsing_private::NAVIGATION_INITIATION_NOTIFICATION_INITIATED;
+ break;
case safe_browsing::ReferrerChainEntry_NavigationInitiation_UNDEFINED:
NOTREACHED();
}
diff --git a/chromium/chrome/browser/extensions/api/scripting/scripting_api.cc b/chromium/chrome/browser/extensions/api/scripting/scripting_api.cc
index 77d0b1ccb6d..9459dec59b0 100644
--- a/chromium/chrome/browser/extensions/api/scripting/scripting_api.cc
+++ b/chromium/chrome/browser/extensions/api/scripting/scripting_api.cc
@@ -16,11 +16,10 @@
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/common/extensions/api/scripting.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "extensions/browser/api/scripting/scripting_constants.h"
+#include "extensions/browser/api/scripting/scripting_utils.h"
#include "extensions/browser/extension_api_frame_id_map.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "extensions/browser/extension_system.h"
@@ -37,8 +36,8 @@
#include "extensions/common/mojom/execution_world.mojom-shared.h"
#include "extensions/common/mojom/host_id.mojom.h"
#include "extensions/common/mojom/run_location.mojom-shared.h"
-#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/user_script.h"
#include "extensions/common/utils/content_script_utils.h"
#include "extensions/common/utils/extension_types_utils.h"
@@ -51,9 +50,6 @@ constexpr char kDuplicateFileSpecifiedError[] =
"Duplicate file specified: '*'.";
constexpr char kExactlyOneOfCssAndFilesError[] =
"Exactly one of 'css' and 'files' must be specified.";
-constexpr char kFilesExceededSizeLimitError[] =
- "Scripts could not be loaded because '*' exceeds the maximum script size "
- "or the extension's maximum total script size.";
// Note: CSS always injects as soon as possible, so we default to
// document_start. Because of tab loading, there's no guarantee this will
@@ -500,37 +496,12 @@ std::unique_ptr<UserScript> ParseUserScript(
return result;
}
-ValidateContentScriptsResult ValidateParsedScriptsOnFileThread(
- ExtensionResource::SymlinkPolicy symlink_policy,
- std::unique_ptr<UserScriptList> scripts) {
- DCHECK(GetExtensionFileTaskRunner()->RunsTasksInCurrentSequence());
-
- // Validate that claimed script resources actually exist, and are UTF-8
- // encoded.
- std::string error;
- std::vector<InstallWarning> warnings;
- bool are_script_files_valid = script_parsing::ValidateFileSources(
- *scripts, symlink_policy, &error, &warnings);
-
- // Script files over the per script/extension limit are recorded as warnings.
- // However, for the scripting API we should treat "install warnings" as
- // errors by turning this call into a no-op and returning an error.
- if (!warnings.empty() && error.empty()) {
- error = ErrorUtils::FormatErrorMessage(kFilesExceededSizeLimitError,
- warnings[0].specific);
- are_script_files_valid = false;
- }
-
- return std::make_pair(std::move(scripts), are_script_files_valid
- ? absl::nullopt
- : absl::make_optional(error));
-}
-
// Converts a UserScript object to a api::scripting::RegisteredContentScript
// object, used for getRegisteredContentScripts.
api::scripting::RegisteredContentScript CreateRegisteredContentScriptInfo(
const UserScript& script) {
api::scripting::RegisteredContentScript script_info;
+ CHECK_EQ(UserScript::Source::kDynamicContentScript, script.GetSource());
script_info.id = script.id();
script_info.matches.emplace();
@@ -936,32 +907,25 @@ ScriptingRegisterContentScriptsFunction::Run() {
std::vector<api::scripting::RegisteredContentScript>& scripts =
params->scripts;
-
ExtensionUserScriptLoader* loader =
ExtensionSystem::Get(browser_context())
->user_script_manager()
->GetUserScriptLoaderForExtension(extension()->id());
- std::set<std::string> existing_script_ids = loader->GetDynamicScriptIDs();
- std::set<std::string> new_script_ids;
- for (const auto& script : scripts) {
- if (script.id.empty())
- return RespondNow(Error("Content script's ID must not be empty"));
-
- if (script.id[0] == UserScript::kGeneratedIDPrefix) {
- return RespondNow(Error(base::StringPrintf(
- "Content script's ID '%s' must not start with '%c'",
- script.id.c_str(), UserScript::kGeneratedIDPrefix)));
- }
- if (base::Contains(existing_script_ids, script.id) ||
- base::Contains(new_script_ids, script.id)) {
- return RespondNow(Error(
- base::StringPrintf("Duplicate script ID '%s'", script.id.c_str())));
- }
-
- new_script_ids.insert(script.id);
+ // Create script ids for dynamic content scripts.
+ std::string error;
+ std::set<std::string> existing_script_ids =
+ loader->GetDynamicScriptIDs(UserScript::Source::kDynamicContentScript);
+ std::set<std::string> new_script_ids = scripting::CreateDynamicScriptIds(
+ scripts, UserScript::Source::kDynamicContentScript, existing_script_ids,
+ &error);
+
+ if (!error.empty()) {
+ CHECK(new_script_ids.empty());
+ return RespondNow(Error(std::move(error)));
}
+ // Parse content scripts.
std::u16string parse_error;
auto parsed_scripts = std::make_unique<UserScriptList>();
std::set<std::string> persistent_script_ids;
@@ -971,12 +935,13 @@ ScriptingRegisterContentScriptsFunction::Run() {
parsed_scripts->reserve(scripts.size());
for (size_t i = 0; i < scripts.size(); ++i) {
if (!scripts[i].matches) {
+ std::string error_script_id =
+ UserScript::TrimPrefixFromScriptID(scripts[i].id);
return RespondNow(
Error(base::StringPrintf("Script with ID '%s' must specify 'matches'",
- scripts[i].id.c_str())));
+ error_script_id.c_str())));
}
- // Parse/Create user script.
std::unique_ptr<UserScript> user_script =
ParseUserScript(browser_context(), *extension(), scripts[i], i,
valid_schemes, &parse_error);
@@ -997,7 +962,7 @@ ScriptingRegisterContentScriptsFunction::Run() {
GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult(
FROM_HERE,
- base::BindOnce(&ValidateParsedScriptsOnFileThread,
+ base::BindOnce(&scripting::ValidateParsedScriptsOnFileThread,
script_parsing::GetSymlinkPolicy(extension()),
std::move(parsed_scripts)),
base::BindOnce(&ScriptingRegisterContentScriptsFunction::
@@ -1012,10 +977,11 @@ ScriptingRegisterContentScriptsFunction::Run() {
void ScriptingRegisterContentScriptsFunction::OnContentScriptFilesValidated(
std::set<std::string> persistent_script_ids,
- ValidateContentScriptsResult result) {
+ scripting::ValidateScriptsResult result) {
// We cannot proceed if the `browser_context` is not valid as the
// `ExtensionSystem` will not exist.
if (!browser_context()) {
+ Release(); // Matches the `AddRef()` in `Run()`.
return;
}
@@ -1028,8 +994,9 @@ void ScriptingRegisterContentScriptsFunction::OnContentScriptFilesValidated(
if (error.has_value()) {
std::set<std::string> ids_to_remove;
- for (const auto& script : *scripts)
+ for (const auto& script : *scripts) {
ids_to_remove.insert(script->id());
+ }
loader->RemovePendingDynamicScriptIDs(std::move(ids_to_remove));
Respond(Error(std::move(*error)));
@@ -1068,8 +1035,10 @@ ScriptingGetRegisteredContentScriptsFunction::Run() {
params->filter;
std::set<std::string> id_filter;
if (filter && filter->ids) {
- id_filter.insert(std::make_move_iterator(filter->ids->begin()),
- std::make_move_iterator(filter->ids->end()));
+ for (const std::string& id : *(filter->ids)) {
+ id_filter.insert(scripting::AddPrefixToDynamicScriptId(
+ id, UserScript::Source::kDynamicContentScript));
+ }
}
ExtensionUserScriptLoader* loader =
@@ -1082,12 +1051,22 @@ ScriptingGetRegisteredContentScriptsFunction::Run() {
std::set<std::string> persistent_script_ids =
loader->GetPersistentDynamicScriptIDs();
for (const std::unique_ptr<UserScript>& script : dynamic_scripts) {
- if (id_filter.empty() || base::Contains(id_filter, script->id())) {
- auto registered_script = CreateRegisteredContentScriptInfo(*script);
- registered_script.persist_across_sessions =
- base::Contains(persistent_script_ids, script->id());
- script_infos.push_back(std::move(registered_script));
+ if (script->GetSource() != UserScript::Source::kDynamicContentScript) {
+ continue;
}
+
+ if (!id_filter.empty() && !base::Contains(id_filter, script->id())) {
+ continue;
+ }
+
+ auto registered_script = CreateRegisteredContentScriptInfo(*script);
+ registered_script.persist_across_sessions =
+ base::Contains(persistent_script_ids, script->id());
+
+ // Remove the internally used prefix from the `script`'s ID before
+ // returning.
+ registered_script.id = script->GetIDWithoutPrefix();
+ script_infos.push_back(std::move(registered_script));
}
return RespondNow(
@@ -1107,41 +1086,26 @@ ScriptingUnregisterContentScriptsFunction::Run() {
EXTENSION_FUNCTION_VALIDATE(params);
absl::optional<api::scripting::ContentScriptFilter>& filter = params->filter;
- std::set<std::string> ids_to_remove;
-
- ExtensionUserScriptLoader* loader =
- ExtensionSystem::Get(browser_context())
- ->user_script_manager()
- ->GetUserScriptLoaderForExtension(extension()->id());
- std::set<std::string> existing_script_ids = loader->GetDynamicScriptIDs();
- if (filter && filter->ids) {
- for (const auto& id : *filter->ids) {
- if (UserScript::IsIDGenerated(id)) {
- return RespondNow(Error(base::StringPrintf(
- "Content script's ID '%s' must not start with '%c'", id.c_str(),
- UserScript::kGeneratedIDPrefix)));
- }
-
- if (!base::Contains(existing_script_ids, id)) {
- return RespondNow(Error(
- base::StringPrintf("Nonexistent script ID '%s'", id.c_str())));
- }
-
- ids_to_remove.insert(id);
- }
+ absl::optional<std::vector<std::string>> ids = absl::nullopt;
+ // TODO(crbug.com/1300657): `ids` should have an empty list when filter ids is
+ // empty, instead of a nullopt. Otherwise, we are incorrectly removing all
+ // content scripts when ids is empty.
+ if (filter && filter->ids && !filter->ids->empty()) {
+ ids = std::move(filter->ids);
}
- if (ids_to_remove.empty()) {
- loader->ClearDynamicScripts(
- base::BindOnce(&ScriptingUnregisterContentScriptsFunction::
- OnContentScriptsUnregistered,
- this));
- } else {
- loader->RemoveDynamicScripts(
- std::move(ids_to_remove),
- base::BindOnce(&ScriptingUnregisterContentScriptsFunction::
- OnContentScriptsUnregistered,
- this));
+ std::string error;
+ bool removal_triggered = scripting::RemoveScripts(
+ ids, UserScript::Source::kDynamicContentScript, browser_context(),
+ extension()->id(),
+ base::BindOnce(&ScriptingUnregisterContentScriptsFunction::
+ OnContentScriptsUnregistered,
+ this),
+ &error);
+
+ if (!removal_triggered) {
+ CHECK(!error.empty());
+ return RespondNow(Error(std::move(error)));
}
return RespondLater();
@@ -1167,6 +1131,18 @@ ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() {
std::vector<api::scripting::RegisteredContentScript>& scripts =
params->scripts;
+ std::string error;
+
+ // Add the prefix for dynamic content scripts onto the IDs of all scripts in
+ // `scripts` before continuing.
+ std::set<std::string> ids_to_update = scripting::CreateDynamicScriptIds(
+ scripts, UserScript::Source::kDynamicContentScript,
+ std::set<std::string>(), &error);
+
+ if (!error.empty()) {
+ CHECK(ids_to_update.empty());
+ return RespondNow(Error(std::move(error)));
+ }
ExtensionUserScriptLoader* loader =
ExtensionSystem::Get(browser_context())
@@ -1177,29 +1153,21 @@ ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() {
loaded_scripts_metadata;
const UserScriptList& dynamic_scripts = loader->GetLoadedDynamicScripts();
for (const std::unique_ptr<UserScript>& script : dynamic_scripts) {
- loaded_scripts_metadata.emplace(script->id(),
- CreateRegisteredContentScriptInfo(*script));
+ if (script->GetSource() == UserScript::Source::kDynamicContentScript) {
+ loaded_scripts_metadata.emplace(
+ script->id(), CreateRegisteredContentScriptInfo(*script));
+ }
}
- std::set<std::string> ids_to_update;
for (const auto& script : scripts) {
+ std::string error_script_id = UserScript::TrimPrefixFromScriptID(script.id);
if (loaded_scripts_metadata.find(script.id) ==
loaded_scripts_metadata.end()) {
return RespondNow(
- Error(base::StringPrintf("Content script with ID '%s' does not exist "
+ Error(base::StringPrintf("Script with ID '%s' does not exist "
"or is not fully registered",
- script.id.c_str())));
- }
-
- DCHECK(!script.id.empty());
- DCHECK(!UserScript::IsIDGenerated(script.id));
-
- if (base::Contains(ids_to_update, script.id)) {
- return RespondNow(Error(
- base::StringPrintf("Duplicate script ID '%s'", script.id.c_str())));
+ error_script_id.c_str())));
}
-
- ids_to_update.insert(script.id);
}
std::u16string parse_error;
@@ -1268,7 +1236,7 @@ ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() {
GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult(
FROM_HERE,
- base::BindOnce(&ValidateParsedScriptsOnFileThread,
+ base::BindOnce(&scripting::ValidateParsedScriptsOnFileThread,
script_parsing::GetSymlinkPolicy(extension()),
std::move(parsed_scripts)),
base::BindOnce(
@@ -1283,10 +1251,11 @@ ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() {
void ScriptingUpdateContentScriptsFunction::OnContentScriptFilesValidated(
std::set<std::string> persistent_script_ids,
- ValidateContentScriptsResult result) {
+ scripting::ValidateScriptsResult result) {
// We cannot proceed if the `browser_context` is not valid as the
// `ExtensionSystem` will not exist.
if (!browser_context()) {
+ Release(); // Matches the `AddRef()` in `Run()`.
return;
}
diff --git a/chromium/chrome/browser/extensions/api/scripting/scripting_api.h b/chromium/chrome/browser/extensions/api/scripting/scripting_api.h
index 811da641457..a9db6bc8959 100644
--- a/chromium/chrome/browser/extensions/api/scripting/scripting_api.h
+++ b/chromium/chrome/browser/extensions/api/scripting/scripting_api.h
@@ -11,6 +11,7 @@
#include <vector>
#include "chrome/common/extensions/api/scripting.h"
+#include "extensions/browser/api/scripting/scripting_utils.h"
#include "extensions/browser/extension_function.h"
#include "extensions/browser/script_executor.h"
#include "extensions/common/mojom/code_injection.mojom.h"
@@ -108,9 +109,6 @@ class ScriptingRemoveCSSFunction : public ExtensionFunction {
void OnCSSRemoved(std::vector<ScriptExecutor::FrameResult> results);
};
-using ValidateContentScriptsResult =
- std::pair<std::unique_ptr<UserScriptList>, absl::optional<std::string>>;
-
class ScriptingRegisterContentScriptsFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("scripting.registerContentScripts",
@@ -131,7 +129,7 @@ class ScriptingRegisterContentScriptsFunction : public ExtensionFunction {
// Called when script files have been checked.
void OnContentScriptFilesValidated(
std::set<std::string> persistent_script_ids,
- ValidateContentScriptsResult result);
+ scripting::ValidateScriptsResult result);
// Called when content scripts have been registered.
void OnContentScriptsRegistered(const absl::optional<std::string>& error);
@@ -196,7 +194,7 @@ class ScriptingUpdateContentScriptsFunction : public ExtensionFunction {
// Called when script files have been checked.
void OnContentScriptFilesValidated(
std::set<std::string> persistent_script_ids,
- ValidateContentScriptsResult result);
+ scripting::ValidateScriptsResult result);
// Called when content scripts have been updated.
void OnContentScriptsUpdated(const absl::optional<std::string>& error);
diff --git a/chromium/chrome/browser/extensions/api/scripting/scripting_apitest.cc b/chromium/chrome/browser/extensions/api/scripting/scripting_apitest.cc
index abe0c434f07..2f86e914ae1 100644
--- a/chromium/chrome/browser/extensions/api/scripting/scripting_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/scripting/scripting_apitest.cc
@@ -21,6 +21,7 @@
#include "extensions/browser/api_test_utils.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/browser/disable_reason.h"
+#include "extensions/common/extension_features.h"
#include "extensions/common/features/feature_channel.h"
#include "extensions/common/utils/content_script_utils.h"
#include "extensions/test/extension_test_message_listener.h"
@@ -550,4 +551,30 @@ IN_PROC_BROWSER_TEST_F(ScriptingAPIPrerenderingTest, MAYBE_Basic) {
ASSERT_TRUE(RunExtensionTest("scripting/prerendering")) << message_;
}
+class ScriptingAndUserScriptsAPITest : public ScriptingAPITest {
+ public:
+ ScriptingAndUserScriptsAPITest() {
+ scoped_feature_list_.InitAndEnableFeature(
+ extensions_features::kApiUserScripts);
+ }
+ ScriptingAndUserScriptsAPITest(const ScriptingAndUserScriptsAPITest&) =
+ delete;
+ ScriptingAndUserScriptsAPITest& operator=(
+ const ScriptingAndUserScriptsAPITest&) = delete;
+ ~ScriptingAndUserScriptsAPITest() override = default;
+
+ private:
+ // The userScripts API is currently behind a channel and feature restriction.
+ // TODO(crbug.com/1472902): Remove channel override when user scripts API goes
+ // to stable.
+ ScopedCurrentChannel current_channel_override_{
+ version_info::Channel::UNKNOWN};
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(ScriptingAndUserScriptsAPITest,
+ ScriptingAPIDoesNotAffectUserScripts) {
+ ASSERT_TRUE(RunExtensionTest("scripting/dynamic_user_scripts")) << message_;
+}
+
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc b/chromium/chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc
index e94dc4dfd5a..e897598888f 100644
--- a/chromium/chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc
+++ b/chromium/chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc
@@ -23,9 +23,12 @@
#include "extensions/browser/extension_prefs_factory.h"
#include "extensions/browser/extension_prefs_helper.h"
#include "extensions/browser/extension_prefs_helper_factory.h"
+#include "extensions/common/api/types.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/manifest_constants.h"
+using extensions::api::types::ChromeSettingScope;
+
namespace extensions {
namespace {
@@ -147,7 +150,7 @@ void SettingsOverridesAPI::SetPref(const std::string& extension_id,
return;
prefs_helper->SetExtensionControlledPref(
- extension_id, pref_key, kExtensionPrefsScopeRegular, std::move(value));
+ extension_id, pref_key, ChromeSettingScope::kRegular, std::move(value));
}
void SettingsOverridesAPI::UnsetPref(const std::string& extension_id,
@@ -157,7 +160,7 @@ void SettingsOverridesAPI::UnsetPref(const std::string& extension_id,
if (!prefs_helper)
return;
prefs_helper->RemoveExtensionControlledPref(extension_id, pref_key,
- kExtensionPrefsScopeRegular);
+ ChromeSettingScope::kRegular);
}
void SettingsOverridesAPI::OnExtensionLoaded(
diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.h b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.h
index ec5992b76b3..bd31f1492c3 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.h
+++ b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.h
@@ -7,8 +7,8 @@
#include <memory>
#include <string>
-#include <unordered_map>
+#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/extensions/api/settings_private/generated_pref.h"
#include "chrome/browser/extensions/api/settings_private/prefs_util_enums.h"
@@ -22,11 +22,10 @@ class Value;
}
namespace extensions {
-namespace api {
-namespace settings_private {
+
+namespace api::settings_private {
struct PrefObject;
-} // namespace settings_private
-} // namespace api
+} // namespace api::settings_private
namespace settings_private {
@@ -36,8 +35,7 @@ namespace settings_private {
class GeneratedPrefs : public KeyedService {
public:
// Preference name to implementation map.
- using PrefsMap =
- std::unordered_map<std::string, std::unique_ptr<GeneratedPref>>;
+ using PrefsMap = base::flat_map<std::string, std::unique_ptr<GeneratedPref>>;
explicit GeneratedPrefs(Profile* profile);
diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc
index af641e055d1..4ad1f0f943e 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc
+++ b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc
@@ -41,9 +41,10 @@ bool GeneratedPrefsFactory::ServiceIsNULLWhileTesting() const {
return true;
}
-KeyedService* GeneratedPrefsFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+GeneratedPrefsFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const {
- return new GeneratedPrefs(static_cast<Profile*>(profile));
+ return std::make_unique<GeneratedPrefs>(static_cast<Profile*>(profile));
}
} // namespace settings_private
diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h
index 2aaea1496d8..69d829feec5 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h
+++ b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h
@@ -32,7 +32,7 @@ class GeneratedPrefsFactory : public ProfileKeyedServiceFactory {
// BrowserContextKeyedServiceFactory implementation.
bool ServiceIsNULLWhileTesting() const override;
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 0e684543447..40fb00a537e 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -42,8 +42,10 @@
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/payments/core/payment_prefs.h"
#include "components/performance_manager/public/user_tuning/prefs.h"
+#include "components/permissions/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
+#include "components/privacy_sandbox/tracking_protection_prefs.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/search_engines/default_search_manager.h"
@@ -188,6 +190,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
(*s_allowlist)[autofill::prefs::kAutofillPaymentMethodsMandatoryReauth] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
#endif
+ (*s_allowlist)[autofill::prefs::kAutofillPaymentCvcStorage] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[payments::kCanMakePaymentEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[bookmarks::prefs::kShowBookmarkBar] =
@@ -208,8 +212,6 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
(*s_allowlist)[::prefs::kPolicyThemeColor] =
settings_api::PrefType::PREF_TYPE_NUMBER;
#if BUILDFLAG(IS_LINUX)
- (*s_allowlist)[::prefs::kUsesSystemThemeDeprecated] =
- settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[::prefs::kSystemTheme] =
settings_api::PrefType::PREF_TYPE_NUMBER;
#endif
@@ -265,6 +267,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
settings_api::PrefType::PREF_TYPE_STRING;
(*s_allowlist)[drive::prefs::kDriveFsBulkPinningEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
+ (*s_allowlist)[drive::prefs::kDisableDriveOverCellular] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
#endif
(*s_allowlist)[::prefs::kDownloadBubblePartialViewEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
@@ -292,6 +296,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
[password_manager::prefs::kBiometricAuthenticationBeforeFilling] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
#endif
+#if BUILDFLAG(IS_MAC)
+ (*s_allowlist)[::prefs::kCreatePasskeysInICloudKeychain] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
+#endif
// Privacy page
(*s_allowlist)[::prefs::kSigninAllowedOnNextStartup] =
@@ -352,6 +360,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[::prefs::kPrivacySandboxFirstPartySetsEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
+ (*s_allowlist)[::prefs::kBlockAll3pcToggleEnabled] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
+ (*s_allowlist)[::prefs::kTrackingProtectionLevel] =
+ settings_api::PrefType::PREF_TYPE_NUMBER;
// Sync and personalization page.
(*s_allowlist)[::prefs::kSearchSuggestEnabled] =
@@ -398,6 +410,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[ash::prefs::kAssistPredictiveWritingEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
+ (*s_allowlist)[ash::prefs::kOrcaEnabled] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[ash::prefs::kEmojiSuggestionEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[ash::prefs::kLacrosProxyControllingExtension] =
@@ -450,6 +464,9 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
settings_api::PrefType::PREF_TYPE_NUMBER;
(*s_allowlist)[::prefs::kEnableQuietNotificationPermissionUi] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
+ (*s_allowlist)
+ [::permissions::prefs::kUnusedSitePermissionsRevocationEnabled] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
// Clear browsing data settings.
(*s_allowlist)[browsing_data::prefs::kDeleteBrowsingHistory] =
@@ -503,6 +520,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[::prefs::kLiveCaptionLanguageCode] =
settings_api::PrefType::PREF_TYPE_STRING;
+ (*s_allowlist)[::prefs::kLiveCaptionMaskOffensiveWords] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[::prefs::kLiveTranslateEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[::prefs::kLiveTranslateTargetLanguageCode] =
@@ -512,6 +531,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
(*s_allowlist)[::prefs::kAccessibilityPdfOcrAlwaysActive] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
#endif
+#if defined(USE_AURA)
+ (*s_allowlist)[::prefs::kOverscrollHistoryNavigationEnabled] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
+#endif
(*s_allowlist)[::prefs::kCaretBrowsingEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
@@ -549,11 +572,11 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() {
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[ash::prefs::kAccessibilityAutoclickMovementThreshold] =
settings_api::PrefType::PREF_TYPE_NUMBER;
- (*s_allowlist)[ash::prefs::kAccessibilityColorFiltering] =
+ (*s_allowlist)[ash::prefs::kAccessibilityColorCorrectionEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_allowlist)[ash::prefs::kAccessibilityColorVisionCorrectionAmount] =
settings_api::PrefType::PREF_TYPE_NUMBER;
- (*s_allowlist)[ash::prefs::kAccessibilityColorVisionDeficiencyType] =
+ (*s_allowlist)[ash::prefs::kAccessibilityColorVisionCorrectionType] =
settings_api::PrefType::PREF_TYPE_NUMBER;
(*s_allowlist)[ash::prefs::kShouldAlwaysShowAccessibilityMenu] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc b/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc
index f28e6c77245..f139561b41a 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc
+++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc
@@ -38,9 +38,11 @@ SettingsPrivateDelegateFactory::SettingsPrivateDelegateFactory()
SettingsPrivateDelegateFactory::~SettingsPrivateDelegateFactory() = default;
-KeyedService* SettingsPrivateDelegateFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+SettingsPrivateDelegateFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const {
- return new SettingsPrivateDelegate(static_cast<Profile*>(profile));
+ return std::make_unique<SettingsPrivateDelegate>(
+ static_cast<Profile*>(profile));
}
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h b/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h
index d756c4c2c9b..eef59cde0f7 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h
+++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h
@@ -35,7 +35,7 @@ class SettingsPrivateDelegateFactory : public ProfileKeyedServiceFactory {
~SettingsPrivateDelegateFactory() override;
// BrowserContextKeyedServiceFactory implementation.
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc
index 418137145ae..532c08d59bf 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc
+++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc
@@ -161,15 +161,16 @@ void SettingsPrivateEventRouter::SendPrefChange(const std::string& pref_name) {
auto args(api::settings_private::OnPrefsChanged::Create(prefs));
- std::unique_ptr<Event> extension_event(new Event(
- events::SETTINGS_PRIVATE_ON_PREFS_CHANGED,
- api::settings_private::OnPrefsChanged::kEventName, std::move(args)));
+ std::unique_ptr<Event> extension_event =
+ std::make_unique<Event>(events::SETTINGS_PRIVATE_ON_PREFS_CHANGED,
+ api::settings_private::OnPrefsChanged::kEventName,
+ std::move(args), context_);
event_router->BroadcastEvent(std::move(extension_event));
}
-SettingsPrivateEventRouter* SettingsPrivateEventRouter::Create(
+std::unique_ptr<SettingsPrivateEventRouter> SettingsPrivateEventRouter::Create(
content::BrowserContext* context) {
- return new SettingsPrivateEventRouter(context);
+ return std::make_unique<SettingsPrivateEventRouter>(context);
}
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.h b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.h
index 7d2c5543799..a16221f9ec5 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.h
+++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.h
@@ -35,9 +35,10 @@ class SettingsPrivateEventRouter
public EventRouter::Observer,
public settings_private::GeneratedPref::Observer {
public:
- static SettingsPrivateEventRouter* Create(
+ static std::unique_ptr<SettingsPrivateEventRouter> Create(
content::BrowserContext* browser_context);
+ explicit SettingsPrivateEventRouter(content::BrowserContext* context);
SettingsPrivateEventRouter(const SettingsPrivateEventRouter&) = delete;
SettingsPrivateEventRouter& operator=(const SettingsPrivateEventRouter&) =
delete;
@@ -50,8 +51,6 @@ class SettingsPrivateEventRouter
content::BrowserContext* context_for_test() { return context_; }
protected:
- explicit SettingsPrivateEventRouter(content::BrowserContext* context);
-
// KeyedService overrides:
void Shutdown() override;
diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc
index bf49f8d1803..24c742fa38e 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc
+++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc
@@ -46,7 +46,8 @@ SettingsPrivateEventRouterFactory::SettingsPrivateEventRouterFactory()
SettingsPrivateEventRouterFactory::~SettingsPrivateEventRouterFactory() =
default;
-KeyedService* SettingsPrivateEventRouterFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+SettingsPrivateEventRouterFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
return SettingsPrivateEventRouter::Create(context);
}
diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h
index a16216aca44..d831a361e81 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h
+++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h
@@ -42,7 +42,7 @@ class SettingsPrivateEventRouterFactory : public ProfileKeyedServiceFactory {
~SettingsPrivateEventRouterFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_unittest.cc b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_unittest.cc
new file mode 100644
index 00000000000..f1ff2ea6e74
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_unittest.cc
@@ -0,0 +1,160 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/settings_private/settings_private_event_router.h"
+
+#include "base/test/run_until.h"
+#include "chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "extensions/browser/event_router_factory.h"
+#include "extensions/browser/process_map.h"
+#include "extensions/browser/process_map_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+namespace {
+
+// A fake that pretends that all contexts are WebUI.
+class ProcessMapFake : public ProcessMap {
+ public:
+ Feature::Context GetMostLikelyContextType(const Extension* extension,
+ int process_id,
+ const GURL* url) const override {
+ return Feature::WEBUI_CONTEXT;
+ }
+};
+
+std::unique_ptr<KeyedService> BuildEventRouter(
+ content::BrowserContext* profile) {
+ return std::make_unique<extensions::EventRouter>(profile, nullptr);
+}
+
+std::unique_ptr<KeyedService> BuildSettingsPrivateEventRouter(
+ content::BrowserContext* profile) {
+ return std::unique_ptr<KeyedService>(
+ SettingsPrivateEventRouter::Create(profile));
+}
+
+std::unique_ptr<KeyedService> BuildProcessMap(
+ content::BrowserContext* profile) {
+ return std::make_unique<ProcessMapFake>();
+}
+
+// Tracks event dispatches to a specific process.
+class EventRouterObserver : public EventRouter::TestObserver {
+ public:
+ // Only counts events that match |process_id|.
+ explicit EventRouterObserver(int process_id) : process_id_(process_id) {}
+
+ void OnWillDispatchEvent(const Event& event) override {
+ // Do nothing.
+ }
+
+ void OnDidDispatchEventToProcess(const Event& event,
+ int process_id) override {
+ if (process_id == process_id_) {
+ ++dispatch_count;
+ }
+ }
+
+ int dispatch_count = 0;
+ const int process_id_;
+};
+
+class SettingsPrivateEventRouterTest : public testing::Test {
+ public:
+ SettingsPrivateEventRouterTest()
+ : manager_(TestingBrowserProcess::GetGlobal()) {}
+ void SetUp() override { ASSERT_TRUE(manager_.SetUp()); }
+
+ protected:
+ content::BrowserTaskEnvironment task_environment_;
+ TestingProfileManager manager_;
+};
+
+// Tests that events from incognito profiles do not get routed to regular
+// profiles. Regression test for https://crbug.com/1381219.
+TEST_F(SettingsPrivateEventRouterTest, IncognitoEventRouting) {
+ // Create a testing profile. Override relevant factories.
+ TestingProfile* profile = manager_.CreateTestingProfile("test");
+ EventRouterFactory::GetInstance()->SetTestingFactory(
+ profile, base::BindRepeating(&BuildEventRouter));
+ SettingsPrivateEventRouterFactory::GetInstance()->SetTestingFactory(
+ profile, base::BindRepeating(&BuildSettingsPrivateEventRouter));
+ ProcessMapFactory::GetInstance()->SetTestingFactory(
+ profile, base::BindRepeating(&BuildProcessMap));
+
+ // Create an otr profile. Override relevant factories.
+ Profile::OTRProfileID otr_id = Profile::OTRProfileID::PrimaryID();
+ Profile* otr_profile =
+ profile->GetOffTheRecordProfile(otr_id, /*create_if_needed=*/true);
+ EventRouterFactory::GetInstance()->SetTestingFactory(
+ otr_profile, base::BindRepeating(&BuildEventRouter));
+ SettingsPrivateEventRouterFactory::GetInstance()->SetTestingFactory(
+ otr_profile, base::BindRepeating(&BuildSettingsPrivateEventRouter));
+ ProcessMapFactory::GetInstance()->SetTestingFactory(
+ otr_profile, base::BindRepeating(&BuildProcessMap));
+
+ // Create the event routers.
+ EventRouter* regular_event_router =
+ EventRouterFactory::GetInstance()->GetForBrowserContext(profile);
+ EventRouter* otr_event_router =
+ EventRouterFactory::GetInstance()->GetForBrowserContext(otr_profile);
+
+ // Today, EventRouter instances are shared between on- and off-the-record
+ // profile instances. We separate them into variables here, since the
+ // SettingsPrivateEventRouter shouldn't necessarily know about that or
+ // care.
+ EXPECT_EQ(regular_event_router, otr_event_router);
+
+ // Create the special routers for settingsPrivate.
+ ASSERT_TRUE(SettingsPrivateEventRouterFactory::GetForProfile(profile));
+ ASSERT_TRUE(SettingsPrivateEventRouterFactory::GetForProfile(otr_profile));
+
+ // Create some mock rphs.
+ content::MockRenderProcessHost regular_rph(profile);
+ content::MockRenderProcessHost otr_rph(otr_profile);
+
+ // Add event listeners, as if we had created two real WebUIs, one in a regular
+ // profile and one in an otr profile. Note that the string chrome://settings
+ // is hardcoded into the api permissions of settingsPrivate.
+ GURL kDummyURL("chrome://settings");
+ regular_event_router->AddEventListenerForURL(
+ api::settings_private::OnPrefsChanged::kEventName, &regular_rph,
+ kDummyURL);
+ otr_event_router->AddEventListenerForURL(
+ api::settings_private::OnPrefsChanged::kEventName, &otr_rph, kDummyURL);
+
+ // Hook up some test observers
+ EventRouterObserver regular_counter(regular_rph.GetID());
+ regular_event_router->AddObserverForTesting(&regular_counter);
+ EventRouterObserver otr_counter(otr_rph.GetID());
+ otr_event_router->AddObserverForTesting(&otr_counter);
+
+ EXPECT_EQ(0, regular_counter.dispatch_count);
+ EXPECT_EQ(0, otr_counter.dispatch_count);
+
+ // Setting an otr pref should not trigger the normal observer.
+ otr_profile->GetPrefs()->SetBoolean(prefs::kPromptForDownload, true);
+ ASSERT_TRUE(
+ base::test::RunUntil([&]() { return otr_counter.dispatch_count == 1; }));
+ EXPECT_EQ(0, regular_counter.dispatch_count);
+ EXPECT_EQ(1, otr_counter.dispatch_count);
+
+ // Setting a regular pref should not trigger the otr observer.
+ profile->GetPrefs()->SetBoolean(prefs::kPromptForDownload, true);
+ ASSERT_TRUE(base::test::RunUntil(
+ [&]() { return regular_counter.dispatch_count == 1; }));
+ EXPECT_EQ(1, regular_counter.dispatch_count);
+ EXPECT_EQ(1, otr_counter.dispatch_count);
+}
+
+} // namespace
+} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.h b/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.h
index 4e672e62e66..571f4e1b0aa 100644
--- a/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.h
+++ b/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.h
@@ -93,7 +93,7 @@ class ManagedValueStoreCache : public ValueStoreCache,
VALID_CONTEXT_REQUIRED(backend_sequence_checker_);
// The profile that owns the extension system being used.
- const raw_ref<Profile, DanglingUntriaged> profile_
+ const raw_ref<Profile, AcrossTasksDanglingUntriaged> profile_
GUARDED_BY_CONTEXT(ui_sequence_checker_);
// The policy domain. This is used for observing the policy updates.
@@ -101,8 +101,8 @@ class ManagedValueStoreCache : public ValueStoreCache,
GUARDED_BY_CONTEXT(ui_sequence_checker_);
// The `profile_`'s `PolicyService`.
- const raw_ref<policy::PolicyService, DanglingUntriaged> policy_service_
- GUARDED_BY_CONTEXT(ui_sequence_checker_);
+ const raw_ref<policy::PolicyService, AcrossTasksDanglingUntriaged>
+ policy_service_ GUARDED_BY_CONTEXT(ui_sequence_checker_);
// Observes extension loading and unloading, and keeps the `Profile`'s
// `PolicyService` aware of the current list of extensions.
diff --git a/chromium/chrome/browser/extensions/api/storage/policy_value_store.h b/chromium/chrome/browser/extensions/api/storage/policy_value_store.h
index fc39df27d06..872f953838e 100644
--- a/chromium/chrome/browser/extensions/api/storage/policy_value_store.h
+++ b/chromium/chrome/browser/extensions/api/storage/policy_value_store.h
@@ -13,6 +13,7 @@
#include "components/value_store/value_store.h"
#include "extensions/browser/api/storage/settings_observer.h"
+#include "extensions/common/extension_id.h"
namespace policy {
class PolicyMap;
@@ -63,7 +64,7 @@ class PolicyValueStore : public value_store::ValueStore {
value_store::ValueStore* delegate() { return delegate_.get(); }
private:
- std::string extension_id_;
+ ExtensionId extension_id_;
SequenceBoundSettingsChangedCallback observer_;
std::unique_ptr<value_store::ValueStore> delegate_;
};
diff --git a/chromium/chrome/browser/extensions/api/storage/setting_sync_data.h b/chromium/chrome/browser/extensions/api/storage/setting_sync_data.h
index 913bc2c76a8..76dce4a4b14 100644
--- a/chromium/chrome/browser/extensions/api/storage/setting_sync_data.h
+++ b/chromium/chrome/browser/extensions/api/storage/setting_sync_data.h
@@ -10,6 +10,7 @@
#include "base/values.h"
#include "components/sync/model/sync_change.h"
+#include "extensions/common/extension_id.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace syncer {
@@ -45,7 +46,7 @@ class SettingSyncData {
const {
return change_type_;
}
- const std::string& extension_id() const { return extension_id_; }
+ const ExtensionId& extension_id() const { return extension_id_; }
const std::string& key() const { return key_; }
// value() cannot be called if ExtractValue() has been called.
const base::Value& value() const { return *value_; }
@@ -60,7 +61,7 @@ class SettingSyncData {
void ExtractSyncData(const syncer::SyncData& sync_data);
absl::optional<syncer::SyncChange::SyncChangeType> change_type_;
- std::string extension_id_;
+ ExtensionId extension_id_;
std::string key_;
absl::optional<base::Value> value_;
};
diff --git a/chromium/chrome/browser/extensions/api/storage/settings_sync_processor.h b/chromium/chrome/browser/extensions/api/storage/settings_sync_processor.h
index e8702bc637c..e1e8f119a2a 100644
--- a/chromium/chrome/browser/extensions/api/storage/settings_sync_processor.h
+++ b/chromium/chrome/browser/extensions/api/storage/settings_sync_processor.h
@@ -12,6 +12,7 @@
#include "base/values.h"
#include "components/sync/base/model_type.h"
#include "components/value_store/value_store_change.h"
+#include "extensions/common/extension_id.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace syncer {
@@ -53,7 +54,7 @@ class SettingsSyncProcessor {
private:
// ID of the extension the changes are for.
- const std::string extension_id_;
+ const ExtensionId extension_id_;
// Sync model type. Either EXTENSION_SETTING or APP_SETTING.
const syncer::ModelType type_;
diff --git a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.cc b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.cc
index e36654a1039..75ca60daa3d 100644
--- a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.cc
+++ b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.cc
@@ -39,14 +39,15 @@ SystemIndicatorManagerFactory::SystemIndicatorManagerFactory()
SystemIndicatorManagerFactory::~SystemIndicatorManagerFactory() = default;
-KeyedService* SystemIndicatorManagerFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+SystemIndicatorManagerFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const {
StatusTray* status_tray = g_browser_process->status_tray();
if (status_tray == NULL)
return NULL;
- return new SystemIndicatorManager(static_cast<Profile*>(profile),
- status_tray);
+ return std::make_unique<SystemIndicatorManager>(
+ static_cast<Profile*>(profile), status_tray);
}
bool SystemIndicatorManagerFactory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h
index 2168833ed93..cb24fad396c 100644
--- a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h
+++ b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h
@@ -30,7 +30,7 @@ class SystemIndicatorManagerFactory : public ProfileKeyedServiceFactory {
~SystemIndicatorManagerFactory() override;
// BrowserContextKeyedServiceFactory implementation.
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
bool ServiceIsCreatedWithBrowserContext() const override;
};
diff --git a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
index 64a8ace89c1..e324c999ad3 100644
--- a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
+++ b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
@@ -3,9 +3,9 @@
// found in the LICENSE file.
#include <cmath>
-#include <unordered_map>
#include "base/command_line.h"
+#include "base/containers/flat_map.h"
#include "base/files/file_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/trace_event_analyzer.h"
@@ -54,7 +54,7 @@ constexpr char kEventSuffixFailRate[] = "FailRate";
constexpr char kEventSuffixLatency[] = "Latency";
constexpr char kEventCommitAndDrawCompositorFrame[] =
"WidgetBase::DidCommitAndDrawCompositorFrame";
-const std::unordered_map<std::string, std::string> kEventToMetricMap(
+const base::flat_map<std::string, std::string> kEventToMetricMap(
{{kEventCapture, kMetricCaptureMs},
{std::string(kEventCapture) + kEventSuffixFailRate,
kMetricCaptureFailRatePercent},
diff --git a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
index 05ea0170bfa..170aa77db94 100644
--- a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
+++ b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
@@ -20,6 +20,7 @@
#include "content/public/browser/web_contents_observer.h"
#include "extensions/browser/event_router.h"
#include "extensions/common/extension.h"
+#include "extensions/common/extension_id.h"
#include "url/origin.h"
using content::BrowserThread;
@@ -35,7 +36,7 @@ namespace tab_capture = api::tab_capture;
class TabCaptureRegistry::LiveRequest : public content::WebContentsObserver {
public:
LiveRequest(content::WebContents* target_contents,
- const std::string& extension_id,
+ const ExtensionId& extension_id,
bool is_anonymous,
TabCaptureRegistry* registry)
: content::WebContentsObserver(target_contents),
@@ -56,7 +57,7 @@ class TabCaptureRegistry::LiveRequest : public content::WebContentsObserver {
~LiveRequest() override {}
// Accessors.
- const std::string& extension_id() const { return extension_id_; }
+ const ExtensionId& extension_id() const { return extension_id_; }
bool is_anonymous() const { return is_anonymous_; }
TabCaptureState capture_state() const { return capture_state_; }
bool is_verified() const { return is_verified_; }
@@ -102,7 +103,7 @@ class TabCaptureRegistry::LiveRequest : public content::WebContentsObserver {
}
private:
- const std::string extension_id_;
+ const ExtensionId extension_id_;
const bool is_anonymous_;
const raw_ptr<TabCaptureRegistry> registry_;
TabCaptureState capture_state_ = tab_capture::TAB_CAPTURE_STATE_NONE;
diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc
index 5025286fe96..93b7498b7c5 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -51,6 +51,8 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
#include "chrome/browser/resource_coordinator/tab_manager.h"
+#include "chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h"
+#include "chrome/browser/safe_browsing/extension_telemetry/tabs_api_signal.h"
#include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/ui/apps/chrome_app_delegate.h"
#include "chrome/browser/ui/browser.h"
@@ -76,6 +78,7 @@
#include "chrome/common/url_constants.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/core/common/features.h"
#include "components/sessions/content/session_tab_helper.h"
#include "components/tab_groups/tab_group_color.h"
#include "components/tab_groups/tab_group_id.h"
@@ -437,6 +440,30 @@ bool WindowBoundsIntersectDisplays(const gfx::Rect& bounds) {
return intersect_area >= (bounds.size().GetArea() / 2);
}
+void NotifyExtensionTelemetry(Profile* profile,
+ const Extension* extension,
+ safe_browsing::TabsApiInfo::ApiMethod api_method,
+ const std::string& current_url,
+ const std::string& new_url) {
+ // Ignore API calls that are not invoked by extensions.
+ if (!extension) {
+ return;
+ }
+
+ auto* extension_telemetry_service =
+ safe_browsing::ExtensionTelemetryService::Get(profile);
+
+ if (!extension_telemetry_service || !extension_telemetry_service->enabled() ||
+ !base::FeatureList::IsEnabled(
+ safe_browsing::kExtensionTelemetryTabsApiSignal)) {
+ return;
+ }
+
+ auto tabs_api_signal = std::make_unique<safe_browsing::TabsApiSignal>(
+ extension->id(), api_method, current_url, new_url);
+ extension_telemetry_service->AddSignal(std::move(tabs_api_signal));
+}
+
} // namespace
void ZoomModeToZoomSettings(ZoomController::ZoomMode zoom_mode,
@@ -1298,6 +1325,11 @@ ExtensionFunction::ResponseAction TabsCreateFunction::Run() {
return Error(result.error());
}
+ NotifyExtensionTelemetry(Profile::FromBrowserContext(browser_context()),
+ extension(), safe_browsing::TabsApiInfo::CREATE,
+ /*current_url=*/std::string(),
+ options.url.value_or(std::string()));
+
// Return data about the newly created tab.
return has_callback() ? WithArguments(std::move(*result)) : NoArguments();
}());
@@ -1509,8 +1541,18 @@ ExtensionFunction::ResponseAction TabsUpdateFunction::Run() {
return RespondNow(Error(ErrorUtils::FormatErrorMessage(
tabs_constants::kURLsNotAllowedInIncognitoError, updated_url)));
}
+
+ // Get last committed or pending URL.
+ std::string current_url = contents->GetVisibleURL().is_valid()
+ ? contents->GetVisibleURL().spec()
+ : std::string();
+
if (!UpdateURL(updated_url, tab_id, &error))
return RespondNow(Error(std::move(error)));
+
+ NotifyExtensionTelemetry(Profile::FromBrowserContext(browser_context()),
+ extension(), safe_browsing::TabsApiInfo::UPDATE,
+ current_url, updated_url);
}
bool active = false;
@@ -1613,12 +1655,6 @@ bool TabsUpdateFunction::UpdateURL(const std::string& url_string,
return false;
}
- // JavaScript URLs are forbidden in chrome.tabs.update().
- if (url->SchemeIs(url::kJavaScriptScheme)) {
- *error = tabs_constants::kJavaScriptUrlsNotAllowedInTabsUpdate;
- return false;
- }
-
NavigationController::LoadURLParams load_params(*url);
// Treat extension-initiated navigations as renderer-initiated so that the URL
@@ -1864,6 +1900,15 @@ bool TabsRemoveFunction::RemoveTab(int tab_id, std::string* error) {
*error = tabs_constants::kTabStripNotEditableError;
return false;
}
+
+ // Get last committed or pending URL.
+ std::string current_url = contents->GetVisibleURL().is_valid()
+ ? contents->GetVisibleURL().spec()
+ : std::string();
+ NotifyExtensionTelemetry(Profile::FromBrowserContext(browser_context()),
+ extension(), safe_browsing::TabsApiInfo::REMOVE,
+ current_url, /*new_url=*/std::string());
+
// The tab might not immediately close after calling Close() below, so we
// should wait until WebContentsDestroyed is called before responding.
web_contents_destroyed_observers_.push_back(
@@ -2420,9 +2465,9 @@ bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) {
int frame_id = details_->frame_id ? *details_->frame_id
: ExtensionApiFrameIdMap::kTopFrameId;
- content::RenderFrameHost* rfh =
+ content::RenderFrameHost* render_frame_host =
ExtensionApiFrameIdMap::GetRenderFrameHostById(contents, frame_id);
- if (!rfh) {
+ if (!render_frame_host) {
*error = ErrorUtils::FormatErrorMessage(
tabs_constants::kFrameNotFoundError, base::NumberToString(frame_id),
base::NumberToString(execute_tab_id_));
@@ -2432,11 +2477,12 @@ bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) {
// Content scripts declared in manifest.json can access frames at about:-URLs
// if the extension has permission to access the frame's origin, so also allow
// programmatic content scripts at about:-URLs for allowed origins.
- GURL effective_document_url(rfh->GetLastCommittedURL());
+ GURL effective_document_url(render_frame_host->GetLastCommittedURL());
bool is_about_url = effective_document_url.SchemeIs(url::kAboutScheme);
if (is_about_url && details_->match_about_blank &&
*details_->match_about_blank) {
- effective_document_url = GURL(rfh->GetLastCommittedOrigin().Serialize());
+ effective_document_url =
+ GURL(render_frame_host->GetLastCommittedOrigin().Serialize());
}
if (!effective_document_url.is_valid()) {
@@ -2454,8 +2500,8 @@ bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) {
mojom::APIPermissionID::kTab)) {
*error = ErrorUtils::FormatErrorMessage(
manifest_errors::kCannotAccessAboutUrl,
- rfh->GetLastCommittedURL().spec(),
- rfh->GetLastCommittedOrigin().Serialize());
+ render_frame_host->GetLastCommittedURL().spec(),
+ render_frame_host->GetLastCommittedOrigin().Serialize());
}
return false;
}
diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index 42c4e323d51..1e164b48f21 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -42,7 +42,7 @@
#endif
#if BUILDFLAG(IS_CHROMEOS)
-#include "chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_content_manager.h"
#endif
namespace extensions {
@@ -537,7 +537,8 @@ TEST_F(TabsApiUnitTest, TabsUpdateJavaScriptUrlNotAllowed) {
kFormatArgs, tab_id, "javascript:void(document.title = 'Won't work')");
std::string error = api_test_utils::RunFunctionAndReturnError(
function.get(), args, profile(), api_test_utils::FunctionMode::kNone);
- EXPECT_EQ(tabs_constants::kJavaScriptUrlsNotAllowedInTabsUpdate, error);
+ EXPECT_EQ(tabs_constants::kJavaScriptUrlsNotAllowedInExtensionNavigations,
+ error);
// Clean up.
browser()->tab_strip_model()->CloseAllTabs();
diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_apitest.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_apitest.cc
index fbac58ab101..1aa7cb74978 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_apitest.cc
@@ -11,7 +11,6 @@
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/scoped_disable_client_side_decorations_for_test.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/chrome_switches.h"
@@ -132,7 +131,13 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTabTest, TabAudible) {
<< message_;
}
-IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, Muted) {
+// TODO(crbug.com/1470083): Re-enable this test
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_Muted DISABLED_Muted
+#else
+#define MAYBE_Muted Muted
+#endif
+IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, MAYBE_Muted) {
ASSERT_TRUE(RunExtensionTest("tabs/basics/muted")) << message_;
}
@@ -146,11 +151,6 @@ IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, Duplicate) {
}
IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, Size) {
- // TODO(crbug.com/1240482): the test expectations fail if the window gets CSD
- // and becomes smaller because of that. Investigate this and remove the line
- // below if possible.
- ui::ScopedDisableClientSideDecorationsForTest scoped_disabled_csd;
-
ASSERT_TRUE(RunExtensionTest("tabs/basics/tab_size")) << message_;
}
@@ -206,7 +206,8 @@ IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, GetCurrent) {
ASSERT_TRUE(RunExtensionTest("tabs/get_current")) << message_;
}
-IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, Connect) {
+// Disabled for being flaky. See crbug.com/1472144
+IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, DISABLED_Connect) {
ASSERT_TRUE(RunExtensionTest("tabs/connect")) << message_;
}
diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_constants.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_constants.cc
index 3c8378d7f00..ed6d1a80111 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_constants.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_constants.cc
@@ -124,9 +124,9 @@ const char kCannotDetermineLanguageOfUnloadedTab[] =
const char kMissingLockWindowFullscreenPrivatePermission[] =
"Cannot lock window to fullscreen or close a locked fullscreen window "
"without lockWindowFullscreenPrivate manifest permission";
-const char kJavaScriptUrlsNotAllowedInTabsUpdate[] =
- "JavaScript URLs are not allowed in chrome.tabs.update. Use "
- "chrome.tabs.executeScript instead.";
+const char kJavaScriptUrlsNotAllowedInExtensionNavigations[] =
+ "JavaScript URLs are not allowed in API based extension navigations. Use "
+ "chrome.scripting.executeScript instead.";
const char kBrowserWindowNotAllowed[] = "Browser windows not allowed.";
const char kLockedFullscreenModeNewTabError[] =
"You cannot create new tabs while in locked fullscreen mode.";
@@ -142,6 +142,8 @@ const char kCannotHighlightTabs[] =
"progress.";
const char kNotAllowedForDevToolsError[] =
"Operation not allowed for DevTools windows";
+const char kFileUrlsNotAllowedInExtensionNavigations[] =
+ "Cannot navigate to a file URL without local file access.";
} // namespace tabs_constants
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_constants.h b/chromium/chrome/browser/extensions/api/tabs/tabs_constants.h
index fc1d59b4317..c68ccde74e3 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_constants.h
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_constants.h
@@ -78,6 +78,7 @@ extern const char kWindowTypeValueDevTools[];
// Error messages.
extern const char kCannotZoomDisabledTabError[];
+extern const char kFileUrlsNotAllowedInExtensionNavigations[];
extern const char kFrameNotFoundError[];
extern const char kNoCrashBrowserError[];
extern const char kNoCurrentWindowError[];
@@ -110,7 +111,7 @@ extern const char kScreenshotsDisabledByDlp[];
extern const char kCannotUpdateMuteCaptured[];
extern const char kCannotDetermineLanguageOfUnloadedTab[];
extern const char kMissingLockWindowFullscreenPrivatePermission[];
-extern const char kJavaScriptUrlsNotAllowedInTabsUpdate[];
+extern const char kJavaScriptUrlsNotAllowedInExtensionNavigations[];
extern const char kBrowserWindowNotAllowed[];
extern const char kLockedFullscreenModeNewTabError[];
extern const char kGroupParamsError[];
diff --git a/chromium/chrome/browser/extensions/api/tabs/windows_event_router.h b/chromium/chrome/browser/extensions/api/tabs/windows_event_router.h
index 9db3a0d58c4..f5cadaf2e34 100644
--- a/chromium/chrome/browser/extensions/api/tabs/windows_event_router.h
+++ b/chromium/chrome/browser/extensions/api/tabs/windows_event_router.h
@@ -88,7 +88,7 @@ class WindowsEventRouter : public AppWindowRegistry::Observer,
// The profile the currently focused window belongs to; either the main or
// incognito profile or NULL (none of the above). We remember this in order
// to correctly handle focus changes between non-OTR and OTR windows.
- raw_ptr<Profile, DanglingUntriaged> focused_profile_;
+ raw_ptr<Profile, AcrossTasksDanglingUntriaged> focused_profile_;
// The currently focused window. We keep this so as to avoid sending multiple
// windows.onFocusChanged events with the same windowId.
diff --git a/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc b/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc
index 2872ac2f667..b5156852191 100644
--- a/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc
@@ -12,6 +12,7 @@
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
@@ -46,7 +47,6 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/api/terminal_private.h"
#include "chromeos/ash/components/dbus/cicerone/cicerone_client.h"
diff --git a/chromium/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc b/chromium/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc
new file mode 100644
index 00000000000..c641309fa82
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc
@@ -0,0 +1,52 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "components/version_info/channel.h"
+#include "content/public/test/browser_test.h"
+#include "extensions/common/extension_features.h"
+#include "net/dns/mock_host_resolver.h"
+
+namespace extensions {
+
+class UserScriptsAPITest : public ExtensionApiTest {
+ public:
+ UserScriptsAPITest();
+ UserScriptsAPITest(const UserScriptsAPITest&) = delete;
+ const UserScriptsAPITest& operator=(const UserScriptsAPITest&) = delete;
+ ~UserScriptsAPITest() override = default;
+
+ void SetUpOnMainThread() override {
+ ExtensionApiTest::SetUpOnMainThread();
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(StartEmbeddedTestServer());
+ }
+
+ private:
+ // The userScripts API is currently behind a channel and feature restriction.
+ // TODO(crbug.com/1472902): Remove channel override when user scripts API goes
+ // to stable.
+ ScopedCurrentChannel current_channel_override_{
+ version_info::Channel::UNKNOWN};
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+UserScriptsAPITest::UserScriptsAPITest() {
+ scoped_feature_list_.InitAndEnableFeature(
+ extensions_features::kApiUserScripts);
+}
+
+IN_PROC_BROWSER_TEST_F(UserScriptsAPITest, RegisterUserScripts) {
+ ASSERT_TRUE(RunExtensionTest("user_scripts/register")) << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(UserScriptsAPITest, GetUserScripts) {
+ ASSERT_TRUE(RunExtensionTest("user_scripts/get_scripts")) << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(UserScriptsAPITest, UnregisterUserScripts) {
+ ASSERT_TRUE(RunExtensionTest("user_scripts/unregister")) << message_;
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 9984c0e75b2..15bb28c4fa6 100644
--- a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -13,6 +13,7 @@
#include "ash/public/cpp/clipboard_history_controller.h"
#include "ash/public/cpp/clipboard_image_model_factory.h"
#include "ash/public/cpp/keyboard/keyboard_types.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
#include "base/check.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
@@ -24,7 +25,6 @@
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-forward.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/crosapi/mojom/clipboard_history.mojom.h"
#include "components/user_manager/user_manager.h"
@@ -127,7 +127,8 @@ bool SendKeyEventImpl(const std::string& type,
SendProcessKeyEvent(ui::ET_KEY_PRESSED, host);
- ui::KeyEvent char_event(key_value, code, ui::DomCode::NONE, ui::EF_NONE);
+ ui::KeyEvent char_event = ui::KeyEvent::FromCharacter(
+ key_value, code, ui::DomCode::NONE, ui::EF_NONE);
if (tic)
tic->InsertChar(char_event);
SendProcessKeyEvent(ui::ET_KEY_RELEASED, host);
@@ -517,9 +518,6 @@ void ChromeVirtualKeyboardDelegate::OnHasInputDevices(
"newheader",
base::FeatureList::IsEnabled(ash::features::kVirtualKeyboardNewHeader)));
features.Append(GenerateFeatureFlag(
- "multitouch",
- base::FeatureList::IsEnabled(ash::features::kVirtualKeyboardMultitouch)));
- features.Append(GenerateFeatureFlag(
"roundCorners", base::FeatureList::IsEnabled(
ash::features::kVirtualKeyboardRoundCorners)));
features.Append(
@@ -540,6 +538,9 @@ void ChromeVirtualKeyboardDelegate::OnHasInputDevices(
features.Append(GenerateFeatureFlag(
"japanesefunctionrow",
base::FeatureList::IsEnabled(ash::features::kJapaneseFunctionRow)));
+ features.Append(GenerateFeatureFlag(
+ "virtualkeyboardremovenacl",
+ base::FeatureList::IsEnabled(ash::features::kVirtualKeyboardRemoveNacl)));
results.Set("features", std::move(features));
diff --git a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/virtual_keyboard_private_apitest.cc b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/virtual_keyboard_private_apitest.cc
index a1c970a77cf..92d43cdd507 100644
--- a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/virtual_keyboard_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/virtual_keyboard_private_apitest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "ash/public/cpp/clipboard_history_controller.h"
+#include "base/containers/flat_map.h"
#include "base/path_service.h"
#include "chrome/browser/ash/login/lock/screen_locker_tester.h"
#include "chrome/browser/extensions/extension_apitest.h"
@@ -31,7 +32,7 @@ void CopyBitmapItem() {
}
void CopyFileItem() {
- const std::unordered_map<std::u16string, std::u16string> input_data = {
+ const base::flat_map<std::u16string, std::u16string> input_data = {
{u"fs/sources", u"/path/to/My%20File.txt"}};
base::Pickle input_data_pickle;
ui::WriteCustomDataToPickle(input_data, &input_data_pickle);
diff --git a/chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc b/chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
index b3ab5fbff15..85c934eaf4e 100644
--- a/chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
@@ -16,7 +16,7 @@
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/common/extensions/api/vpn_provider.h"
#include "chromeos/ash/components/network/shill_property_handler.h"
-#include "chromeos/crosapi/mojom/vpn_service.mojom-test-utils.h"
+#include "chromeos/crosapi/mojom/vpn_service.mojom.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/pepper_vpn_provider_resource_host_proxy.h"
#include "content/public/browser/vpn_service_proxy.h"
@@ -41,7 +41,6 @@
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h"
#include "chromeos/crosapi/mojom/test_controller.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#endif
@@ -208,9 +207,11 @@ class VpnProviderApiTestLacros : public VpnProviderApiTestBase {
LOG(ERROR) << "Unsupported ash version.";
return false;
}
- crosapi::mojom::TestControllerAsyncWaiter waiter{
- service->GetRemote<crosapi::mojom::TestController>().get()};
- waiter.BindTestShillController(controller_.BindNewPipeAndPassReceiver());
+ base::test::TestFuture<void> future;
+ service->GetRemote<crosapi::mojom::TestController>()
+ ->BindTestShillController(controller_.BindNewPipeAndPassReceiver(),
+ future.GetCallback());
+ EXPECT_TRUE(future.Wait());
return true;
}
@@ -650,9 +651,9 @@ IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, PlatformMessage) {
extension_id(), remote.BindNewPipeAndPassReceiver(),
receiver.BindNewPipeAndPassRemote());
- crosapi::mojom::VpnServiceForExtensionAsyncWaiter waiter{remote.get()};
- crosapi::mojom::VpnErrorResponsePtr error;
- waiter.CreateConfiguration(kTestConfig, &error);
+ base::test::TestFuture<crosapi::mojom::VpnErrorResponsePtr> future;
+ remote->CreateConfiguration(kTestConfig, future.GetCallback());
+ auto error = future.Take();
ASSERT_FALSE(error) << "CreateConfiguration failed with |message| = "
<< error->message.value_or(std::string{});
diff --git a/chromium/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc b/chromium/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc
index 143cbd58c2d..275a78505da 100644
--- a/chromium/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc
+++ b/chromium/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc
@@ -33,8 +33,9 @@ bool FrameNavigationState::allow_extension_scheme_ = false;
DOCUMENT_USER_DATA_KEY_IMPL(FrameNavigationState);
-FrameNavigationState::FrameNavigationState(content::RenderFrameHost* rfh)
- : content::DocumentUserData<FrameNavigationState>(rfh) {}
+FrameNavigationState::FrameNavigationState(
+ content::RenderFrameHost* render_frame_host)
+ : content::DocumentUserData<FrameNavigationState>(render_frame_host) {}
FrameNavigationState::~FrameNavigationState() = default;
// static
diff --git a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
index 1639909dca1..e72083bb18a 100644
--- a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
+++ b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
@@ -430,11 +430,11 @@ bool WebNavigationTabObserver::IsReferenceFragmentNavigation(
}
void WebNavigationTabObserver::RenderFrameHostPendingDeletion(
- content::RenderFrameHost* pending_delete_rfh) {
- // The |pending_delete_rfh| and its children are now pending deletion.
- // Stop tracking them.
+ content::RenderFrameHost* pending_delete_render_frame_host) {
+ // The |pending_delete_render_frame_host| and its children are now pending
+ // deletion. Stop tracking them.
- pending_delete_rfh->ForEachRenderFrameHost(
+ pending_delete_render_frame_host->ForEachRenderFrameHost(
[this](content::RenderFrameHost* render_frame_host) {
auto* navigation_state =
FrameNavigationState::GetForCurrentDocument(render_frame_host);
diff --git a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index e6308c358c0..3d5386ddb06 100644
--- a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -111,8 +111,9 @@ class DelayLoadStartAndExecuteJavascript : public TabStripModelObserver,
// WebContentsObserver:
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override {
- if (navigation_handle->GetURL() != delay_url_ || !rfh_)
+ if (navigation_handle->GetURL() != delay_url_ || !render_frame_host_) {
return;
+ }
auto throttle =
std::make_unique<WillStartRequestObserverThrottle>(navigation_handle);
@@ -120,11 +121,11 @@ class DelayLoadStartAndExecuteJavascript : public TabStripModelObserver,
navigation_handle->RegisterThrottleForTesting(std::move(throttle));
if (has_user_gesture_) {
- rfh_->ExecuteJavaScriptWithUserGestureForTests(base::UTF8ToUTF16(script_),
- base::NullCallback());
+ render_frame_host_->ExecuteJavaScriptWithUserGestureForTests(
+ base::UTF8ToUTF16(script_), base::NullCallback());
} else {
- rfh_->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script_),
- base::NullCallback());
+ render_frame_host_->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script_),
+ base::NullCallback());
}
script_was_executed_ = true;
}
@@ -143,7 +144,7 @@ class DelayLoadStartAndExecuteJavascript : public TabStripModelObserver,
}
if (navigation_handle->IsInMainFrame())
- rfh_ = navigation_handle->GetRenderFrameHost();
+ render_frame_host_ = navigation_handle->GetRenderFrameHost();
}
void set_has_user_gesture(bool has_user_gesture) {
@@ -184,7 +185,8 @@ class DelayLoadStartAndExecuteJavascript : public TabStripModelObserver,
std::string script_;
bool has_user_gesture_ = false;
bool script_was_executed_ = false;
- raw_ptr<content::RenderFrameHost, DanglingUntriaged> rfh_ = nullptr;
+ raw_ptr<content::RenderFrameHost, AcrossTasksDanglingUntriaged>
+ render_frame_host_ = nullptr;
};
// Handles requests for URLs with paths of "/test*" sent to the test server, so
@@ -479,6 +481,7 @@ IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, UserAction) {
params.is_editable = false;
params.media_type = blink::mojom::ContextMenuDataMediaType::kNone;
params.page_url = url;
+ params.frame_url = url;
params.link_url = extension->GetResourceURL("b.html");
// Get the child frame, which will be the one associated with the context
diff --git a/chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
index 9955d59dad3..95f82df0033 100644
--- a/chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
@@ -39,6 +39,7 @@
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/api/declarative_net_request/test_utils.h"
+#include "extensions/browser/api/web_request/extension_web_request_event_router.h"
#include "extensions/browser/api/web_request/upload_data_presenter.h"
#include "extensions/browser/api/web_request/web_request_api.h"
#include "extensions/browser/api/web_request/web_request_api_constants.h"
@@ -151,12 +152,12 @@ TEST_F(ExtensionWebRequestTest, AddAndRemoveListeners) {
// Add two listeners.
ExtensionWebRequestEventRouter::GetInstance()->AddEventListener(
- &profile_, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName1,
+ &profile_, ext_id, ext_id, kEventName, kSubEventName1,
ExtensionWebRequestEventRouter::RequestFilter(), 0,
1 /* render_process_id */, 0, extensions::kMainThreadId,
blink::mojom::kInvalidServiceWorkerVersionId);
ExtensionWebRequestEventRouter::GetInstance()->AddEventListener(
- &profile_, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName2,
+ &profile_, ext_id, ext_id, kEventName, kSubEventName2,
ExtensionWebRequestEventRouter::RequestFilter(), 0,
1 /* render_process_id */, 0, extensions::kMainThreadId,
blink::mojom::kInvalidServiceWorkerVersionId);
@@ -167,20 +168,18 @@ TEST_F(ExtensionWebRequestTest, AddAndRemoveListeners) {
// Now remove the listeners one at a time, verifying the counts after each
// removal.
- ExtensionWebRequestEventRouter::GetInstance()->UpdateActiveListener(
- ExtensionWebRequestEventRouter::ListenerUpdateType::kRemove,
- ExtensionWebRequestEventRouter::GetBrowserContextID(&profile_), ext_id,
- kSubEventName1, extensions::kMainThreadId,
+ ExtensionWebRequestEventRouter::GetInstance()->UpdateActiveListenerForTesting(
+ &profile_, ExtensionWebRequestEventRouter::ListenerUpdateType::kRemove,
+ ext_id, kSubEventName1, extensions::kMainThreadId,
blink::mojom::kInvalidServiceWorkerVersionId);
EXPECT_EQ(
1u,
ExtensionWebRequestEventRouter::GetInstance()->GetListenerCountForTesting(
&profile_, kEventName));
- ExtensionWebRequestEventRouter::GetInstance()->UpdateActiveListener(
- ExtensionWebRequestEventRouter::ListenerUpdateType::kRemove,
- ExtensionWebRequestEventRouter::GetBrowserContextID(&profile_), ext_id,
- kSubEventName2, extensions::kMainThreadId,
+ ExtensionWebRequestEventRouter::GetInstance()->UpdateActiveListenerForTesting(
+ &profile_, ExtensionWebRequestEventRouter::ListenerUpdateType::kRemove,
+ ext_id, kSubEventName2, extensions::kMainThreadId,
blink::mojom::kInvalidServiceWorkerVersionId);
EXPECT_EQ(
0u,
@@ -200,23 +199,23 @@ TEST_F(ExtensionWebRequestTest, BrowserContextShutdown) {
const std::string kSubEventName = kEventName + "/1";
EXPECT_EQ(0u,
event_router->GetListenerCountForTesting(&profile_, kEventName));
- EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerImpl(&profile_));
+ EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerForTesting(&profile_));
// Add two listeners for the main profile.
event_router->AddEventListener(
- &profile_, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName,
+ &profile_, ext_id, ext_id, kEventName, kSubEventName,
ExtensionWebRequestEventRouter::RequestFilter(), 0,
1 /* render_process_id */, 0, extensions::kMainThreadId,
blink::mojom::kInvalidServiceWorkerVersionId);
event_router->AddEventListener(
- &profile_, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName,
+ &profile_, ext_id, ext_id, kEventName, kSubEventName,
ExtensionWebRequestEventRouter::RequestFilter(), 0,
2 /* render_process_id */, 0, extensions::kMainThreadId,
blink::mojom::kInvalidServiceWorkerVersionId);
event_router->IncrementExtraHeadersListenerCount(&profile_);
EXPECT_EQ(2u,
event_router->GetListenerCountForTesting(&profile_, kEventName));
- EXPECT_TRUE(event_router->HasAnyExtraHeadersListenerImpl(&profile_));
+ EXPECT_TRUE(event_router->HasAnyExtraHeadersListenerForTesting(&profile_));
// Create an off-the-record profile.
auto otr_profile_id = Profile::OTRProfileID::CreateUniqueForTesting();
@@ -232,29 +231,29 @@ TEST_F(ExtensionWebRequestTest, BrowserContextShutdown) {
event_router->OnOTRBrowserContextCreated(&profile_, otr_profile);
EXPECT_EQ(0u,
event_router->GetListenerCountForTesting(otr_profile, kEventName));
- EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerImpl(otr_profile));
+ EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerForTesting(otr_profile));
// Add two listeners for the otr profile.
event_router->AddEventListener(
- otr_profile, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName,
+ otr_profile, ext_id, ext_id, kEventName, kSubEventName,
ExtensionWebRequestEventRouter::RequestFilter(), 0,
1 /* render_process_id */, 0, extensions::kMainThreadId,
blink::mojom::kInvalidServiceWorkerVersionId);
event_router->AddEventListener(
- otr_profile, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName,
+ otr_profile, ext_id, ext_id, kEventName, kSubEventName,
ExtensionWebRequestEventRouter::RequestFilter(), 0,
2 /* render_process_id */, 0, extensions::kMainThreadId,
blink::mojom::kInvalidServiceWorkerVersionId);
event_router->IncrementExtraHeadersListenerCount(otr_profile);
EXPECT_EQ(2u,
event_router->GetListenerCountForTesting(otr_profile, kEventName));
- EXPECT_TRUE(event_router->HasAnyExtraHeadersListenerImpl(otr_profile));
+ EXPECT_TRUE(event_router->HasAnyExtraHeadersListenerForTesting(otr_profile));
// Simulate the OTR being destroyed.
event_router->OnOTRBrowserContextDestroyed(&profile_, otr_profile);
EXPECT_EQ(0u,
event_router->GetListenerCountForTesting(otr_profile, kEventName));
- EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerImpl(otr_profile));
+ EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerForTesting(otr_profile));
// We can't just delete the profile, because the call comes through the
// WebRequestAPI instance for that profile, and creating that requires
@@ -263,7 +262,7 @@ TEST_F(ExtensionWebRequestTest, BrowserContextShutdown) {
event_router->OnBrowserContextShutdown(&profile_);
EXPECT_EQ(0u,
event_router->GetListenerCountForTesting(&profile_, kEventName));
- EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerImpl(&profile_));
+ EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerForTesting(&profile_));
}
namespace {
diff --git a/chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 6b76ea8fcb3..5c19a78e7dc 100644
--- a/chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -98,6 +98,7 @@
#include "content/public/test/url_loader_interceptor.h"
#include "content/public/test/url_loader_monitor.h"
#include "content/public/test/web_transport_simple_test_server.h"
+#include "extensions/browser/api/web_request/extension_web_request_event_router.h"
#include "extensions/browser/api/web_request/web_request_api.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/browser/blocked_action_type.h"
@@ -1290,8 +1291,8 @@ IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiTestWithContextType,
// Check that reloading an extension that runs in incognito split mode and
// has two active background pages with registered events does not crash the
// browser. Regression test for http://crbug.com/224094
-// Flaky on linux-lacros. See http://crbug.com/1423252
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
+// Flaky on linux-lacros and Linux. See http://crbug.com/1423252
+#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
#define MAYBE_IncognitoSplitModeReload DISABLED_IncognitoSplitModeReload
#else
#define MAYBE_IncognitoSplitModeReload IncognitoSplitModeReload
@@ -2837,7 +2838,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionWebRequestMockedClockTest,
LoadExtension(test_dir.AppendASCII("extension_1"));
ASSERT_TRUE(extension_1);
ASSERT_TRUE(ready_1_listener.WaitUntilSatisfied());
- const std::string extension_id_1 = extension_1->id();
+ const ExtensionId extension_id_1 = extension_1->id();
// Load the second extension.
ExtensionTestMessageListener ready_2_listener("ready_2");
@@ -2845,7 +2846,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionWebRequestMockedClockTest,
LoadExtension(test_dir.AppendASCII("extension_2"));
ASSERT_TRUE(extension_2);
ASSERT_TRUE(ready_2_listener.WaitUntilSatisfied());
- const std::string extension_id_2 = extension_2->id();
+ const ExtensionId extension_id_2 = extension_2->id();
const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
EXPECT_LT(prefs->GetLastUpdateTime(extension_id_1),
@@ -3667,8 +3668,14 @@ IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiTestWithContextType,
// Ensure we don't strip off initiator incorrectly in web request events when
// both the normal and incognito contexts are active. Regression test for
// crbug.com/934398.
+// Flaky on Linux. See http://crbug.com/1423252
+#if BUILDFLAG(IS_LINUX)
+#define MAYBE_Initiator_SplitIncognito DISABLED_Initiator_SplitIncognito
+#else
+#define MAYBE_Initiator_SplitIncognito Initiator_SplitIncognito
+#endif
IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiTestWithContextType,
- Initiator_SplitIncognito) {
+ MAYBE_Initiator_SplitIncognito) {
embedded_test_server()->ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
@@ -5602,6 +5609,7 @@ IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest, WebRequestBlocking) {
EXPECT_EQ(net::OK, nav_observer.last_net_error_code());
}
+ base::HistogramTester histogram_tester;
// Now, navigate to block.example. This navigation should be blocked.
{
content::TestNavigationObserver nav_observer(web_contents);
@@ -5610,6 +5618,16 @@ IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest, WebRequestBlocking) {
embedded_test_server()->GetURL("block.example", "/simple.html")));
EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, nav_observer.last_net_error_code());
}
+
+ // TODO(crbug.com/1441221): Create more targeted tests to confirm when metrics
+ // should be firing or not.
+ // Web request API events should not have metrics emitted for SW/MV3.
+ histogram_tester.ExpectTotalCount(
+ "Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2",
+ /*expected_count=*/0);
+ histogram_tester.ExpectTotalCount(
+ "Extensions.Events.DispatchToAckLongTime.ExtensionServiceWorker2",
+ /*expected_count=*/0);
}
// Tests a service worker-based extension registering multiple webRequest events
@@ -6420,6 +6438,61 @@ IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest,
<< errors[0]->message();
}
+// Tests that an extension that doesn't have the `webView` permission cannot
+// manually create and add a WebRequestEvent that specifies a webViewInstanceId.
+// TODO(tjudkins): It would be good to also stop this on the JS layer by not
+// allowing extensions to manually create and add WebRequestEvents.
+// Regression test for crbug.com/1472830
+IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest,
+ TestWebviewIdSpecifiedOnEvent_NoPermission) {
+ ASSERT_TRUE(StartEmbeddedTestServer());
+
+ static constexpr char kManifest[] =
+ R"({
+ "name": "MV3 WebRequest",
+ "version": "0.1",
+ "manifest_version": 3,
+ "permissions": ["webRequest"],
+ "host_permissions": [ "http://example.com/*" ],
+ "background": {"service_worker": "background.js"}
+ })";
+ // The extension tries to add a listener; this will fail asynchronously
+ // as a part of the webRequestInternal API trying to add the listener.
+ // This results in runtime.lastError being set, but since it's an
+ // internal API, there's no way for the extension to catch the error.
+ static constexpr char kBackgroundJs[] =
+ R"(let event = new chrome.webRequest.onBeforeRequest.constructor(
+ 'webRequest.onBeforeRequest',
+ undefined,
+ undefined,
+ undefined,
+ 1); // webViewInstanceId
+ event.addListener(() => {},
+ {urls: ['*://*.example.com/*']});)";
+
+ // Since we can't catch the error in the extension's JS, we instead listen to
+ // the error come into the error console.
+ ErrorConsoleTestObserver error_observer(1u, profile());
+ error_observer.EnableErrorCollection();
+
+ // Load the extension and wait for the error to come.
+ TestExtensionDir test_dir;
+ test_dir.WriteManifest(kManifest);
+ test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundJs);
+ const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+
+ ASSERT_TRUE(extension);
+ error_observer.WaitForErrors();
+
+ const ErrorList& errors =
+ ErrorConsole::Get(profile())->GetErrorsForExtension(extension->id());
+ ASSERT_EQ(1u, errors.size());
+ EXPECT_EQ(u"Unchecked runtime.lastError: Missing webview permission.",
+ errors[0]->message());
+ EXPECT_EQ(0u, web_request_router()->GetListenerCountForTesting(
+ profile(), "webRequest.onBeforeRequest"));
+}
+
IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest, RecordUkmOnNavigation) {
ASSERT_TRUE(StartEmbeddedTestServer());
TestExtensionDir test_dir1;
diff --git a/chromium/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc b/chromium/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc
index 020d47ebc04..b4f4a634e88 100644
--- a/chromium/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc
+++ b/chromium/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc
@@ -27,10 +27,9 @@ ChromeWebViewInternalContextMenusCreateFunction::Run() {
MenuItem::Id id(
Profile::FromBrowserContext(browser_context())->IsOffTheRecord(),
- MenuItem::ExtensionKey(
- extension_id(),
- GetSenderWebContents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
- params->instance_id));
+ MenuItem::ExtensionKey(extension_id(),
+ render_frame_host()->GetProcess()->GetID(),
+ params->instance_id));
if (params->create_properties.id) {
id.string_uid = *params->create_properties.id;
@@ -62,10 +61,9 @@ ChromeWebViewInternalContextMenusUpdateFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
MenuItem::Id item_id(
profile->IsOffTheRecord(),
- MenuItem::ExtensionKey(
- extension_id(),
- GetSenderWebContents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
- params->instance_id));
+ MenuItem::ExtensionKey(extension_id(),
+ render_frame_host()->GetProcess()->GetID(),
+ params->instance_id));
if (params->id.as_string)
item_id.string_uid = *params->id.as_string;
@@ -92,10 +90,9 @@ ChromeWebViewInternalContextMenusRemoveFunction::Run() {
MenuItem::Id id(
Profile::FromBrowserContext(browser_context())->IsOffTheRecord(),
- MenuItem::ExtensionKey(
- extension_id(),
- GetSenderWebContents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
- params->instance_id));
+ MenuItem::ExtensionKey(extension_id(),
+ render_frame_host()->GetProcess()->GetID(),
+ params->instance_id));
if (params->menu_item_id.as_string) {
id.string_uid = *params->menu_item_id.as_string;
@@ -127,8 +124,7 @@ ChromeWebViewInternalContextMenusRemoveAllFunction::Run() {
MenuManager* menu_manager =
MenuManager::Get(Profile::FromBrowserContext(browser_context()));
menu_manager->RemoveAllContextItems(MenuItem::ExtensionKey(
- extension_id(),
- GetSenderWebContents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ extension_id(), render_frame_host()->GetProcess()->GetID(),
params->instance_id));
return RespondNow(NoArguments());
diff --git a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc
index ed1df85124e..dd65d77b192 100644
--- a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h"
#include <memory>
+#include <string>
#include <utility>
#include <vector>
@@ -12,6 +13,8 @@
#include "base/lazy_instance.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/media/webrtc/media_device_salt_service_factory.h"
+#include "components/media_device_salt/media_device_salt_service.h"
#include "content/public/browser/audio_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
@@ -23,6 +26,7 @@
#include "extensions/common/error_utils.h"
#include "extensions/common/permissions/permissions_data.h"
#include "media/audio/audio_system.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "url/gurl.h"
#include "url/origin.h"
@@ -102,7 +106,12 @@ WebrtcAudioPrivateFunction::WebrtcAudioPrivateFunction() {}
WebrtcAudioPrivateFunction::~WebrtcAudioPrivateFunction() {}
+url::Origin WebrtcAudioPrivateFunction::GetExtensionOrigin() const {
+ return url::Origin::Create(source_url());
+}
+
std::string WebrtcAudioPrivateFunction::CalculateHMAC(
+ const std::string& extension_salt,
const std::string& raw_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -114,18 +123,40 @@ std::string WebrtcAudioPrivateFunction::CalculateHMAC(
if (media::AudioDeviceDescription::IsDefaultDevice(raw_id))
return media::AudioDeviceDescription::kDefaultDeviceId;
- url::Origin security_origin =
- url::Origin::Create(source_url().DeprecatedGetOriginAsURL());
- return content::GetHMACForMediaDeviceID(device_id_salt(), security_origin,
+ return content::GetHMACForMediaDeviceID(extension_salt, GetExtensionOrigin(),
raw_id);
}
-void WebrtcAudioPrivateFunction::InitDeviceIDSalt() {
- device_id_salt_ = browser_context()->GetMediaDeviceIDSalt();
+void WebrtcAudioPrivateFunction::GetSalt(
+ const url::Origin& origin,
+ base::OnceCallback<void(const std::string&)> salt_callback) {
+ media_device_salt::MediaDeviceSaltService* salt_service =
+ MediaDeviceSaltServiceFactory::GetInstance()->GetForBrowserContext(
+ browser_context());
+ if (!salt_service) {
+ std::move(salt_callback).Run(browser_context()->UniqueId());
+ return;
+ }
+
+ salt_service->GetSalt(blink::StorageKey::CreateFirstParty(origin),
+ std::move(salt_callback));
+}
+
+void WebrtcAudioPrivateFunction::GetSaltAndDeviceDescriptions(
+ const url::Origin& origin,
+ bool is_input_devices,
+ SaltAndDeviceDescriptionsCallback callback) {
+ GetSalt(origin, base::BindOnce(
+ &WebrtcAudioPrivateFunction::GotSaltForDeviceDescriptions,
+ this, is_input_devices, std::move(callback)));
}
-std::string WebrtcAudioPrivateFunction::device_id_salt() const {
- return device_id_salt_;
+void WebrtcAudioPrivateFunction::GotSaltForDeviceDescriptions(
+ bool is_input_devices,
+ SaltAndDeviceDescriptionsCallback callback,
+ const std::string& device_id_salt) {
+ GetAudioSystem()->GetDeviceDescriptions(
+ is_input_devices, base::BindOnce(std::move(callback), device_id_salt));
}
media::AudioSystem* WebrtcAudioPrivateFunction::GetAudioSystem() {
@@ -137,9 +168,9 @@ media::AudioSystem* WebrtcAudioPrivateFunction::GetAudioSystem() {
ExtensionFunction::ResponseAction WebrtcAudioPrivateGetSinksFunction::Run() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- InitDeviceIDSalt();
- GetAudioSystem()->GetDeviceDescriptions(
- false,
+ GetSaltAndDeviceDescriptions(
+ GetExtensionOrigin(),
+ /*is_input_devices=*/false,
base::BindOnce(
&WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions,
this));
@@ -147,12 +178,13 @@ ExtensionFunction::ResponseAction WebrtcAudioPrivateGetSinksFunction::Run() {
}
void WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions(
+ const std::string& extension_salt,
media::AudioDeviceDescriptions sink_devices) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto results = std::make_unique<SinkInfoVector>();
for (const media::AudioDeviceDescription& description : sink_devices) {
wap::SinkInfo info;
- info.sink_id = CalculateHMAC(description.unique_id);
+ info.sink_id = CalculateHMAC(extension_salt, description.unique_id);
info.sink_label = description.device_name;
// TODO(joi): Add other parameters.
results->push_back(std::move(info));
@@ -161,38 +193,37 @@ void WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions(
}
WebrtcAudioPrivateGetAssociatedSinkFunction::
- WebrtcAudioPrivateGetAssociatedSinkFunction() {}
+ WebrtcAudioPrivateGetAssociatedSinkFunction() = default;
WebrtcAudioPrivateGetAssociatedSinkFunction::
- ~WebrtcAudioPrivateGetAssociatedSinkFunction() {}
+ ~WebrtcAudioPrivateGetAssociatedSinkFunction() = default;
ExtensionFunction::ResponseAction
WebrtcAudioPrivateGetAssociatedSinkFunction::Run() {
params_ = wap::GetAssociatedSink::Params::Create(args());
DCHECK_CURRENTLY_ON(BrowserThread::UI);
EXTENSION_FUNCTION_VALIDATE(params_);
- InitDeviceIDSalt();
-
- GetAudioSystem()->GetDeviceDescriptions(
- true, base::BindOnce(&WebrtcAudioPrivateGetAssociatedSinkFunction::
- ReceiveInputDeviceDescriptions,
- this));
+ url::Origin origin = url::Origin::Create(GURL(params_->security_origin));
+ GetSaltAndDeviceDescriptions(
+ origin, /*is_input_devices=*/true,
+ base::BindOnce(&WebrtcAudioPrivateGetAssociatedSinkFunction::
+ ReceiveInputDeviceDescriptions,
+ this, origin));
return RespondLater();
}
void WebrtcAudioPrivateGetAssociatedSinkFunction::
ReceiveInputDeviceDescriptions(
+ const url::Origin& origin,
+ const std::string& salt,
media::AudioDeviceDescriptions source_devices) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- url::Origin security_origin =
- url::Origin::Create(GURL(params_->security_origin));
std::string source_id_in_origin(params_->source_id_in_origin);
// Find the raw source ID for source_id_in_origin.
std::string raw_source_id;
for (const auto& device : source_devices) {
- if (content::DoesMediaDeviceIDMatchHMAC(device_id_salt(), security_origin,
- source_id_in_origin,
+ if (content::DoesMediaDeviceIDMatchHMAC(salt, origin, source_id_in_origin,
device.unique_id)) {
raw_source_id = device.unique_id;
DVLOG(2) << "Found raw ID " << raw_source_id
@@ -201,22 +232,32 @@ void WebrtcAudioPrivateGetAssociatedSinkFunction::
}
}
if (raw_source_id.empty()) {
- CalculateHMACAndReply(absl::nullopt);
+ Reply(media::AudioDeviceDescription::kDefaultDeviceId);
return;
}
+ GetSalt(GetExtensionOrigin(),
+ base::BindOnce(
+ &WebrtcAudioPrivateGetAssociatedSinkFunction::GotExtensionSalt,
+ this, raw_source_id));
+}
+
+void WebrtcAudioPrivateGetAssociatedSinkFunction::GotExtensionSalt(
+ const std::string& raw_source_id,
+ const std::string& extension_salt) {
GetAudioSystem()->GetAssociatedOutputDeviceID(
raw_source_id,
base::BindOnce(
&WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACAndReply,
- this));
+ this, extension_salt));
}
void WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACAndReply(
+ const std::string& extension_salt,
const absl::optional<std::string>& raw_sink_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!raw_sink_id || !raw_sink_id->empty());
// If no |raw_sink_id| is provided, the default device is used.
- Reply(CalculateHMAC(raw_sink_id.value_or(std::string())));
+ Reply(CalculateHMAC(extension_salt, raw_sink_id.value_or(std::string())));
}
void WebrtcAudioPrivateGetAssociatedSinkFunction::Reply(
diff --git a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h
index 708e02161d3..8d5d6c7c83e 100644
--- a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h
+++ b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h
@@ -52,31 +52,40 @@ class WebrtcAudioPrivateEventService
// Common base for WebrtcAudioPrivate functions, that provides a
// couple of optionally-used common implementations.
class WebrtcAudioPrivateFunction : public ExtensionFunction {
- protected:
- WebrtcAudioPrivateFunction();
-
+ public:
WebrtcAudioPrivateFunction(const WebrtcAudioPrivateFunction&) = delete;
WebrtcAudioPrivateFunction& operator=(const WebrtcAudioPrivateFunction&) =
delete;
+ protected:
+ WebrtcAudioPrivateFunction();
~WebrtcAudioPrivateFunction() override;
- protected:
- // Calculates a single HMAC, using the extension ID as the security origin.
- std::string CalculateHMAC(const std::string& raw_id);
+ using SaltAndDeviceDescriptionsCallback =
+ base::OnceCallback<void(const std::string&,
+ media::AudioDeviceDescriptions)>;
+ // Calculates the HMAC for `raw_id` using extension ID as the security origin.
+ // `extension_salt` must be the salt for the extension ID.
+ std::string CalculateHMAC(const std::string& extension_salt,
+ const std::string& raw_id);
- // Initializes |device_id_salt_|. Must be called on the UI thread,
- // before any calls to |device_id_salt()|.
- void InitDeviceIDSalt();
+ // Returns the extension ID as an origin.
+ url::Origin GetExtensionOrigin() const;
- // Callable from any thread. Must previously have called
- // |InitDeviceIDSalt()|.
- std::string device_id_salt() const;
+ void GetSalt(const url::Origin&,
+ base::OnceCallback<void(const std::string&)> salt_callback);
+
+ // Returns the device ID salt and device descriptions.
+ void GetSaltAndDeviceDescriptions(const url::Origin& security_origin,
+ bool is_input_devices,
+ SaltAndDeviceDescriptionsCallback callback);
media::AudioSystem* GetAudioSystem();
private:
- std::string device_id_salt_;
+ void GotSaltForDeviceDescriptions(bool is_input_devices,
+ SaltAndDeviceDescriptionsCallback callback,
+ const std::string& device_id_salt);
std::unique_ptr<media::AudioSystem> audio_system_;
};
@@ -95,6 +104,7 @@ class WebrtcAudioPrivateGetSinksFunction : public WebrtcAudioPrivateFunction {
// Receives output device descriptions, calculates HMACs for them and sends
// the response.
void ReceiveOutputDeviceDescriptions(
+ const std::string& extension_salt,
media::AudioDeviceDescriptions sink_devices);
};
@@ -116,10 +126,16 @@ class WebrtcAudioPrivateGetAssociatedSinkFunction
// Receives the input device descriptions, looks up the raw source device ID
// basing on |params|, and requests the associated raw sink ID for it.
void ReceiveInputDeviceDescriptions(
+ const url::Origin& origin,
+ const std::string& salt,
media::AudioDeviceDescriptions source_devices);
- // Receives the raw sink ID, calculates HMAC and calls Reply().
- void CalculateHMACAndReply(const absl::optional<std::string>& raw_sink_id);
+ void GotExtensionSalt(const std::string& raw_source_id,
+ const std::string& extension_salt);
+
+ // Calculates HMAC and calls Reply().
+ void CalculateHMACAndReply(const std::string& extension_salt,
+ const absl::optional<std::string>& raw_sink_id);
// Receives the associated sink ID as HMAC and sends the response.
void Reply(const std::string& hmac);
diff --git a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc
index f6c71b8346d..f3a5ab79336 100644
--- a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc
@@ -23,6 +23,7 @@
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/media/webrtc/media_device_salt_service_factory.h"
#include "chrome/browser/media/webrtc/webrtc_log_uploader.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/recently_audible_helper.h"
@@ -30,6 +31,7 @@
#include "chrome/common/buildflags.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
+#include "components/media_device_salt/media_device_salt_service.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "content/public/browser/audio_service.h"
#include "content/public/browser/browser_thread.h"
@@ -49,6 +51,7 @@
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/windows_version.h"
@@ -123,6 +126,16 @@ class WebrtcAudioPrivateTest : public AudioWaitingExtensionTest {
return RunFunctionAndReturnSingleResult(function.get(), "[]", profile());
}
+ std::string GetMediaDeviceIDSalt(const url::Origin& origin) {
+ media_device_salt::MediaDeviceSaltService* salt_service =
+ MediaDeviceSaltServiceFactory::GetInstance()->GetForBrowserContext(
+ profile());
+ base::test::TestFuture<const std::string&> future;
+ salt_service->GetSalt(blink::StorageKey::CreateFirstParty(origin),
+ future.GetCallback());
+ return future.Get();
+ }
+
GURL source_url_;
};
@@ -152,13 +165,12 @@ IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, GetSinks) {
const std::string* sink_id = dict.FindString("sinkId");
EXPECT_TRUE(sink_id);
+ url::Origin origin = url::Origin::Create(source_url_);
std::string expected_id =
media::AudioDeviceDescription::IsDefaultDevice(it->unique_id)
? media::AudioDeviceDescription::kDefaultDeviceId
- : content::GetHMACForMediaDeviceID(
- profile()->GetMediaDeviceIDSalt(),
- url::Origin::Create(source_url_.DeprecatedGetOriginAsURL()),
- it->unique_id);
+ : content::GetHMACForMediaDeviceID(GetMediaDeviceIDSalt(origin),
+ origin, it->unique_id);
EXPECT_EQ(expected_id, *sink_id);
const std::string* sink_label = dict.FindString("sinkLabel");
@@ -189,13 +201,13 @@ IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, GetAssociatedSink) {
std::string raw_device_id = device.unique_id;
VLOG(2) << "Trying to find associated sink for device " << raw_device_id;
- GURL origin(GURL("http://www.google.com/").DeprecatedGetOriginAsURL());
+ GURL gurl("http://www.google.com/");
+ url::Origin origin = url::Origin::Create(gurl);
std::string source_id_in_origin = content::GetHMACForMediaDeviceID(
- profile()->GetMediaDeviceIDSalt(), url::Origin::Create(origin),
- raw_device_id);
+ GetMediaDeviceIDSalt(origin), origin, raw_device_id);
base::Value::List parameters;
- parameters.Append(origin.spec());
+ parameters.Append(gurl.spec());
parameters.Append(source_id_in_origin);
std::string parameter_string;
JSONWriter::Write(parameters, &parameter_string);
diff --git a/chromium/chrome/browser/extensions/api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc b/chromium/chrome/browser/extensions/api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc
index db80d6505a6..7db82d71476 100644
--- a/chromium/chrome/browser/extensions/api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc
@@ -48,15 +48,16 @@ WebrtcDesktopCapturePrivateChooseDesktopMediaFunction::Run() {
absl::optional<Params> params = Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
- content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
- params->request.guest_process_id,
- params->request.guest_render_frame_id);
+ content::RenderFrameHost* render_frame_host =
+ content::RenderFrameHost::FromID(params->request.guest_process_id,
+ params->request.guest_render_frame_id);
- if (!rfh) {
+ if (!render_frame_host) {
return RespondNow(Error(kTargetNotFoundError));
}
- GURL origin = rfh->GetLastCommittedURL().DeprecatedGetOriginAsURL();
+ GURL origin =
+ render_frame_host->GetLastCommittedURL().DeprecatedGetOriginAsURL();
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kAllowHttpScreenCapture) &&
!network::IsUrlPotentiallyTrustworthy(origin)) {
@@ -74,8 +75,8 @@ WebrtcDesktopCapturePrivateChooseDesktopMediaFunction::Run() {
// suppressLocalAudioPlaybackIntended here.
return Execute(*sources, /*exclude_system_audio=*/false,
/*exclude_self_browser_surface=*/false,
- /*suppress_local_audio_playback_intended=*/false, rfh, origin,
- target_name);
+ /*suppress_local_audio_playback_intended=*/false,
+ render_frame_host, origin, target_name);
}
WebrtcDesktopCapturePrivateCancelChooseDesktopMediaFunction::
diff --git a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc
index 34fbd329f70..05b58430520 100644
--- a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc
@@ -995,7 +995,7 @@ class WebrtcLoggingPrivateApiStartEventLoggingTestInIncognitoMode
bool WebRtcEventLogCollectionPolicy() const override { return true; }
private:
- raw_ptr<Browser, DanglingUntriaged> browser_{
+ raw_ptr<Browser, AcrossTasksDanglingUntriaged> browser_{
nullptr}; // Does not own the object.
};
diff --git a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index 4da2a1cf861..6431da933a5 100644
--- a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -69,8 +69,6 @@
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-// TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build
-// flag to #if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/supervised_user/supervised_user_browser_utils.h"
#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
#include "components/supervised_user/core/browser/supervised_user_service.h"
@@ -223,17 +221,9 @@ const char kEphemeralAppLaunchingNotSupported[] =
"Ephemeral launching of apps is no longer supported.";
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-// Note that the following error doesn't mean an incorrect password was entered,
-// nor that the parent permisison request was canceled by the user, but rather
-// that the Parent permission request after credential entry and acceptance
-// failed due to either a network connection error or some unsatisfied invariant
-// that prevented the request from completing.
-const char kWebstoreParentPermissionFailedError[] =
- "Parent permission request failed";
-
const char kParentBlockedExtensionInstallError[] =
"Parent has blocked extension/app installation";
-#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#endif
// The number of user gestures to trace back for the referrer chain.
const int kExtensionReferrerUserGestureLimit = 2;
@@ -658,7 +648,9 @@ void WebstorePrivateBeginInstallWithManifest3Function::
OnExtensionApprovalCanceled() {
if (test_delegate) {
test_delegate->OnExtensionInstallFailure(
- dummy_extension_->id(), kWebstoreParentPermissionFailedError,
+ dummy_extension_->id(),
+ l10n_util::GetStringUTF8(
+ IDS_EXTENSIONS_SUPERVISED_USER_PARENTAL_PERMISSION_FAILURE),
WebstoreInstaller::FailureReason::FAILURE_REASON_CANCELLED);
}
@@ -669,12 +661,16 @@ void WebstorePrivateBeginInstallWithManifest3Function::
OnExtensionApprovalFailed() {
if (test_delegate) {
test_delegate->OnExtensionInstallFailure(
- dummy_extension_->id(), kWebstoreParentPermissionFailedError,
+ dummy_extension_->id(),
+ l10n_util::GetStringUTF8(
+ IDS_EXTENSIONS_SUPERVISED_USER_PARENTAL_PERMISSION_FAILURE),
WebstoreInstaller::FailureReason::FAILURE_REASON_OTHER);
}
- Respond(BuildResponse(api::webstore_private::RESULT_UNKNOWN_ERROR,
- kWebstoreParentPermissionFailedError));
+ Respond(BuildResponse(
+ api::webstore_private::RESULT_UNKNOWN_ERROR,
+ l10n_util::GetStringUTF8(
+ IDS_EXTENSIONS_SUPERVISED_USER_PARENTAL_PERMISSION_FAILURE)));
}
void WebstorePrivateBeginInstallWithManifest3Function::
@@ -856,12 +852,23 @@ WebstorePrivateBeginInstallWithManifest3Function::BuildResponse(
api::webstore_private::Result result,
const std::string& error) {
if (result != api::webstore_private::RESULT_SUCCESS) {
+ // TODO(tjudkins): We should not be using ErrorWithArguments here as it
+ // doesn't play well with promise based API calls (only emitting the error
+ // and dropping the arguments). In almost every case the error directly
+ // responds with the result enum value returned, so instead we should drop
+ // the error and have the caller just base logic on the enum value alone.
+ // In the cases where they do not correspond we should add a new enum value.
+ // We will need to ensure that the Webstore is entirely basing its logic on
+ // the result alone before removing the error.
return ErrorWithArguments(
BeginInstallWithManifest3::Results::Create(result), error);
}
- // The web store expects an empty string on success, so don't use
+ // The old Webstore expects an empty string on success, so don't use
// RESULT_SUCCESS here.
+ // TODO(crbug.com/709120): The new Webstore accepts either the empty string or
+ // RESULT_SUCCESS on success now, so once the old Webstore is turned down this
+ // can be changed over.
return ArgumentList(BeginInstallWithManifest3::Results::Create(
api::webstore_private::RESULT_EMPTY_STRING));
}
@@ -1264,11 +1271,11 @@ WebstorePrivateGetReferrerChainFunction::Run() {
return RespondNow(ArgumentList(
api::webstore_private::GetReferrerChain::Results::Create("")));
- content::RenderFrameHost* rfh = render_frame_host();
- content::RenderFrameHost* outermost_rfh =
- rfh ? rfh->GetOutermostMainFrame() : nullptr;
+ content::RenderFrameHost* outermost_render_frame_host =
+ render_frame_host() ? render_frame_host()->GetOutermostMainFrame()
+ : nullptr;
- if (!outermost_rfh) {
+ if (!outermost_render_frame_host) {
return RespondNow(ErrorWithArguments(
api::webstore_private::GetReferrerChain::Results::Create(""),
kWebstoreUserCancelledError));
@@ -1281,7 +1288,8 @@ WebstorePrivateGetReferrerChainFunction::Run() {
safe_browsing::ReferrerChain referrer_chain;
SafeBrowsingNavigationObserverManager::AttributionResult result =
navigation_observer_manager->IdentifyReferrerChainByRenderFrameHost(
- outermost_rfh, kExtensionReferrerUserGestureLimit, &referrer_chain);
+ outermost_render_frame_host, kExtensionReferrerUserGestureLimit,
+ &referrer_chain);
// If the referrer chain is incomplete we'll append the most recent
// navigations to referrer chain for diagnostic purposes. This only happens if
diff --git a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.h b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
index 957ab11765a..3626267125e 100644
--- a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
+++ b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
@@ -26,8 +26,6 @@
#include "third_party/skia/include/core/SkBitmap.h"
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-// TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build
-// flag to #if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h"
#include "extensions/browser/supervised_user_extensions_delegate.h"
#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS)
diff --git a/chromium/chrome/browser/favicon/BUILD.gn b/chromium/chrome/browser/favicon/BUILD.gn
index 2920e35e4a2..8ab176c634d 100644
--- a/chromium/chrome/browser/favicon/BUILD.gn
+++ b/chromium/chrome/browser/favicon/BUILD.gn
@@ -28,6 +28,7 @@ source_set("favicon") {
"//components/favicon/core:history_implementation",
"//components/image_fetcher/core",
"//components/keyed_service/content",
+ "//components/password_manager/content/common",
"//components/sync",
"//content/public/browser",
"//ui/gfx",
diff --git a/chromium/chrome/browser/feature_engagement/BUILD.gn b/chromium/chrome/browser/feature_engagement/BUILD.gn
index 72334e95a46..9d9d7b237bf 100644
--- a/chromium/chrome/browser/feature_engagement/BUILD.gn
+++ b/chromium/chrome/browser/feature_engagement/BUILD.gn
@@ -7,26 +7,15 @@ import("//build/config/android/rules.gni")
android_library("java") {
srcjar_deps = [ ":jni_headers" ]
- sources = [
- "java/src/org/chromium/chrome/browser/feature_engagement/ScreenshotMonitor.java",
- "java/src/org/chromium/chrome/browser/feature_engagement/ScreenshotMonitorDelegate.java",
- "java/src/org/chromium/chrome/browser/feature_engagement/ScreenshotMonitorImpl.java",
- "java/src/org/chromium/chrome/browser/feature_engagement/ScreenshotTabObserver.java",
- "java/src/org/chromium/chrome/browser/feature_engagement/TrackerFactory.java",
- ]
+ sources = [ "java/src/org/chromium/chrome/browser/feature_engagement/TrackerFactory.java" ]
deps = [
"//base:base_java",
"//base:jni_java",
- "//build/android:build_java",
"//chrome/browser/profiles/android:java",
- "//chrome/browser/tab:java",
"//components/feature_engagement/public:public_java",
- "//components/ukm/android:java",
- "//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_core_core_java",
- "//ui/android:ui_full_java",
]
}
diff --git a/chromium/chrome/browser/feed/android/BUILD.gn b/chromium/chrome/browser/feed/android/BUILD.gn
index d213fe87075..9d681a01f8b 100644
--- a/chromium/chrome/browser/feed/android/BUILD.gn
+++ b/chromium/chrome/browser/feed/android/BUILD.gn
@@ -13,6 +13,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/feed/BackToTopBubble.java",
"java/src/org/chromium/chrome/browser/feed/BackToTopBubbleScrollListener.java",
"java/src/org/chromium/chrome/browser/feed/CardMenuBottomSheetContent.java",
+ "java/src/org/chromium/chrome/browser/feed/ColorProviderImpl.java",
"java/src/org/chromium/chrome/browser/feed/FeedActionDelegate.java",
"java/src/org/chromium/chrome/browser/feed/FeedBubbleDelegate.java",
"java/src/org/chromium/chrome/browser/feed/FeedContentFirstLoadWatcher.java",
@@ -25,6 +26,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/feed/FeedProcessScopeDependencyProvider.java",
"java/src/org/chromium/chrome/browser/feed/FeedReliabilityLogger.java",
"java/src/org/chromium/chrome/browser/feed/FeedReliabilityLoggingBridge.java",
+ "java/src/org/chromium/chrome/browser/feed/FeedResourceFetcher.java",
"java/src/org/chromium/chrome/browser/feed/FeedScrollState.java",
"java/src/org/chromium/chrome/browser/feed/FeedServiceBridge.java",
"java/src/org/chromium/chrome/browser/feed/FeedServiceDependencyProviderFactory.java",
@@ -35,6 +37,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/feed/FeedSurfaceDelegate.java",
"java/src/org/chromium/chrome/browser/feed/FeedSurfaceLifecycleManager.java",
"java/src/org/chromium/chrome/browser/feed/FeedSurfaceProvider.java",
+ "java/src/org/chromium/chrome/browser/feed/FeedSurfaceRendererBridge.java",
"java/src/org/chromium/chrome/browser/feed/FeedSurfaceScopeDependencyProviderImpl.java",
"java/src/org/chromium/chrome/browser/feed/FeedSurfaceScrollDelegate.java",
"java/src/org/chromium/chrome/browser/feed/FeedSurfaceTracker.java",
@@ -60,8 +63,6 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementItemView.java",
"java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementItemViewBinder.java",
"java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementMediator.java",
- "java/src/org/chromium/chrome/browser/feed/hooks/FeedHooks.java",
- "java/src/org/chromium/chrome/browser/feed/hooks/FeedHooksImpl.java",
"java/src/org/chromium/chrome/browser/feed/sections/OnSectionHeaderSelectedListener.java",
"java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawable.java",
"java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderListProperties.java",
@@ -115,12 +116,14 @@ android_library("java") {
"//chrome/browser/user_education:java",
"//chrome/browser/util:java",
"//chrome/browser/xsurface:java",
+ "//chrome/browser/xsurface_provider:java",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/settings/android:java",
"//components/browser_ui/share/android:java",
"//components/browser_ui/styles/android:java",
"//components/browser_ui/util/android:java",
"//components/browser_ui/widget/android:java",
+ "//components/embedder_support/android:simple_factory_key_java",
"//components/favicon/android:java",
"//components/feature_engagement:feature_engagement_java",
"//components/feed/core/proto:proto_java_v2",
@@ -151,16 +154,6 @@ android_library("java") {
]
resources_package = "org.chromium.chrome.browser.feed"
-
- # Add the actual implementation where necessary so that downstream targets
- # can provide their own implementations.
- jar_excluded_patterns = [ "*/FeedHooksImpl*.class" ]
-}
-
-android_library("hooks_public_impl_java") {
- sources =
- [ "java/src/org/chromium/chrome/browser/feed/hooks/FeedHooksImpl.java" ]
- deps = [ ":java" ]
}
android_resources("feed_java_resources") {
@@ -170,11 +163,13 @@ android_resources("feed_java_resources") {
"java/res/color/menu_footer_chip_background_baseline_list.xml",
"java/res/color/menu_footer_chip_background_list.xml",
"java/res/color/tab_layout_text_color_list.xml",
+ "java/res/drawable-night/header_title_tab_selected_background_polished.xml",
"java/res/drawable/back_to_top_arrow.xml",
"java/res/drawable/creator_content_unavailable_error_illustration.xml",
"java/res/drawable/creator_general_error_illustration.xml",
"java/res/drawable/header_title_section_tab_background.xml",
"java/res/drawable/header_title_tab_selected_background.xml",
+ "java/res/drawable/header_title_tab_selected_background_polished.xml",
"java/res/drawable/rounded_corners.xml",
"java/res/drawable/web_feed_post_follow_illustration.xml",
"java/res/layout/back_to_top_bubble.xml",
@@ -219,7 +214,8 @@ generate_jni("jni_headers") {
"java/src/org/chromium/chrome/browser/feed/FeedProcessScopeDependencyProvider.java",
"java/src/org/chromium/chrome/browser/feed/FeedReliabilityLoggingBridge.java",
"java/src/org/chromium/chrome/browser/feed/FeedServiceBridge.java",
- "java/src/org/chromium/chrome/browser/feed/FeedStream.java",
+ "java/src/org/chromium/chrome/browser/feed/FeedSurfaceRendererBridge.java",
+ "java/src/org/chromium/chrome/browser/feed/FeedSurfaceScopeDependencyProviderImpl.java",
"java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java",
]
}
@@ -228,7 +224,7 @@ android_library("javatests") {
testonly = true
resources_package = "org.chromium.chrome.browser.feed.test"
- sources = [ "java/src/org/chromium/chrome/browser/feed/FeedProcessScopeDependencyProviderNativeTest.java" ]
+ sources = [ "java/src/org/chromium/chrome/browser/feed/FeedSurfaceScopeDependencyProviderImplTest.java" ]
deps = [
":feed_java_resources",
@@ -237,6 +233,7 @@ android_library("javatests") {
"//chrome/browser/feature_engagement:java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
+ "//chrome/browser/privacy:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/settings:test_support_java",
"//chrome/browser/signin/services/android:java",
@@ -262,6 +259,7 @@ android_library("javatests") {
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
@@ -313,6 +311,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/feature_engagement:java",
"//chrome/browser/feedback/android:java",
"//chrome/browser/flags:java",
@@ -351,6 +350,7 @@ robolectric_library("junit") {
"//third_party/android_deps:protobuf_lite_runtime_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+ "//third_party/androidx:androidx_browser_browser_java",
"//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_core_java",
@@ -367,7 +367,6 @@ robolectric_library("junit") {
"//ui/android:ui_recycler_view_java",
"//ui/base/mojom:mojom_java",
"//url:gurl_java",
- "//url:gurl_junit_shadows",
"//url:gurl_junit_test_support",
]
resources_package = "org.chromium.chrome.browser.feed.test"
diff --git a/chromium/chrome/browser/feedback/android/BUILD.gn b/chromium/chrome/browser/feedback/android/BUILD.gn
index 0375832add4..4333cf43075 100644
--- a/chromium/chrome/browser/feedback/android/BUILD.gn
+++ b/chromium/chrome/browser/feedback/android/BUILD.gn
@@ -33,7 +33,6 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/feedback/DeviceInfoFeedbackSource.java",
"java/src/org/chromium/chrome/browser/feedback/FamilyInfoFeedbackSource.java",
"java/src/org/chromium/chrome/browser/feedback/FeedbackContextFeedbackSource.java",
- "java/src/org/chromium/chrome/browser/feedback/FeedbackReporter.java",
"java/src/org/chromium/chrome/browser/feedback/FragmentHelpAndFeedbackLauncher.java",
"java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncher.java",
"java/src/org/chromium/chrome/browser/feedback/IMEFeedbackSource.java",
diff --git a/chromium/chrome/browser/first_run/android/BUILD.gn b/chromium/chrome/browser/first_run/android/BUILD.gn
index bf2db93b175..a1f24a6927e 100644
--- a/chromium/chrome/browser/first_run/android/BUILD.gn
+++ b/chromium/chrome/browser/first_run/android/BUILD.gn
@@ -10,6 +10,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/firstrun/MobileFreProgress.java",
]
deps = [
+ "//base:base_java",
"//chrome/browser/preferences:java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
diff --git a/chromium/chrome/browser/flags/BUILD.gn b/chromium/chrome/browser/flags/BUILD.gn
index be222f2e653..dc6a77d26c9 100644
--- a/chromium/chrome/browser/flags/BUILD.gn
+++ b/chromium/chrome/browser/flags/BUILD.gn
@@ -16,7 +16,6 @@ android_library("java") {
"android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureMap.java",
"android/java/src/org/chromium/chrome/browser/flags/ChromeSessionState.java",
"android/java/src/org/chromium/chrome/browser/flags/DoubleCachedFieldTrialParameter.java",
- "android/java/src/org/chromium/chrome/browser/flags/FeatureParamUtils.java",
"android/java/src/org/chromium/chrome/browser/flags/IntCachedFieldTrialParameter.java",
"android/java/src/org/chromium/chrome/browser/flags/MutableFlagWithSafeDefault.java",
"android/java/src/org/chromium/chrome/browser/flags/PostNativeFlag.java",
@@ -43,7 +42,6 @@ android_library("java") {
generate_jni("jni_headers") {
sources = [
- "android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java",
"android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureMap.java",
"android/java/src/org/chromium/chrome/browser/flags/ChromeSessionState.java",
]
@@ -51,8 +49,6 @@ generate_jni("jni_headers") {
static_library("flags_android") {
sources = [
- "android/cached_feature_flags.cc",
- "android/cached_feature_flags.h",
"android/chrome_session_state.cc",
"android/chrome_session_state.h",
]
@@ -80,12 +76,10 @@ robolectric_library("flags_junit_tests") {
"android/java/src/org/chromium/chrome/browser/flags/BaseFlagTestRule.java",
"android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsAnnotationUnitTest.java",
"android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java",
- "android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsUnitTest.java",
"android/java/src/org/chromium/chrome/browser/flags/CachedFieldTrialParameterUnitTest.java",
"android/java/src/org/chromium/chrome/browser/flags/CachedFlagUnitTest.java",
"android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithProcessorUnitTest.java",
"android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureListWithoutProcessorUnitTest.java",
- "android/java/src/org/chromium/chrome/browser/flags/FeatureParamUtilsUnitTest.java",
"android/java/src/org/chromium/chrome/browser/flags/FlagUnitTest.java",
"android/java/src/org/chromium/chrome/browser/flags/MutableFlagWithSafeDefaultUnitTest.java",
"android/java/src/org/chromium/chrome/browser/flags/PostNativeFlagUnitTest.java",
@@ -115,6 +109,7 @@ android_library("javatests") {
"//base:base_java",
"//base:base_java_test_support",
"//base/test:test_support_java",
+ "//chrome/android:chrome_java",
"//chrome/test/android:chrome_java_integration_test_support",
"//third_party/android_sdk:android_test_mock_java",
"//third_party/androidx:androidx_test_runner_java",
diff --git a/chromium/chrome/browser/gcm/gcm_profile_service_factory.cc b/chromium/chrome/browser/gcm/gcm_profile_service_factory.cc
index 0046cbbbe59..c8172d83831 100644
--- a/chromium/chrome/browser/gcm/gcm_profile_service_factory.cc
+++ b/chromium/chrome/browser/gcm/gcm_profile_service_factory.cc
@@ -89,9 +89,19 @@ GCMProfileServiceFactory::ScopedTestingFactoryInstaller::
// static
GCMProfileService* GCMProfileServiceFactory::GetForProfile(
content::BrowserContext* profile) {
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+ // On desktop, incognito profiles are checked with IsIncognitoProfile().
+ // It's possible for non-incognito profiles to also be off-the-record.
+ bool is_profile_supported =
+ !Profile::FromBrowserContext(profile)->IsIncognitoProfile();
+#else
+ bool is_profile_supported = !profile->IsOffTheRecord();
+#endif
+
// GCM is not supported in incognito mode.
- if (profile->IsOffTheRecord())
+ if (!is_profile_supported) {
return nullptr;
+ }
return static_cast<GCMProfileService*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
@@ -108,8 +118,6 @@ GCMProfileServiceFactory::GCMProfileServiceFactory()
"GCMProfileService",
ProfileSelections::Builder()
.WithRegular(ProfileSelection::kOwnInstance)
- // TODO(crbug.com/1418376): Check if this service is needed in
- // Guest mode.
.WithGuest(ProfileSelection::kOwnInstance)
.Build()) {
DependsOn(IdentityManagerFactory::GetInstance());
@@ -121,7 +129,11 @@ GCMProfileServiceFactory::~GCMProfileServiceFactory() {
KeyedService* GCMProfileServiceFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+ DCHECK(!profile->IsIncognitoProfile());
+#else
DCHECK(!profile->IsOffTheRecord());
+#endif
TestingFactory& testing_factory = GetTestingFactory();
if (testing_factory)
diff --git a/chromium/chrome/browser/gcm/instance_id/instance_id_profile_service_factory.cc b/chromium/chrome/browser/gcm/instance_id/instance_id_profile_service_factory.cc
index fa7cd8019c2..8912610c025 100644
--- a/chromium/chrome/browser/gcm/instance_id/instance_id_profile_service_factory.cc
+++ b/chromium/chrome/browser/gcm/instance_id/instance_id_profile_service_factory.cc
@@ -16,9 +16,23 @@ namespace instance_id {
// static
InstanceIDProfileService* InstanceIDProfileServiceFactory::GetForProfile(
content::BrowserContext* profile) {
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+ // On desktop, the guest profile is actually the primary OTR profile of
+ // the "regular" guest profile. The regular guest profile is never used
+ // directly by users. Also, user are not able to create child OTR profiles
+ // from guest profiles, the menu item "New incognito window" is not
+ // available. So, if this is a guest session, allow it only if it is a
+ // child OTR profile as well.
+ bool is_profile_supported =
+ !Profile::FromBrowserContext(profile)->IsIncognitoProfile();
+#else
+ bool is_profile_supported = !profile->IsOffTheRecord();
+#endif
+
// Instance ID is not supported in incognito mode.
- if (profile->IsOffTheRecord())
+ if (!is_profile_supported) {
return nullptr;
+ }
return static_cast<InstanceIDProfileService*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
@@ -36,8 +50,6 @@ InstanceIDProfileServiceFactory::InstanceIDProfileServiceFactory()
"InstanceIDProfileService",
ProfileSelections::Builder()
.WithRegular(ProfileSelection::kOwnInstance)
- // TODO(crbug.com/1418376): Check if this service is needed in
- // Guest mode.
.WithGuest(ProfileSelection::kOwnInstance)
.Build()) {
// GCM is needed for device ID.
@@ -49,9 +61,17 @@ InstanceIDProfileServiceFactory::~InstanceIDProfileServiceFactory() = default;
KeyedService* InstanceIDProfileServiceFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+ // On desktop, incognito profiles are checked with IsIncognitoProfile().
+ // It's possible for non-incognito profiles to also be off-the-record.
+ bool is_incognito = profile->IsIncognitoProfile();
+#else
+ bool is_incognito = profile->IsOffTheRecord();
+#endif
+
return new InstanceIDProfileService(
gcm::GCMProfileServiceFactory::GetForProfile(profile)->driver(),
- profile->IsOffTheRecord());
+ is_incognito);
}
} // namespace instance_id
diff --git a/chromium/chrome/browser/headless/BUILD.gn b/chromium/chrome/browser/headless/BUILD.gn
index d48c5be494f..36c31d20160 100644
--- a/chromium/chrome/browser/headless/BUILD.gn
+++ b/chromium/chrome/browser/headless/BUILD.gn
@@ -12,6 +12,8 @@ source_set("headless") {
"headless_mode_util.h",
]
+ configs += [ "//build/config/compiler:wexit_time_destructors" ]
+
deps = [
"//base",
"//chrome/common:non_code_constants",
@@ -32,6 +34,8 @@ source_set("metrics") {
"headless_mode_metrics.h",
]
+ configs += [ "//build/config/compiler:wexit_time_destructors" ]
+
deps = [
":headless",
"//base",
@@ -46,9 +50,12 @@ source_set("command_processor") {
"headless_command_processor.h",
]
+ configs += [ "//build/config/compiler:wexit_time_destructors" ]
+
deps = [
":headless",
"//base",
+ "//chrome/common:non_code_constants",
"//components/headless/command_handler",
"//content/public/browser",
]
@@ -84,10 +91,6 @@ if (!is_android) {
sources += [ "headless_mode_browsertest_win.cc" ]
}
- if (is_apple) {
- configs += [ "//build/config/compiler:enable_arc" ]
- }
-
if (is_mac) {
sources += [ "headless_mode_browsertest_mac.mm" ]
}
@@ -98,6 +101,7 @@ if (!is_android) {
"//chrome/browser",
"//chrome/test:test_support",
"//content/test:test_support",
+ "//pdf",
"//testing/gtest",
]
diff --git a/chromium/chrome/browser/history_clusters/BUILD.gn b/chromium/chrome/browser/history_clusters/BUILD.gn
index f503088d992..839472523da 100644
--- a/chromium/chrome/browser/history_clusters/BUILD.gn
+++ b/chromium/chrome/browser/history_clusters/BUILD.gn
@@ -72,6 +72,7 @@ android_library("java") {
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
+ "//ui/accessibility:ax_base_java",
"//ui/android:ui_no_recycler_view_java",
"//ui/android:ui_recycler_view_java",
"//ui/android:ui_utils_java",
diff --git a/chromium/chrome/browser/image_descriptions/BUILD.gn b/chromium/chrome/browser/image_descriptions/BUILD.gn
index 96fed314de5..551f8916225 100644
--- a/chromium/chrome/browser/image_descriptions/BUILD.gn
+++ b/chromium/chrome/browser/image_descriptions/BUILD.gn
@@ -31,6 +31,7 @@ android_library("java") {
"//net/android:net_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_preference_preference_java",
+ "//ui/accessibility:ax_base_java",
"//ui/android:ui_full_java",
]
@@ -69,6 +70,7 @@ android_library("unit_device_javatests") {
":java",
":java_resources",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/browser/device:java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
@@ -104,6 +106,7 @@ android_library("javatests") {
":java",
":java_resources",
"//base:base_java_test_support",
+ "//chrome/android:chrome_java",
"//chrome/browser/device:java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
@@ -117,6 +120,7 @@ android_library("javatests") {
"//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/hamcrest:hamcrest_core_java",
diff --git a/chromium/chrome/browser/image_editor/BUILD.gn b/chromium/chrome/browser/image_editor/BUILD.gn
index 5fff74ba1d8..3acbdae3dd1 100644
--- a/chromium/chrome/browser/image_editor/BUILD.gn
+++ b/chromium/chrome/browser/image_editor/BUILD.gn
@@ -39,7 +39,6 @@ static_library("image_editor") {
"event_capture_mac.h",
"event_capture_mac.mm",
]
- configs += [ "//build/config/compiler:enable_arc" ]
deps += [ "//components/remote_cocoa/app_shim" ]
}
diff --git a/chromium/chrome/browser/incognito/BUILD.gn b/chromium/chrome/browser/incognito/BUILD.gn
index eccefc91c69..b6c611172ac 100644
--- a/chromium/chrome/browser/incognito/BUILD.gn
+++ b/chromium/chrome/browser/incognito/BUILD.gn
@@ -53,6 +53,7 @@ android_library("java") {
"//components/device_reauth:device_reauth_java_enums",
"//components/prefs/android:java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/android_deps:dagger_java",
"//third_party/android_deps:javax_inject_javax_inject_java",
"//third_party/androidx:androidx_activity_activity_java",
@@ -111,9 +112,11 @@ android_library("incognito_java_tests") {
"//components/browser_ui/widget/android:java",
"//components/prefs/android:java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_core_java",
@@ -144,6 +147,7 @@ robolectric_library("incognito_junit_tests") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
"//chrome/android/features/tab_ui/public:java",
"//chrome/browser/android/lifecycle:java",
@@ -155,9 +159,11 @@ robolectric_library("incognito_junit_tests") {
"//chrome/browser/tabmodel:java",
"//chrome/browser/ui/android/layouts:java",
"//chrome/test/android:chrome_java_unit_test_support",
+ "//components/browser_ui/settings/android:java",
"//components/browser_ui/widget/android:java",
"//components/prefs/android:java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/androidx:androidx_activity_activity_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit:junit",
diff --git a/chromium/chrome/browser/lacros/cros_apps/api/BUILD.gn b/chromium/chrome/browser/lacros/cros_apps/api/BUILD.gn
new file mode 100644
index 00000000000..18e6f81aa68
--- /dev/null
+++ b/chromium/chrome/browser/lacros/cros_apps/api/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_lacros)
+
+source_set("browser_tests") {
+ testonly = true
+
+ sources = [ "cros_apps_api_browsertest.cc" ]
+
+ defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+ deps = [
+ # TODO(b/298329656): We shouldn't depend on things in .../extensions/. Ideally,
+ # this target would be in //chrome/browser/chromeos/ or //chromeos/, and then
+ # we could depend on it.
+ "//chrome/browser/chromeos/extensions/telemetry/api/telemetry:test_support",
+ "//chrome/browser/ui",
+ "//chrome/test:test_support_ui",
+ "//chromeos/constants",
+ "//chromeos/lacros",
+ "//content/test:test_support",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/chrome/browser/lacros/cros_apps/api/diagnostics/BUILD.gn b/chromium/chrome/browser/lacros/cros_apps/api/diagnostics/BUILD.gn
new file mode 100644
index 00000000000..d0bfb93258a
--- /dev/null
+++ b/chromium/chrome/browser/lacros/cros_apps/api/diagnostics/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_lacros)
+
+source_set("diagnostics") {
+ sources = [
+ "cros_diagnostics_impl.cc",
+ "cros_diagnostics_impl.h",
+ ]
+ deps = [
+ "//chromeos/crosapi/mojom",
+ "//chromeos/lacros",
+ "//content/public/browser",
+ "//content/public/common",
+ ]
+}
diff --git a/chromium/chrome/browser/language/android/BUILD.gn b/chromium/chrome/browser/language/android/BUILD.gn
index efb14802b94..7c41bcafc8e 100644
--- a/chromium/chrome/browser/language/android/BUILD.gn
+++ b/chromium/chrome/browser/language/android/BUILD.gn
@@ -27,7 +27,6 @@ android_library("java") {
srcjar_deps = [ ":jni_headers" ]
sources = [
"java/src/org/chromium/chrome/browser/language/AppLanguagePromoDialog.java",
- "java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java",
"java/src/org/chromium/chrome/browser/language/LanguageBridge.java",
"java/src/org/chromium/chrome/browser/language/settings/AlwaysTranslateListFragment.java",
"java/src/org/chromium/chrome/browser/language/settings/AppLanguagePreferenceDelegate.java",
@@ -96,8 +95,6 @@ android_resources("java_resources") {
"java/res/layout/app_language_prompt_content.xml",
"java/res/layout/app_language_prompt_more_languages.xml",
"java/res/layout/app_language_prompt_row.xml",
- "java/res/layout/language_ask_prompt_content.xml",
- "java/res/layout/language_ask_prompt_row.xml",
"java/res/layout/language_ask_prompt_row_separator.xml",
"java/res/layout/language_list_with_add_button.xml",
"java/res/layout/languages_preference.xml",
@@ -183,11 +180,13 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android:base_module_java",
"//chrome/browser/flags:java",
"//chrome/browser/language/android:java",
"//chrome/browser/preferences:java",
"//chrome/test/android:chrome_java_unit_test_support",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit",
diff --git a/chromium/chrome/browser/lens/BUILD.gn b/chromium/chrome/browser/lens/BUILD.gn
index 52e6227dff6..292f9f1b0d6 100644
--- a/chromium/chrome/browser/lens/BUILD.gn
+++ b/chromium/chrome/browser/lens/BUILD.gn
@@ -11,6 +11,7 @@ android_library("java") {
deps = [
":java_resources",
+ "//base:base_java",
"//chrome/browser/contextmenu:java",
"//chrome/browser/ui/android/strings:ui_strings_grd",
"//third_party/androidx:androidx_annotation_annotation_java",
@@ -34,6 +35,7 @@ android_library("delegate_java") {
deps = [
":java_resources",
":util_java",
+ "//base:base_java",
"//chrome/browser/contextmenu:java",
"//chrome/browser/ui/android/strings:ui_strings_grd",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/chrome/browser/loading_modal/android/BUILD.gn b/chromium/chrome/browser/loading_modal/android/BUILD.gn
index f76737d0948..ec4639c1bbc 100644
--- a/chromium/chrome/browser/loading_modal/android/BUILD.gn
+++ b/chromium/chrome/browser/loading_modal/android/BUILD.gn
@@ -60,6 +60,7 @@ android_library("unit_device_javatests") {
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
diff --git a/chromium/chrome/browser/long_screenshots/BUILD.gn b/chromium/chrome/browser/long_screenshots/BUILD.gn
index c5460b7625e..5cc88e4668c 100644
--- a/chromium/chrome/browser/long_screenshots/BUILD.gn
+++ b/chromium/chrome/browser/long_screenshots/BUILD.gn
@@ -12,7 +12,7 @@ source_set("services") {
]
deps = [
- "//chrome/browser/share/android:jni_headers",
+ "//chrome/android:chrome_jni_headers",
"//components/google/core/common:common",
"//components/keyed_service/core",
"//components/paint_preview/browser",
diff --git a/chromium/chrome/browser/mac/BUILD.gn b/chromium/chrome/browser/mac/BUILD.gn
index 3491d98a602..2131713754a 100644
--- a/chromium/chrome/browser/mac/BUILD.gn
+++ b/chromium/chrome/browser/mac/BUILD.gn
@@ -19,6 +19,4 @@ source_set("keystone_glue") {
"//components/version_info",
"//ui/base",
]
-
- configs += [ "//build/config/compiler:enable_arc" ]
}
diff --git a/chromium/chrome/browser/mandatory_reauth/android/internal/BUILD.gn b/chromium/chrome/browser/mandatory_reauth/android/internal/BUILD.gn
index b4f60eb2d71..5f5a75c2ad2 100644
--- a/chromium/chrome/browser/mandatory_reauth/android/internal/BUILD.gn
+++ b/chromium/chrome/browser/mandatory_reauth/android/internal/BUILD.gn
@@ -59,11 +59,13 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/mandatory_reauth/android:java",
"//chrome/test/android:chrome_java_unit_test_support",
"//components/autofill/android:main_autofill_java",
"//components/browser_ui/bottomsheet/android:java",
"//third_party/espresso:espresso_core_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
diff --git a/chromium/chrome/browser/media/effects/BUILD.gn b/chromium/chrome/browser/media/effects/BUILD.gn
new file mode 100644
index 00000000000..1262c9a7612
--- /dev/null
+++ b/chromium/chrome/browser/media/effects/BUILD.gn
@@ -0,0 +1,32 @@
+source_set("effects") {
+ deps = [
+ "//base",
+ "//chrome/browser/profiles:profile",
+ "//content/public/browser",
+ "//services/video_capture/public/mojom",
+ "//third_party/abseil-cpp:absl",
+ ]
+ sources = [
+ "media_effects_service.cc",
+ "media_effects_service.h",
+ "media_effects_service_factory.cc",
+ "media_effects_service_factory.h",
+ "video_effects_manager_impl.cc",
+ "video_effects_manager_impl.h",
+ ]
+}
+
+source_set("unittests") {
+ testonly = true
+ deps = [
+ ":effects",
+ "//base",
+ "//chrome/test:test_support",
+ "//testing/gtest",
+ ]
+ sources = [
+ "media_effects_service_factory_unittest.cc",
+ "media_effects_service_unittest.cc",
+ "video_effects_manager_impl_unittest.cc",
+ ]
+}
diff --git a/chromium/chrome/browser/media/router/BUILD.gn b/chromium/chrome/browser/media/router/BUILD.gn
index d1e6f36efb1..b770cb24779 100644
--- a/chromium/chrome/browser/media/router/BUILD.gn
+++ b/chromium/chrome/browser/media/router/BUILD.gn
@@ -123,8 +123,6 @@ static_library("router") {
"mojo/media_router_debugger_impl.h",
"mojo/media_router_desktop.cc",
"mojo/media_router_desktop.h",
- "mojo/media_router_mojo_impl.cc",
- "mojo/media_router_mojo_impl.h",
"mojo/media_router_mojo_metrics.cc",
"mojo/media_router_mojo_metrics.h",
"mojo/media_sink_service_status.cc",
@@ -305,7 +303,7 @@ source_set("unittests") {
"logger_list_unittest.cc",
"media_router_feature_unittest.cc",
"mojo/media_router_debugger_impl_unittest.cc",
- "mojo/media_router_mojo_impl_unittest.cc",
+ "mojo/media_router_desktop_unittest.cc",
"mojo/media_sink_service_status_unittest.cc",
"providers/cast/app_activity_unittest.cc",
"providers/cast/cast_activity_manager_unittest.cc",
@@ -348,7 +346,12 @@ source_set("unittests") {
"//third_party/openscreen/src/cast/common:public",
]
- if (!is_chromeos_lacros) {
+ if (is_chromeos_lacros) {
+ deps += [
+ "//chromeos/crosapi/mojom",
+ "//chromeos/lacros:lacros",
+ ]
+ } else {
sources += [
"discovery/access_code/access_code_cast_pref_updater_impl_unittest.cc",
]
diff --git a/chromium/chrome/browser/media/router/discovery/BUILD.gn b/chromium/chrome/browser/media/router/discovery/BUILD.gn
index df7ec8bc8aa..2363eff0fca 100644
--- a/chromium/chrome/browser/media/router/discovery/BUILD.gn
+++ b/chromium/chrome/browser/media/router/discovery/BUILD.gn
@@ -98,7 +98,6 @@ static_library("discovery") {
if (is_mac) {
sources += [ "discovery_network_list_wifi_mac.mm" ]
- configs += [ "//build/config/compiler:enable_arc" ]
frameworks = [ "CoreWLAN.framework" ]
}
diff --git a/chromium/chrome/browser/media/webrtc/audio_debug_recordings_handler.h b/chromium/chrome/browser/media/webrtc/audio_debug_recordings_handler.h
index fea4e80051c..bee81634dbc 100644
--- a/chromium/chrome/browser/media/webrtc/audio_debug_recordings_handler.h
+++ b/chromium/chrome/browser/media/webrtc/audio_debug_recordings_handler.h
@@ -87,7 +87,8 @@ class AudioDebugRecordingsHandler
const base::FilePath& log_directory);
// The browser context associated with our renderer process.
- const raw_ptr<content::BrowserContext, DanglingUntriaged> browser_context_;
+ const raw_ptr<content::BrowserContext, AcrossTasksDanglingUntriaged>
+ browser_context_;
// This counter allows saving each debug recording in separate files.
uint64_t current_audio_debug_recordings_id_;
diff --git a/chromium/chrome/browser/media/webrtc/capture_handle_browsertest.cc b/chromium/chrome/browser/media/webrtc/capture_handle_browsertest.cc
index 0745da8b705..414ffc4c7ea 100644
--- a/chromium/chrome/browser/media/webrtc/capture_handle_browsertest.cc
+++ b/chromium/chrome/browser/media/webrtc/capture_handle_browsertest.cc
@@ -165,7 +165,7 @@ struct TabInfo {
}
raw_ptr<Browser> browser;
- raw_ptr<WebContents, DanglingUntriaged> web_contents;
+ raw_ptr<WebContents, AcrossTasksDanglingUntriaged> web_contents;
int tab_strip_index;
std::string capture_handle; // Expected value for those who may observe.
};
@@ -319,7 +319,7 @@ class CaptureHandleBrowserTest : public WebRtcTestBase {
// Incognito browser.
// Note: The regular one is accessible via browser().
- raw_ptr<Browser, DanglingUntriaged> incognito_browser_ = nullptr;
+ raw_ptr<Browser, AcrossTasksDanglingUntriaged> incognito_browser_ = nullptr;
};
IN_PROC_BROWSER_TEST_F(CaptureHandleBrowserTest,
@@ -786,7 +786,8 @@ class CaptureHandleBrowserTestPrerender : public CaptureHandleBrowserTest {
protected:
std::unique_ptr<content::test::PrerenderTestHelper> prerender_helper_;
- raw_ptr<WebContents, DanglingUntriaged> captured_web_contents_ = nullptr;
+ raw_ptr<WebContents, AcrossTasksDanglingUntriaged> captured_web_contents_ =
+ nullptr;
};
// Verifies that pre-rendered pages don't change the capture handle config.
diff --git a/chromium/chrome/browser/media/webrtc/capture_policy_utils.cc b/chromium/chrome/browser/media/webrtc/capture_policy_utils.cc
index b879049715e..c637f9e2a5b 100644
--- a/chromium/chrome/browser/media/webrtc/capture_policy_utils.cc
+++ b/chromium/chrome/browser/media/webrtc/capture_policy_utils.cc
@@ -159,9 +159,9 @@ bool IsGetAllScreensMediaAllowedForAnySite(content::BrowserContext* context) {
if (!host_content_settings_map) {
return false;
}
- ContentSettingsForOneType content_settings;
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::ALL_SCREEN_CAPTURE, &content_settings);
+ ContentSettingsForOneType content_settings =
+ host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::ALL_SCREEN_CAPTURE);
return base::ranges::any_of(content_settings,
[](const ContentSettingPatternSource& source) {
return source.GetContentSetting() ==
diff --git a/chromium/chrome/browser/media/webrtc/multi_capture_browsertest.cc b/chromium/chrome/browser/media/webrtc/capture_policy_utils_browsertest.cc
index 94377cec2c9..94377cec2c9 100644
--- a/chromium/chrome/browser/media/webrtc/multi_capture_browsertest.cc
+++ b/chromium/chrome/browser/media/webrtc/capture_policy_utils_browsertest.cc
diff --git a/chromium/chrome/browser/media/webrtc/conditional_focus_browsertest.cc b/chromium/chrome/browser/media/webrtc/conditional_focus_browsertest.cc
index 9601dc0ace7..71f2776aeb2 100644
--- a/chromium/chrome/browser/media/webrtc/conditional_focus_browsertest.cc
+++ b/chromium/chrome/browser/media/webrtc/conditional_focus_browsertest.cc
@@ -172,8 +172,8 @@ class ConditionalFocusBrowserTest : public WebRtcTestBase {
}
protected:
- raw_ptr<WebContents, DanglingUntriaged> captured_tab_ = nullptr;
- raw_ptr<WebContents, DanglingUntriaged> capturing_tab_ = nullptr;
+ raw_ptr<WebContents, AcrossTasksDanglingUntriaged> captured_tab_ = nullptr;
+ raw_ptr<WebContents, AcrossTasksDanglingUntriaged> capturing_tab_ = nullptr;
};
// Flaky on Win bots and on linux release bots http://crbug.com/1264744
diff --git a/chromium/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc b/chromium/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
index 4543baa7b16..c2073e32b30 100644
--- a/chromium/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
@@ -34,7 +34,7 @@
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
#if BUILDFLAG(IS_CHROMEOS)
-#include "chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_content_manager.h"
#include "ui/aura/window.h"
#endif // BUILDFLAG(IS_CHROMEOS)
diff --git a/chromium/chrome/browser/media/webrtc/desktop_capturer_wrapper.cc b/chromium/chrome/browser/media/webrtc/desktop_capturer_wrapper.cc
new file mode 100644
index 00000000000..1c9f7f2050b
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/desktop_capturer_wrapper.cc
@@ -0,0 +1,41 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/webrtc/desktop_capturer_wrapper.h"
+
+#include "base/check.h"
+
+DesktopCapturerWrapper::DesktopCapturerWrapper(
+ std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer)
+ : desktop_capturer_(std::move(desktop_capturer)) {
+ CHECK(desktop_capturer_);
+}
+
+DesktopCapturerWrapper::~DesktopCapturerWrapper() = default;
+
+void DesktopCapturerWrapper::Start(Consumer* consumer) {
+ desktop_capturer_->Start(consumer);
+}
+
+ThumbnailCapturer::FrameDeliveryMethod
+DesktopCapturerWrapper::GetFrameDeliveryMethod() const {
+ return FrameDeliveryMethod::kOnRequest;
+}
+
+webrtc::DelegatedSourceListController*
+DesktopCapturerWrapper::GetDelegatedSourceListController() {
+ return desktop_capturer_->GetDelegatedSourceListController();
+}
+
+void DesktopCapturerWrapper::CaptureFrame() {
+ desktop_capturer_->CaptureFrame();
+}
+
+bool DesktopCapturerWrapper::GetSourceList(SourceList* sources) {
+ return desktop_capturer_->GetSourceList(sources);
+}
+
+bool DesktopCapturerWrapper::SelectSource(SourceId id) {
+ return desktop_capturer_->SelectSource(id);
+}
diff --git a/chromium/chrome/browser/media/webrtc/desktop_capturer_wrapper.h b/chromium/chrome/browser/media/webrtc/desktop_capturer_wrapper.h
new file mode 100644
index 00000000000..8e848c0ae0e
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/desktop_capturer_wrapper.h
@@ -0,0 +1,41 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_WEBRTC_DESKTOP_CAPTURER_WRAPPER_H_
+#define CHROME_BROWSER_MEDIA_WEBRTC_DESKTOP_CAPTURER_WRAPPER_H_
+
+#include <memory>
+#include <vector>
+
+#include "chrome/browser/media/webrtc/thumbnail_capturer.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
+
+// This class is a wrapper around a webrtc::DesktopCapturer object to make it
+// possible to use it as a ThumbnailCapturer in NativeDesktopMediaList. All
+// calls are forwarded directly to the webrtc::DesktopCapturer implementation.
+class DesktopCapturerWrapper : public ThumbnailCapturer {
+ public:
+ explicit DesktopCapturerWrapper(
+ std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer);
+
+ ~DesktopCapturerWrapper() override;
+
+ void Start(Consumer* consumer) override;
+
+ FrameDeliveryMethod GetFrameDeliveryMethod() const override;
+
+ webrtc::DelegatedSourceListController* GetDelegatedSourceListController()
+ override;
+
+ void CaptureFrame() override;
+
+ bool GetSourceList(SourceList* sources) override;
+
+ bool SelectSource(SourceId id) override;
+
+ private:
+ std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer_;
+};
+
+#endif // CHROME_BROWSER_MEDIA_WEBRTC_DESKTOP_CAPTURER_WRAPPER_H_
diff --git a/chromium/chrome/browser/media/webrtc/desktop_media_list_ash.cc b/chromium/chrome/browser/media/webrtc/desktop_media_list_ash.cc
index 0ae576c4107..17af28e277f 100644
--- a/chromium/chrome/browser/media/webrtc/desktop_media_list_ash.cc
+++ b/chromium/chrome/browser/media/webrtc/desktop_media_list_ash.cc
@@ -115,16 +115,17 @@ void DesktopMediaListAsh::EnumerateSources(
if (update_thumbnails)
CaptureThumbnail(screen_source.id, root_windows[i]);
} else {
- // The list of desks containers depends on whether the Virtual Desks
- // feature is enabled or not.
- for (int desk_id : ash::desks_util::GetDesksContainersIds())
+ for (int desk_id : ash::desks_util::GetDesksContainersIds()) {
EnumerateWindowsForRoot(sources, update_thumbnails, root_windows[i],
desk_id);
+ }
EnumerateWindowsForRoot(sources, update_thumbnails, root_windows[i],
ash::kShellWindowId_AlwaysOnTopContainer);
EnumerateWindowsForRoot(sources, update_thumbnails, root_windows[i],
ash::kShellWindowId_PipContainer);
+ EnumerateWindowsForRoot(sources, update_thumbnails, root_windows[i],
+ ash::kShellWindowId_FloatContainer);
}
}
}
diff --git a/chromium/chrome/browser/media/webrtc/desktop_media_list_ash_unittest.cc b/chromium/chrome/browser/media/webrtc/desktop_media_list_ash_unittest.cc
index c802dc12b98..0cab51d1dae 100644
--- a/chromium/chrome/browser/media/webrtc/desktop_media_list_ash_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/desktop_media_list_ash_unittest.cc
@@ -95,4 +95,21 @@ TEST_F(DesktopMediaListAshTest, WindowOnly) {
base::RunLoop().Run();
window.reset();
base::RunLoop().Run();
+
+ // Tests that a floated window shows up on the list. Regression test for
+ // crbug.com/1462516.
+ std::unique_ptr<aura::Window> float_window = CreateAppWindow();
+ ui::test::EventGenerator event_generator(float_window->GetRootWindow());
+ event_generator.PressAndReleaseKey(ui::VKEY_F,
+ ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
+
+ EXPECT_CALL(observer_, OnSourceAdded(0));
+ EXPECT_CALL(observer_, OnSourceThumbnailChanged(0))
+ .WillOnce(QuitMessageLoop())
+ .WillRepeatedly(DoDefault());
+ EXPECT_CALL(observer_, OnSourceRemoved(0)).WillOnce(QuitMessageLoop());
+
+ base::RunLoop().Run();
+ float_window.reset();
+ base::RunLoop().Run();
}
diff --git a/chromium/chrome/browser/media/webrtc/desktop_media_list_base.cc b/chromium/chrome/browser/media/webrtc/desktop_media_list_base.cc
index 0389599ac78..489e6f7b7b0 100644
--- a/chromium/chrome/browser/media/webrtc/desktop_media_list_base.cc
+++ b/chromium/chrome/browser/media/webrtc/desktop_media_list_base.cc
@@ -179,6 +179,31 @@ void DesktopMediaListBase::UpdateSourcesList(
}
}
+// TODO(https://crbug.com/1479849): Remove this function once there's a function
+// in NativeDesktopMediaList that can set the window_id. Once this is in place
+// we can determine a correct DesktopMediaID and call UpdateSourceThumbnail().
+void DesktopMediaListBase::UpdateSourceThumbnailForId(
+ const DesktopMediaID::Id id,
+ const gfx::ImageSkia& image) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // Unlike other methods that check can_refresh(), this one won't cause
+ // OnRefreshComplete() to be called, but the caller is expected to schedule a
+ // call to OnRefreshComplete() after this method and UpdateSourcePreview()
+ // have been called as many times as needed, so the check is still valid.
+ DCHECK(can_refresh());
+
+ for (size_t i = 0; i < sources_.size(); ++i) {
+ if (sources_[i].id.id == id) {
+ sources_[i].thumbnail = image;
+ if (observer_) {
+ observer_->OnSourceThumbnailChanged(i);
+ }
+ break;
+ }
+ }
+}
+
void DesktopMediaListBase::UpdateSourceThumbnail(const DesktopMediaID& id,
const gfx::ImageSkia& image) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/chromium/chrome/browser/media/webrtc/desktop_media_list_base.h b/chromium/chrome/browser/media/webrtc/desktop_media_list_base.h
index f25dede94be..2cd1000b90f 100644
--- a/chromium/chrome/browser/media/webrtc/desktop_media_list_base.h
+++ b/chromium/chrome/browser/media/webrtc/desktop_media_list_base.h
@@ -18,7 +18,7 @@ class Image;
}
// Thumbnail size is 100*100 pixels
-static const int kDefaultThumbnailSize = 100;
+constexpr gfx::Size kDefaultThumbnailSize = {100, 100};
// Base class for DesktopMediaList implementations. Implements logic shared
// between implementations. Specifically it's responsible for keeping current
@@ -79,6 +79,8 @@ class DesktopMediaListBase : public DesktopMediaList {
void UpdateSourcesList(const std::vector<SourceDescription>& new_sources);
// Update a thumbnail to observer.
+ void UpdateSourceThumbnailForId(const content::DesktopMediaID::Id id,
+ const gfx::ImageSkia& image);
void UpdateSourceThumbnail(const content::DesktopMediaID& id,
const gfx::ImageSkia& image);
@@ -98,8 +100,7 @@ class DesktopMediaListBase : public DesktopMediaList {
const absl::optional<content::DesktopMediaID>& id) override {}
// Size of thumbnails generated by the model.
- gfx::Size thumbnail_size_ =
- gfx::Size(kDefaultThumbnailSize, kDefaultThumbnailSize);
+ gfx::Size thumbnail_size_ = kDefaultThumbnailSize;
// ID of the hosting dialog.
content::DesktopMediaID view_dialog_id_ =
diff --git a/chromium/chrome/browser/media/webrtc/desktop_media_picker_factory_impl.cc b/chromium/chrome/browser/media/webrtc/desktop_media_picker_factory_impl.cc
index 47ce2fe7230..f1d993eeaae 100644
--- a/chromium/chrome/browser/media/webrtc/desktop_media_picker_factory_impl.cc
+++ b/chromium/chrome/browser/media/webrtc/desktop_media_picker_factory_impl.cc
@@ -9,14 +9,34 @@
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/media/webrtc/current_tab_desktop_media_list.h"
+#include "chrome/browser/media/webrtc/desktop_capturer_wrapper.h"
#include "chrome/browser/media/webrtc/native_desktop_media_list.h"
#include "chrome/browser/media/webrtc/tab_desktop_media_list.h"
+#include "chrome/browser/media/webrtc/thumbnail_capturer_mac.h"
#include "content/public/browser/desktop_capture.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/media/webrtc/desktop_media_list_ash.h"
#endif
+namespace {
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+std::unique_ptr<ThumbnailCapturer> MakeWindowCapturer() {
+#if BUILDFLAG(IS_MAC)
+ if (ShouldUseThumbnailCapturerMac()) {
+ return CreateThumbnailCapturerMac();
+ }
+#endif // BUILDFLAG(IS_MAC)
+
+ std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer =
+ content::desktop_capture::CreateWindowCapturer();
+ return desktop_capturer ? std::make_unique<DesktopCapturerWrapper>(
+ std::move(desktop_capturer))
+ : nullptr;
+}
+#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
+} // namespace
+
DesktopMediaPickerFactoryImpl::DesktopMediaPickerFactoryImpl() = default;
DesktopMediaPickerFactoryImpl::~DesktopMediaPickerFactoryImpl() = default;
@@ -69,8 +89,12 @@ DesktopMediaPickerFactoryImpl::CreateMediaList(
// If screen capture is not supported on the platform, then we should
// not attempt to create an instance of NativeDesktopMediaList. Doing so
// will hit a DCHECK.
- std::unique_ptr<webrtc::DesktopCapturer> capturer =
+ std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer =
content::desktop_capture::CreateScreenCapturer();
+ auto capturer = desktop_capturer
+ ? std::make_unique<DesktopCapturerWrapper>(
+ std::move(desktop_capturer))
+ : nullptr;
if (!capturer)
continue;
@@ -92,8 +116,7 @@ DesktopMediaPickerFactoryImpl::CreateMediaList(
// If window capture is not supported on the platform, then we should
// not attempt to create an instance of NativeDesktopMediaList. Doing so
// will hit a DCHECK.
- std::unique_ptr<webrtc::DesktopCapturer> capturer =
- content::desktop_capture::CreateWindowCapturer();
+ std::unique_ptr<ThumbnailCapturer> capturer = MakeWindowCapturer();
if (!capturer)
continue;
// If the capturer is not going to enumerate current process windows
diff --git a/chromium/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc b/chromium/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
index c286df580c3..a829877f052 100644
--- a/chromium/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
@@ -29,12 +29,8 @@
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
-#if BUILDFLAG(IS_MAC)
-#include "base/mac/mac_util.h"
-#endif
-
#if BUILDFLAG(IS_CHROMEOS)
-#include "chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_content_manager.h"
#endif // BUILDFLAG(IS_CHROMEOS)
#if !BUILDFLAG(IS_ANDROID)
@@ -177,6 +173,7 @@ class DisplayMediaAccessHandlerTest : public ChromeRenderViewHostTestHarness {
EXPECT_TRUE(test_flags_[0].picker_created);
+ picker_factory_ = nullptr;
access_handler_.reset();
EXPECT_TRUE(test_flags_[0].picker_deleted);
}
@@ -210,6 +207,8 @@ class DisplayMediaAccessHandlerTest : public ChromeRenderViewHostTestHarness {
&wait_loop, &result, devices);
wait_loop.Run();
EXPECT_FALSE(test_flags_[0].picker_created);
+
+ picker_factory_ = nullptr;
access_handler_.reset();
EXPECT_EQ(expected_result, result);
@@ -227,8 +226,9 @@ class DisplayMediaAccessHandlerTest : public ChromeRenderViewHostTestHarness {
std::vector<FakeDesktopMediaPickerFactory::TestFlags> test_flags_;
protected:
- raw_ptr<FakeDesktopMediaPickerFactory, DanglingUntriaged> picker_factory_;
+ // `access_handler` owns `picker_factory` and must outlive it.
std::unique_ptr<DisplayMediaAccessHandler> access_handler_;
+ raw_ptr<FakeDesktopMediaPickerFactory> picker_factory_;
};
TEST_F(DisplayMediaAccessHandlerTest, PermissionGiven) {
@@ -237,15 +237,13 @@ TEST_F(DisplayMediaAccessHandlerTest, PermissionGiven) {
ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
content::DesktopMediaID::kFakeId),
&result, devices, false /* request_audio */);
-// TODO(https://crbug.com/1266425): Fix screen-capture tests on MacOS
+// TODO(https://crbug.com/1266425): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
- // Starting from macOS 10.15, screen capture requires system permissions
- // that are disabled by default.
- if (base::mac::IsAtLeastOS10_15()) {
- EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
- result);
- return;
- }
+ // On macOS, screen capture requires system permissions that are disabled by
+ // default.
+ EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
+ result);
+ return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
@@ -281,15 +279,13 @@ TEST_F(DisplayMediaAccessHandlerTest, PermissionGivenToRequestWithAudio) {
content::DesktopMediaID::kFakeId,
true /* audio_share */);
ProcessRequest(fake_media_id, &result, devices, true /* request_audio */);
-// TODO(https://crbug.com/1266425): Fix screen-capture tests on MacOS
+// TODO(https://crbug.com/1266425): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
- // Starting from macOS 10.15, screen capture requires system permissions
- // that are disabled by default.
- if (base::mac::IsAtLeastOS10_15()) {
- EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
- result);
- return;
- }
+ // On macOS, screen capture requires system permissions that are disabled by
+ // default.
+ EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
+ result);
+ return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(2u, blink::CountDevices(devices));
@@ -422,7 +418,6 @@ TEST_F(DisplayMediaAccessHandlerTest, UpdateMediaRequestStateWithClosing) {
EXPECT_TRUE(queue_it != GetRequestQueues().end());
EXPECT_EQ(0u, queue_it->second.size());
EXPECT_TRUE(test_flags_[0].picker_deleted);
- access_handler_.reset();
}
TEST_F(DisplayMediaAccessHandlerTest, CorrectHostAsksForPermissions) {
@@ -562,7 +557,6 @@ TEST_F(DisplayMediaAccessHandlerTest, WebContentsDestroyed) {
NotifyWebContentsDestroyed();
EXPECT_EQ(0u, GetRequestQueues().size());
- access_handler_.reset();
}
TEST_F(DisplayMediaAccessHandlerTest, MultipleRequests) {
@@ -616,16 +610,13 @@ TEST_F(DisplayMediaAccessHandlerTest, MultipleRequests) {
wait_loop[0].Run();
EXPECT_TRUE(test_flags_[0].picker_created);
EXPECT_TRUE(test_flags_[0].picker_deleted);
-// TODO(https://crbug.com/1266425): Fix screen-capture tests on MacOS
+// TODO(https://crbug.com/1266425): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
- // Starting from macOS 10.15, screen capture requires system permissions
- // that are disabled by default.
- if (base::mac::IsAtLeastOS10_15()) {
- EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
- result);
- access_handler_.reset();
- return;
- }
+ // On macOS, screen capture requires system permissions that are disabled by
+ // default.
+ EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
+ result);
+ return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_EQ(1u, devices.size());
@@ -642,8 +633,6 @@ TEST_F(DisplayMediaAccessHandlerTest, MultipleRequests) {
EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
devices[0].type);
EXPECT_FALSE(devices[0].IsSameDevice(first_device));
-
- access_handler_.reset();
}
TEST_F(DisplayMediaAccessHandlerTest,
@@ -727,22 +716,18 @@ TEST_F(DisplayMediaAccessHandlerTest, ChangeSourceWithPendingPickerRequest) {
wait_loop[0].Run();
EXPECT_TRUE(test_flags_[0].picker_created);
EXPECT_TRUE(test_flags_[0].picker_deleted);
-// TODO(https://crbug.com/1266425): Fix screen-capture tests on MacOS
+// TODO(https://crbug.com/1266425): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
- // Starting from macOS 10.15, screen capture requires system permissions
- // that are disabled by default.
- if (base::mac::IsAtLeastOS10_15()) {
- EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
- results[0]);
- access_handler_.reset();
- return;
- }
+ // On macOS, screen capture requires system permissions that are disabled by
+ // default.
+ EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
+ results[0]);
+ return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, results[0]);
EXPECT_FALSE(test_flags_[1].picker_created);
EXPECT_FALSE(test_flags_[1].picker_deleted);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, results[1]);
- access_handler_.reset();
}
TEST_F(DisplayMediaAccessHandlerTest,
@@ -766,22 +751,18 @@ TEST_F(DisplayMediaAccessHandlerTest,
profile->GetPrefs()->SetBoolean(prefs::kScreenCaptureAllowed, false);
wait_loop[0].Run();
-// TODO(https://crbug.com/1266425): Fix screen-capture tests on MacOS
+// TODO(https://crbug.com/1266425): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
- // Starting from macOS 10.15, screen capture requires system permissions
- // that are disabled by default.
- if (base::mac::IsAtLeastOS10_15()) {
- EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
- results[0]);
- access_handler_.reset();
- return;
- }
+ // On macOS, screen capture requires system permissions that are disabled by
+ // default.
+ EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
+ results[0]);
+ return;
#endif
EXPECT_FALSE(test_flags_[1].picker_created);
EXPECT_FALSE(test_flags_[1].picker_deleted);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
results[1]);
- access_handler_.reset();
}
TEST_F(DisplayMediaAccessHandlerTest,
@@ -806,21 +787,17 @@ TEST_F(DisplayMediaAccessHandlerTest,
wait_loop[0].Run();
EXPECT_TRUE(test_flags_[0].picker_created);
EXPECT_TRUE(test_flags_[0].picker_deleted);
-// TODO(https://crbug.com/1266425): Fix screen-capture tests on MacOS
+// TODO(https://crbug.com/1266425): Fix screen-capture tests on macOS.
#if BUILDFLAG(IS_MAC)
- // Starting from macOS 10.15, screen capture requires system permissions
- // that are disabled by default.
- if (base::mac::IsAtLeastOS10_15()) {
- EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
- results[0]);
- access_handler_.reset();
- return;
- }
+ // On macOS, screen capture requires system permissions that are disabled by
+ // default.
+ EXPECT_EQ(blink::mojom::MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED,
+ results[0]);
+ return;
#endif
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, results[0]);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::INVALID_STATE, results[1]);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, results[2]);
- access_handler_.reset();
}
class DisplayMediaAccessHandlerTestWithSelfBrowserSurface
@@ -852,5 +829,4 @@ TEST_P(DisplayMediaAccessHandlerTestWithSelfBrowserSurface,
&wait_loop, &result, devices);
wait_loop.Run();
EXPECT_EQ(exclude_self_browser_surface_, IsWebContentsExcluded());
- access_handler_.reset();
}
diff --git a/chromium/chrome/browser/media/webrtc/fake_desktop_media_picker_factory.h b/chromium/chrome/browser/media/webrtc/fake_desktop_media_picker_factory.h
index 239a12d1951..c79beb1f0ef 100644
--- a/chromium/chrome/browser/media/webrtc/fake_desktop_media_picker_factory.h
+++ b/chromium/chrome/browser/media/webrtc/fake_desktop_media_picker_factory.h
@@ -57,8 +57,9 @@ class FakeDesktopMediaPickerFactory : public DesktopMediaPickerFactory {
override;
private:
- raw_ptr<FakeDesktopMediaPicker, DanglingUntriaged> picker_;
- raw_ptr<TestFlags, DanglingUntriaged | AllowPtrArithmetic> test_flags_;
+ raw_ptr<FakeDesktopMediaPicker, AcrossTasksDanglingUntriaged> picker_;
+ raw_ptr<TestFlags, AcrossTasksDanglingUntriaged | AllowPtrArithmetic>
+ test_flags_;
int tests_count_;
int current_test_;
bool is_web_contents_excluded_ = false;
diff --git a/chromium/chrome/browser/media/webrtc/get_all_screen_media_browsertest.cc b/chromium/chrome/browser/media/webrtc/get_all_screen_media_browsertest.cc
index 18c9ed06671..a4c68840ee3 100644
--- a/chromium/chrome/browser/media/webrtc/get_all_screen_media_browsertest.cc
+++ b/chromium/chrome/browser/media/webrtc/get_all_screen_media_browsertest.cc
@@ -14,8 +14,11 @@
#include "base/strings/string_split.h"
#include "base/test/gtest_tags.h"
#include "chrome/browser/chrome_content_browser_client.h"
+#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
+#include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
#include "chrome/browser/media/webrtc/webrtc_browsertest_base.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/web_contents.h"
@@ -41,16 +44,16 @@ std::string ExtractError(const std::string& message) {
: "";
}
-bool RunGetAllScreensMedia(content::WebContents* tab,
- std::set<std::string>& stream_ids,
- std::set<std::string>& track_ids,
- std::string* error_name_out = nullptr) {
+bool RunGetAllScreensMediaAndGetIds(content::WebContents* tab,
+ std::set<std::string>& stream_ids,
+ std::set<std::string>& track_ids,
+ std::string* error_name_out = nullptr) {
EXPECT_TRUE(stream_ids.empty());
EXPECT_TRUE(track_ids.empty());
- std::string result =
- content::EvalJs(tab->GetPrimaryMainFrame(), "runGetAllScreensMedia();")
- .ExtractString();
+ std::string result = content::EvalJs(tab->GetPrimaryMainFrame(),
+ "runGetAllScreensMediaAndGetIds();")
+ .ExtractString();
const std::vector<std::string> split_id_components = base::SplitString(
result, ":", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
@@ -159,7 +162,7 @@ IN_PROC_BROWSER_TEST_F(GetAllScreensMediaBrowserTest,
SetScreens(/*screen_count=*/1u);
std::set<std::string> stream_ids;
std::set<std::string> track_ids;
- EXPECT_TRUE(RunGetAllScreensMedia(contents_, stream_ids, track_ids));
+ EXPECT_TRUE(RunGetAllScreensMediaAndGetIds(contents_, stream_ids, track_ids));
EXPECT_EQ(1u, track_ids.size());
}
@@ -168,7 +171,7 @@ IN_PROC_BROWSER_TEST_F(GetAllScreensMediaBrowserTest,
SetScreens(/*screen_count=*/0u);
std::set<std::string> stream_ids;
std::set<std::string> track_ids;
- EXPECT_TRUE(RunGetAllScreensMedia(contents_, stream_ids, track_ids));
+ EXPECT_TRUE(RunGetAllScreensMediaAndGetIds(contents_, stream_ids, track_ids));
// If no screen is attached to a device, the |DisplayManager| will add a
// default device. This same behavior is used in other places in Chrome that
// handle multiple screens (e.g. in JS window.getScreenDetails() API) and
@@ -184,7 +187,7 @@ IN_PROC_BROWSER_TEST_F(GetAllScreensMediaBrowserTest,
SetScreens(/*screen_count=*/5u);
std::set<std::string> stream_ids;
std::set<std::string> track_ids;
- EXPECT_TRUE(RunGetAllScreensMedia(contents_, stream_ids, track_ids));
+ EXPECT_TRUE(RunGetAllScreensMediaAndGetIds(contents_, stream_ids, track_ids));
// TODO(crbug.com/1404274): Adapt this test if a decision is made on whether
// stream ids shall be shared or unique.
EXPECT_EQ(1u, stream_ids.size());
@@ -196,7 +199,7 @@ IN_PROC_BROWSER_TEST_F(GetAllScreensMediaBrowserTest,
SetScreens(/*screen_count=*/1u);
std::set<std::string> stream_ids;
std::set<std::string> track_ids;
- EXPECT_TRUE(RunGetAllScreensMedia(contents_, stream_ids, track_ids));
+ EXPECT_TRUE(RunGetAllScreensMediaAndGetIds(contents_, stream_ids, track_ids));
ASSERT_EQ(1u, stream_ids.size());
ASSERT_EQ(1u, track_ids.size());
@@ -208,7 +211,7 @@ IN_PROC_BROWSER_TEST_F(GetAllScreensMediaBrowserTest,
SetScreens(/*screen_count=*/5u);
std::set<std::string> stream_ids;
std::set<std::string> track_ids;
- EXPECT_TRUE(RunGetAllScreensMedia(contents_, stream_ids, track_ids));
+ EXPECT_TRUE(RunGetAllScreensMediaAndGetIds(contents_, stream_ids, track_ids));
EXPECT_EQ(1u, stream_ids.size());
EXPECT_EQ(5u, track_ids.size());
@@ -226,9 +229,110 @@ IN_PROC_BROWSER_TEST_F(GetAllScreensMediaBrowserTest,
std::set<std::string> stream_ids;
std::set<std::string> track_ids;
std::string error_name;
- EXPECT_FALSE(
- RunGetAllScreensMedia(contents_, stream_ids, track_ids, &error_name));
+ EXPECT_FALSE(RunGetAllScreensMediaAndGetIds(contents_, stream_ids, track_ids,
+ &error_name));
EXPECT_EQ("NotAllowedError", error_name);
}
+// Test that getDisplayMedia and getAllScreensMedia are independent,
+// so stopping one will not stop the other.
+class InteractionBetweenGetAllScreensMediaAndGetDisplayMediaTest
+ : public GetAllScreensMediaBrowserTest,
+ public ::testing::WithParamInterface<bool> {
+ public:
+ InteractionBetweenGetAllScreensMediaAndGetDisplayMediaTest()
+ : method1_(GetParam() ? "getDisplayMedia" : "getAllScreensMedia"),
+ method2_(GetParam() ? "getAllScreensMedia" : "getDisplayMedia") {}
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ // Flags use to automatically select the right desktop source and get
+ // around security restrictions.
+ // TODO(crbug.com/1459164): Use a less error-prone flag.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ command_line->AppendSwitchASCII(switches::kAutoSelectDesktopCaptureSource,
+ "Display");
+#else
+ command_line->AppendSwitchASCII(switches::kAutoSelectDesktopCaptureSource,
+ "Entire screen");
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ }
+
+ content::EvalJsResult Run(const std::string& method) {
+ return content::EvalJs(contents_,
+ base::StringPrintf("run(\"%s\");", method.c_str()));
+ }
+
+ content::EvalJsResult ProgrammaticallyStop(const std::string& method) {
+ return content::EvalJs(contents_,
+ base::StringPrintf("stop(\"%s\");", method.c_str()));
+ }
+
+ content::EvalJsResult AreAllTracksLive(const std::string& method) {
+ return content::EvalJs(
+ contents_,
+ base::StringPrintf("areAllTracksLive(\"%s\");", method.c_str()));
+ }
+
+ const std::string method1_;
+ const std::string method2_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ InteractionBetweenGetAllScreensMediaAndGetDisplayMediaTest,
+ ::testing::Bool());
+
+IN_PROC_BROWSER_TEST_P(
+ InteractionBetweenGetAllScreensMediaAndGetDisplayMediaTest,
+ ProgrammaticallyStoppingOneDoesNotStopTheOther) {
+ SetScreens(/*screen_count=*/1u);
+
+ ASSERT_EQ(nullptr, Run(method1_));
+ ASSERT_EQ(nullptr, Run(method2_));
+ ASSERT_EQ(nullptr, ProgrammaticallyStop(method1_));
+
+ EXPECT_EQ(false, AreAllTracksLive(method1_));
+ EXPECT_EQ(true, AreAllTracksLive(method2_));
+}
+
+// Identical to StoppingOneDoesNotStopTheOther other than that this following
+// test stops the second-started method first.
+IN_PROC_BROWSER_TEST_P(
+ InteractionBetweenGetAllScreensMediaAndGetDisplayMediaTest,
+ ProgrammaticallyStoppingOneDoesNotStopTheOtherInverseOrder) {
+ SetScreens(/*screen_count=*/1u);
+
+ ASSERT_EQ(nullptr, Run(method1_));
+ ASSERT_EQ(nullptr, Run(method2_));
+ ASSERT_EQ(nullptr, ProgrammaticallyStop(method2_));
+
+ EXPECT_EQ(true, AreAllTracksLive(method1_));
+ EXPECT_EQ(false, AreAllTracksLive(method2_));
+}
+
+// TODO(crbug.com/1479984): re-enable once the bug is fixed.
+IN_PROC_BROWSER_TEST_P(
+ InteractionBetweenGetAllScreensMediaAndGetDisplayMediaTest,
+ DISABLED_UserStoppingGetDisplayMediaDoesNotStopGetAllScreensMedia) {
+ SetScreens(/*screen_count=*/1u);
+
+ ASSERT_EQ(nullptr, Run(method1_));
+ ASSERT_EQ(nullptr, Run(method2_));
+
+ // The capture which was started via getDisplayMedia() caused the
+ // browser to show the user UX for stopping that capture. Simlate a user
+ // interaction with that UX.
+ MediaCaptureDevicesDispatcher::GetInstance()
+ ->GetMediaStreamCaptureIndicator()
+ ->StopMediaCapturing(
+ contents_, MediaStreamCaptureIndicator::MediaType::kDisplayMedia);
+ EXPECT_EQ(nullptr,
+ content::EvalJs(contents_,
+ "waitUntilStoppedByUser(\"getDisplayMedia\");"));
+
+ // Test-focus - the capture started through gASM was not affected
+ // by the user's interaction with the capture started via gDM.
+ EXPECT_EQ(true, AreAllTracksLive("getAllScreensMedia"));
+}
+
#endif // #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chromium/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h b/chromium/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h
index 6089bf1093b..ea4d848a001 100644
--- a/chromium/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h
+++ b/chromium/chrome/browser/media/webrtc/media_authorization_wrapper_mac.h
@@ -5,7 +5,8 @@
#ifndef CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
#define CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_AUTHORIZATION_WRAPPER_MAC_H_
-#import <Foundation/NSString.h>
+#import <AVFoundation/AVFoundation.h>
+#import <Foundation/Foundation.h>
#include "base/functional/callback_forward.h"
@@ -15,9 +16,8 @@ class MediaAuthorizationWrapper {
public:
virtual ~MediaAuthorizationWrapper() {}
- // NB: NSInteger is used rather than AVAuthorizationStatus; when macOS 10.14
- // is the minimum requirement for Chromium, switch types.
- virtual NSInteger AuthorizationStatusForMediaType(NSString* media_type) = 0;
+ virtual AVAuthorizationStatus AuthorizationStatusForMediaType(
+ NSString* media_type) = 0;
virtual void RequestAccessForMediaType(NSString* media_type,
base::OnceClosure callback) = 0;
};
diff --git a/chromium/chrome/browser/media/webrtc/media_device_salt_service_factory.cc b/chromium/chrome/browser/media/webrtc/media_device_salt_service_factory.cc
new file mode 100644
index 00000000000..c75eaf7f2bc
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/media_device_salt_service_factory.cc
@@ -0,0 +1,40 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/webrtc/media_device_salt_service_factory.h"
+
+#include "base/files/file_path.h"
+#include "components/media_device_salt/media_device_salt_service.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+
+MediaDeviceSaltServiceFactory* MediaDeviceSaltServiceFactory::GetInstance() {
+ static base::NoDestructor<MediaDeviceSaltServiceFactory> factory;
+ return factory.get();
+}
+
+media_device_salt::MediaDeviceSaltService*
+MediaDeviceSaltServiceFactory::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return static_cast<media_device_salt::MediaDeviceSaltService*>(
+ GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+MediaDeviceSaltServiceFactory::MediaDeviceSaltServiceFactory()
+ : ProfileKeyedServiceFactory("MediaDeviceSaltServiceFactory",
+ ProfileSelections::Builder()
+ // TODO(crbug.com/1418376): Check if this
+ // service is needed in Guest mode.
+ .WithGuest(ProfileSelection::kOriginalOnly)
+ .Build()) {}
+
+std::unique_ptr<KeyedService>
+MediaDeviceSaltServiceFactory::BuildServiceInstanceForBrowserContext(
+ content::BrowserContext* context) const {
+ return std::make_unique<media_device_salt::MediaDeviceSaltService>(
+ user_prefs::UserPrefs::Get(context),
+ context->IsOffTheRecord()
+ ? base::FilePath()
+ : context->GetPath().AppendASCII("MediaDeviceSalts"));
+}
diff --git a/chromium/chrome/browser/media/webrtc/media_device_salt_service_factory.h b/chromium/chrome/browser/media/webrtc/media_device_salt_service_factory.h
new file mode 100644
index 00000000000..b02a5aa218b
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/media_device_salt_service_factory.h
@@ -0,0 +1,39 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_DEVICE_SALT_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_DEVICE_SALT_SERVICE_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+
+namespace media_device_salt {
+class MediaDeviceSaltService;
+} // namespace media_device_salt
+
+class MediaDeviceSaltServiceFactory : public ProfileKeyedServiceFactory {
+ public:
+ MediaDeviceSaltServiceFactory(const MediaDeviceSaltServiceFactory&) = delete;
+ MediaDeviceSaltServiceFactory& operator=(
+ const MediaDeviceSaltServiceFactory&) = delete;
+
+ // Returns the singleton instance of the MediaDeviceSaltServiceFactory.
+ static MediaDeviceSaltServiceFactory* GetInstance();
+
+ // Returns the MediaDeviceSaltService associated with |context|.
+ static media_device_salt::MediaDeviceSaltService* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ private:
+ friend class base::NoDestructor<MediaDeviceSaltServiceFactory>;
+
+ MediaDeviceSaltServiceFactory();
+ ~MediaDeviceSaltServiceFactory() override = default;
+
+ // BrowserContextKeyedServiceFactory implementation.
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+ content::BrowserContext* context) const override;
+};
+
+#endif // CHROME_BROWSER_MEDIA_WEBRTC_MEDIA_DEVICE_SALT_SERVICE_FACTORY_H_
diff --git a/chromium/chrome/browser/media/webrtc/media_device_salt_service_factory_unittest.cc b/chromium/chrome/browser/media/webrtc/media_device_salt_service_factory_unittest.cc
new file mode 100644
index 00000000000..e9547b927a4
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/media_device_salt_service_factory_unittest.cc
@@ -0,0 +1,30 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/webrtc/media_device_salt_service_factory.h"
+
+#include "base/test/test_future.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/media_device_salt/media_device_salt_service.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+
+TEST(MediaDeviceSaltServiceFactoryTest, Test) {
+ content::BrowserTaskEnvironment task_environment;
+ TestingProfile profile;
+
+ MediaDeviceSaltServiceFactory* factory =
+ MediaDeviceSaltServiceFactory::GetInstance();
+ ASSERT_TRUE(factory);
+
+ auto* service = factory->GetForBrowserContext(&profile);
+ ASSERT_TRUE(service);
+
+ base::test::TestFuture<const std::string&> future;
+ service->GetSalt(
+ blink::StorageKey::CreateFromStringForTesting("https://example.com"),
+ future.GetCallback());
+ EXPECT_FALSE(future.Get().empty());
+}
diff --git a/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.cc b/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
index 1cb1001c7d7..add372921f3 100644
--- a/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
+++ b/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
@@ -163,13 +163,14 @@ class MediaStreamCaptureIndicator::WebContentsDeviceUsage
// Increment ref-counts up based on the type of each device provided.
void AddDevices(const blink::mojom::StreamDevices& devices,
- base::OnceClosure stop_callback);
+ base::OnceClosure stop_callback,
+ int stop_callback_id);
// Decrement ref-counts up based on the type of each device provided.
- void RemoveDevices(const blink::mojom::StreamDevices& devices);
+ void RemoveDevices(const blink::mojom::StreamDevices& devices,
+ int stop_callback_id);
- // Helper to call |stop_callback_|.
- void NotifyStopped();
+ void StopMediaCapturing(int media_type);
private:
int& GetStreamCount(const blink::MediaStreamDevice& device);
@@ -183,6 +184,8 @@ class MediaStreamCaptureIndicator::WebContentsDeviceUsage
indicator_->UnregisterWebContents(web_contents());
}
+ void StopCallbacks(std::map<int, base::OnceClosure>& stop_callbacks);
+
scoped_refptr<MediaStreamCaptureIndicator> indicator_;
int audio_stream_count_ = 0;
int video_stream_count_ = 0;
@@ -190,7 +193,8 @@ class MediaStreamCaptureIndicator::WebContentsDeviceUsage
int window_stream_count_ = 0;
int display_stream_count_ = 0;
- base::OnceClosure stop_callback_;
+ std::map<int, base::OnceClosure> display_media_stop_callbacks_;
+ std::map<int, base::OnceClosure> user_media_stop_callbacks_;
base::WeakPtrFactory<WebContentsDeviceUsage> weak_factory_{this};
};
@@ -212,7 +216,8 @@ class MediaStreamCaptureIndicator::UIDelegate : public content::MediaStreamUI {
#if !BUILDFLAG(IS_ANDROID)
focus_delegate_(web_contents),
#endif
- application_title_(std::move(application_title)) {
+ application_title_(std::move(application_title)),
+ stop_callback_id_(MediaStreamCaptureIndicator::g_stop_callback_id_++) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(devices_.audio_device.has_value() ||
devices_.video_device.has_value());
@@ -223,7 +228,7 @@ class MediaStreamCaptureIndicator::UIDelegate : public content::MediaStreamUI {
~UIDelegate() override {
if (started_ && device_usage_)
- device_usage_->RemoveDevices(devices_);
+ device_usage_->RemoveDevices(devices_, stop_callback_id_);
}
private:
@@ -244,8 +249,7 @@ class MediaStreamCaptureIndicator::UIDelegate : public content::MediaStreamUI {
if (device_usage_) {
// |device_usage_| handles |stop_callback| when |ui_| is unspecified.
- device_usage_->AddDevices(devices_,
- ui_ ? base::OnceClosure() : stop_callback);
+ device_usage_->AddDevices(devices_, stop_callback, stop_callback_id_);
}
#if BUILDFLAG(IS_CHROMEOS)
@@ -305,6 +309,7 @@ class MediaStreamCaptureIndicator::UIDelegate : public content::MediaStreamUI {
#endif
const std::u16string application_title_;
bool started_ = false;
+ const int stop_callback_id_;
};
std::unique_ptr<content::MediaStreamUI>
@@ -321,14 +326,35 @@ MediaStreamCaptureIndicator::WebContentsDeviceUsage::RegisterMediaStream(
void MediaStreamCaptureIndicator::WebContentsDeviceUsage::AddDevices(
const blink::mojom::StreamDevices& devices,
- base::OnceClosure stop_callback) {
- if (devices.audio_device.has_value())
+ base::OnceClosure stop_callback,
+ int stop_callback_id) {
+ MediaType type = MediaType::kUnknown;
+
+ if (devices.audio_device.has_value()) {
AddDevice(devices.audio_device.value());
- if (devices.video_device.has_value())
+ type = MediaStreamCaptureIndicator::GetMediaType(
+ devices.audio_device.value().type);
+ }
+
+ if (devices.video_device.has_value()) {
AddDevice(devices.video_device.value());
+ type = MediaStreamCaptureIndicator::GetMediaType(
+ devices.video_device.value().type);
+ }
+
+ if (type == MediaType::kUserMedia) {
+ user_media_stop_callbacks_[stop_callback_id] = std::move(stop_callback);
+ }
+
+ // TODO(crbug.com/1479984): Don't turn on this until related bugs are fixed.
+ // This may record the same stop_callback twice and lead to a crash if
+ // called later on.
+ // if (type == MediaType::kDisplayMedia) {
+ // display_media_stop_callbacks_[stop_callback_id] =
+ // std::move(stop_callback);
+ // }
if (web_contents()) {
- stop_callback_ = std::move(stop_callback);
web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
}
@@ -336,11 +362,28 @@ void MediaStreamCaptureIndicator::WebContentsDeviceUsage::AddDevices(
}
void MediaStreamCaptureIndicator::WebContentsDeviceUsage::RemoveDevices(
- const blink::mojom::StreamDevices& devices) {
- if (devices.audio_device.has_value())
+ const blink::mojom::StreamDevices& devices,
+ int stop_callback_id) {
+ MediaType type = MediaType::kUnknown;
+
+ if (devices.audio_device.has_value()) {
RemoveDevice(devices.audio_device.value());
- if (devices.video_device.has_value())
+ type = MediaStreamCaptureIndicator::GetMediaType(
+ devices.audio_device.value().type);
+ }
+
+ if (devices.video_device.has_value()) {
RemoveDevice(devices.video_device.value());
+ type = MediaStreamCaptureIndicator::GetMediaType(
+ devices.video_device.value().type);
+ }
+ if (type == MediaType::kUserMedia) {
+ user_media_stop_callbacks_.erase(stop_callback_id);
+ }
+
+ if (type == MediaType::kDisplayMedia) {
+ display_media_stop_callbacks_.erase(stop_callback_id);
+ }
if (web_contents() && !web_contents()->IsBeingDestroyed()) {
web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
@@ -350,9 +393,27 @@ void MediaStreamCaptureIndicator::WebContentsDeviceUsage::RemoveDevices(
indicator_->UpdateNotificationUserInterface();
}
-void MediaStreamCaptureIndicator::WebContentsDeviceUsage::NotifyStopped() {
- if (stop_callback_)
- std::move(stop_callback_).Run();
+void MediaStreamCaptureIndicator::WebContentsDeviceUsage::StopMediaCapturing(
+ int media_type) {
+ if (media_type & MediaType::kUserMedia) {
+ StopCallbacks(user_media_stop_callbacks_);
+ }
+ if (media_type & MediaType::kDisplayMedia) {
+ StopCallbacks(display_media_stop_callbacks_);
+ }
+}
+
+void MediaStreamCaptureIndicator::WebContentsDeviceUsage::StopCallbacks(
+ std::map<int, base::OnceClosure>& stop_callbacks) {
+ // This for loop is implemented in a weird way because std::move on the value
+ // will invalid the iterator; we need to record the next iter before
+ // std::move.
+ for (auto it = stop_callbacks.begin(), it_next = it;
+ it != stop_callbacks.end(); it = it_next) {
+ ++it_next;
+ std::move(it->second).Run();
+ }
+ stop_callbacks.clear();
}
int& MediaStreamCaptureIndicator::WebContentsDeviceUsage::GetStreamCount(
@@ -410,11 +471,38 @@ void MediaStreamCaptureIndicator::WebContentsDeviceUsage::RemoveDevice(
}
}
+// Return whether current device os a screen sharing.
+MediaStreamCaptureIndicator::MediaType
+MediaStreamCaptureIndicator::GetMediaType(blink::mojom::MediaStreamType type) {
+ switch (type) {
+ case blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE:
+ case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
+ return MediaStreamCaptureIndicator::MediaType::kUserMedia;
+
+ case blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE:
+ case blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE:
+ case blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE:
+ case blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE:
+ case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE:
+ case blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE:
+ case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB:
+ return MediaStreamCaptureIndicator::MediaType::kDisplayMedia;
+
+ case blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET:
+ return MediaStreamCaptureIndicator::MediaType::kAllScreensMedia;
+
+ default:
+ return MediaStreamCaptureIndicator::MediaType::kUnknown;
+ }
+}
+
MediaStreamCaptureIndicator::Observer::~Observer() {
DCHECK(!IsInObserverList());
}
-MediaStreamCaptureIndicator::MediaStreamCaptureIndicator() {}
+int MediaStreamCaptureIndicator::g_stop_callback_id_ = 0;
+
+MediaStreamCaptureIndicator::MediaStreamCaptureIndicator() = default;
MediaStreamCaptureIndicator::~MediaStreamCaptureIndicator() {
// The user is responsible for cleaning up by reporting the closure of any
@@ -520,16 +608,21 @@ bool MediaStreamCaptureIndicator::IsCapturingDisplay(
base::BindRepeating(&WebContentsDeviceUsage::IsCapturingDisplay));
}
-void MediaStreamCaptureIndicator::NotifyStopped(
- content::WebContents* web_contents) const {
+void MediaStreamCaptureIndicator::StopMediaCapturing(
+ content::WebContents* web_contents,
+ int media_type) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // AllScreensMedia is an managed device feature, and should not be stopped
+ // unless fully discussed.
+ CHECK(!(media_type & MediaType::kAllScreensMedia))
+ << "AllScreensMedia should not be stopped by MediaStreamCaptureIndicator";
auto it = usage_map_.find(web_contents);
- DCHECK(it != usage_map_.end());
- it->second->NotifyStopped();
-
+ if (it != usage_map_.end()) {
+ it->second->StopMediaCapturing(media_type);
+ }
for (auto* inner_contents : web_contents->GetInnerWebContents())
- NotifyStopped(inner_contents);
+ StopMediaCapturing(inner_contents, media_type);
}
void MediaStreamCaptureIndicator::UnregisterWebContents(
diff --git a/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.h b/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.h
index 5243e743185..e113822f9ec 100644
--- a/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.h
+++ b/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator.h
@@ -59,6 +59,16 @@ class MediaStreamCaptureIndicator
: public base::RefCountedThreadSafe<MediaStreamCaptureIndicator>,
public StatusIconMenuModel::Delegate {
public:
+ enum MediaType {
+ kUnknown = 0,
+ kUserMedia = 1,
+ kDisplayMedia = 2,
+ kAllScreensMedia = 4,
+ };
+
+ // Maps blink::mojom::MediaStreamType to MediaType.
+ static MediaType GetMediaType(blink::mojom::MediaStreamType type);
+
class Observer : public base::CheckedObserver {
public:
virtual void OnIsCapturingVideoChanged(content::WebContents* web_contents,
@@ -114,8 +124,10 @@ class MediaStreamCaptureIndicator
// Returns true if |web_contents| is capturing a display.
bool IsCapturingDisplay(content::WebContents* web_contents) const;
- // Called when STOP button in media capture notification is clicked.
- void NotifyStopped(content::WebContents* web_contents) const;
+ // Called to stop media capturing of the |media_type|.
+ // |media_type| is underlying_type of MediaType.
+ void StopMediaCapturing(content::WebContents* web_contents,
+ int media_type) const;
// Adds/Removes observers. Observers needs to be removed during the lifetime
// of this object.
@@ -168,6 +180,11 @@ class MediaStreamCaptureIndicator
std::unique_ptr<WebContentsDeviceUsage>>
usage_map_;
+ // g_stop_callback_id_ is used to identify each stop_callbacks when
+ // AddDevices or RemoveDevices is called. We need this because the device_id
+ // are not unique.
+ static int g_stop_callback_id_;
+
// A vector which maps command IDs to their associated WebContents
// instance. This is rebuilt each time the status tray icon context menu is
// updated.
diff --git a/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc b/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc
index a5b3bc4e757..583815011ef 100644
--- a/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc
@@ -6,6 +6,7 @@
#include "base/functional/bind.h"
#include "base/functional/callback.h"
+#include "base/test/bind.h"
#include "chrome/browser/media/webrtc/desktop_capture_devices_util.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -271,6 +272,40 @@ TEST_P(MediaStreamCaptureIndicatorObserverMethodTest, AddAndRemoveDevice) {
::testing::Mock::VerifyAndClear(observer());
}
+// TODO(crbug.com/1479984): re-enable once the bug is fixed.
+TEST_P(MediaStreamCaptureIndicatorObserverMethodTest,
+ DISABLED_StopMediaCapturing) {
+ const ObserverMethodTestParam& param = std::get<0>(GetParam());
+ const auto media_tpy =
+ MediaStreamCaptureIndicator::GetMediaType(param.stream_type);
+ bool is_portal = std::get<1>(GetParam());
+ content::WebContents* source = is_portal ? portal_contents() : web_contents();
+
+ // By default all accessors should return false as there's no stream device.
+ EXPECT_FALSE((indicator()->*(param.accessor_method))(web_contents()));
+ std::unique_ptr<content::MediaStreamUI> ui =
+ indicator()->RegisterMediaStream(source, CreateFakeDevice(param));
+ auto stop_callback = base::BindLambdaForTesting([&]() { ui.reset(); });
+
+ // Make sure that the observer gets called and that the corresponding accessor
+ // gets called when |OnStarted| is called.
+ (observer()->*(param.observer_method))(source, true);
+ ui->OnStarted(std::move(stop_callback),
+ content::MediaStreamUI::SourceCallback(),
+ /*label=*/std::string(), /*screen_capture_ids=*/{},
+ content::MediaStreamUI::StateChangeCallback());
+ EXPECT_TRUE((indicator()->*(param.accessor_method))(web_contents()));
+ ::testing::Mock::VerifyAndClear(observer());
+
+ // StopMediaCapturing calls the stop_callback which calls ui.reset; which will
+ // notify the observer_method that the capturing is stopped.
+ (observer()->*(param.observer_method))(source, false);
+ indicator()->StopMediaCapturing(source, media_tpy);
+
+ EXPECT_FALSE((indicator()->*(param.accessor_method))(web_contents()));
+ ::testing::Mock::VerifyAndClear(observer());
+}
+
TEST_P(MediaStreamCaptureIndicatorObserverMethodTest, CloseActiveWebContents) {
const ObserverMethodTestParam& param = std::get<0>(GetParam());
bool is_portal = std::get<1>(GetParam());
diff --git a/chromium/chrome/browser/media/webrtc/media_stream_device_permission_context_unittest.cc b/chromium/chrome/browser/media/webrtc/media_stream_device_permission_context_unittest.cc
index 9a90eb3be60..fa4265a6c36 100644
--- a/chromium/chrome/browser/media/webrtc/media_stream_device_permission_context_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/media_stream_device_permission_context_unittest.cc
@@ -21,12 +21,16 @@
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
#if !BUILDFLAG(IS_ANDROID)
#include "components/permissions/permission_request_manager.h"
#endif
namespace {
+
+using PermissionStatus = blink::mojom::PermissionStatus;
+
class TestPermissionContext : public MediaStreamDevicePermissionContext {
public:
TestPermissionContext(Profile* profile,
@@ -73,17 +77,17 @@ class MediaStreamDevicePermissionContextTests
secure_url.DeprecatedGetOriginAsURL(),
content_settings_type));
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
+ EXPECT_EQ(PermissionStatus::DENIED,
permission_context
.GetPermissionStatus(nullptr /* render_frame_host */,
insecure_url, insecure_url)
- .content_setting);
+ .status);
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
+ EXPECT_EQ(PermissionStatus::DENIED,
permission_context
.GetPermissionStatus(nullptr /* render_frame_host */,
insecure_url, secure_url)
- .content_setting);
+ .status);
}
void TestSecureQueryingUrl(ContentSettingsType content_settings_type) {
@@ -97,11 +101,11 @@ class MediaStreamDevicePermissionContextTests
secure_url.DeprecatedGetOriginAsURL(),
content_settings_type));
- EXPECT_EQ(CONTENT_SETTING_ASK,
+ EXPECT_EQ(PermissionStatus::ASK,
permission_context
.GetPermissionStatus(nullptr /* render_frame_host */,
secure_url, secure_url)
- .content_setting);
+ .status);
}
void TestUseFakeUiSwitch(ContentSettingsType content_setting_type,
@@ -109,11 +113,11 @@ class MediaStreamDevicePermissionContextTests
GURL secure_url("https://www.example.com");
TestPermissionContext permission_context(profile(), content_setting_type);
- EXPECT_EQ(CONTENT_SETTING_ASK,
+ EXPECT_EQ(PermissionStatus::ASK,
permission_context
.GetPermissionStatus(nullptr /* render_frame_host */,
secure_url, secure_url)
- .content_setting);
+ .status);
if (use_deny_switch) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
@@ -123,11 +127,12 @@ class MediaStreamDevicePermissionContextTests
switches::kUseFakeUIForMediaStream);
}
- EXPECT_EQ(use_deny_switch ? CONTENT_SETTING_BLOCK : CONTENT_SETTING_ALLOW,
- permission_context
- .GetPermissionStatus(nullptr /* render_frame_host */,
- secure_url, secure_url)
- .content_setting);
+ EXPECT_EQ(
+ use_deny_switch ? PermissionStatus::DENIED : PermissionStatus::GRANTED,
+ permission_context
+ .GetPermissionStatus(nullptr /* render_frame_host */, secure_url,
+ secure_url)
+ .status);
base::CommandLine::ForCurrentProcess()->RemoveSwitch(
switches::kUseFakeUIForMediaStream);
diff --git a/chromium/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc b/chromium/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
index 2b60ca4d519..63fc37ba424 100644
--- a/chromium/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
+++ b/chromium/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
@@ -196,8 +196,7 @@ class MediaStreamDevicesControllerTest : public WebRtcTestBase {
DCHECK(example_url_.is_empty());
example_url_ = url;
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), example_url_));
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().Empty());
}
virtual media::VideoCaptureControlSupport GetControlSupport() const {
@@ -303,8 +302,8 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest, RequestAndAllowMic) {
ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_FALSE(GetContentSettings()->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_MIC));
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_ACCESSED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().Has(
+ PageSpecificContentSettings::kMicrophoneAccessed));
EXPECT_EQ(example_audio_id(),
GetContentSettings()->media_stream_requested_audio_device());
EXPECT_EQ(example_audio_id(),
@@ -330,8 +329,8 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest, RequestAndAllowCam) {
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_FALSE(GetContentSettings()->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
- EXPECT_EQ(PageSpecificContentSettings::CAMERA_ACCESSED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().Has(
+ PageSpecificContentSettings::kCameraAccessed));
EXPECT_EQ(std::string(),
GetContentSettings()->media_stream_requested_audio_device());
EXPECT_EQ(std::string(),
@@ -357,9 +356,9 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest, RequestAndBlockMic) {
ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_TRUE(GetContentSettings()->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_MIC));
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_ACCESSED |
- PageSpecificContentSettings::MICROPHONE_BLOCKED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().HasAll(
+ {PageSpecificContentSettings::kMicrophoneAccessed,
+ PageSpecificContentSettings::kMicrophoneBlocked}));
EXPECT_EQ(example_audio_id(),
GetContentSettings()->media_stream_requested_audio_device());
EXPECT_EQ(example_audio_id(),
@@ -385,9 +384,9 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest, RequestAndBlockCam) {
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_TRUE(GetContentSettings()->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
- EXPECT_EQ(PageSpecificContentSettings::CAMERA_ACCESSED |
- PageSpecificContentSettings::CAMERA_BLOCKED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().HasAll(
+ {PageSpecificContentSettings::kCameraAccessed,
+ PageSpecificContentSettings::kCameraBlocked}));
EXPECT_EQ(std::string(),
GetContentSettings()->media_stream_requested_audio_device());
EXPECT_EQ(std::string(),
@@ -420,9 +419,9 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_FALSE(GetContentSettings()->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_ACCESSED |
- PageSpecificContentSettings::CAMERA_ACCESSED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().HasAll(
+ {PageSpecificContentSettings::kMicrophoneAccessed,
+ PageSpecificContentSettings::kCameraAccessed}));
EXPECT_EQ(example_audio_id(),
GetContentSettings()->media_stream_requested_audio_device());
EXPECT_EQ(example_audio_id(),
@@ -455,11 +454,11 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_TRUE(GetContentSettings()->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_ACCESSED |
- PageSpecificContentSettings::MICROPHONE_BLOCKED |
- PageSpecificContentSettings::CAMERA_ACCESSED |
- PageSpecificContentSettings::CAMERA_BLOCKED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().HasAll(
+ {PageSpecificContentSettings::kMicrophoneAccessed,
+ PageSpecificContentSettings::kMicrophoneBlocked,
+ PageSpecificContentSettings::kCameraAccessed,
+ PageSpecificContentSettings::kCameraBlocked}));
EXPECT_EQ(example_audio_id(),
GetContentSettings()->media_stream_requested_audio_device());
EXPECT_EQ(example_audio_id(),
@@ -493,11 +492,11 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_TRUE(GetContentSettings()->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_ACCESSED |
- PageSpecificContentSettings::MICROPHONE_BLOCKED |
- PageSpecificContentSettings::CAMERA_ACCESSED |
- PageSpecificContentSettings::CAMERA_BLOCKED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().HasAll(
+ {PageSpecificContentSettings::kMicrophoneAccessed,
+ PageSpecificContentSettings::kMicrophoneBlocked,
+ PageSpecificContentSettings::kCameraAccessed,
+ PageSpecificContentSettings::kCameraBlocked}));
EXPECT_EQ(example_audio_id(),
GetContentSettings()->media_stream_requested_audio_device());
EXPECT_EQ(example_audio_id(),
@@ -531,11 +530,11 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_TRUE(GetContentSettings()->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_ACCESSED |
- PageSpecificContentSettings::MICROPHONE_BLOCKED |
- PageSpecificContentSettings::CAMERA_ACCESSED |
- PageSpecificContentSettings::CAMERA_BLOCKED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().HasAll(
+ {PageSpecificContentSettings::kMicrophoneAccessed,
+ PageSpecificContentSettings::kMicrophoneBlocked,
+ PageSpecificContentSettings::kCameraAccessed,
+ PageSpecificContentSettings::kCameraBlocked}));
EXPECT_EQ(example_audio_id(),
GetContentSettings()->media_stream_requested_audio_device());
EXPECT_EQ(example_audio_id(),
@@ -612,8 +611,8 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
GetContentSettings()->media_stream_requested_video_device());
EXPECT_EQ(example_video_id(),
GetContentSettings()->media_stream_selected_video_device());
- EXPECT_EQ(PageSpecificContentSettings::CAMERA_ACCESSED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().Has(
+ PageSpecificContentSettings::kCameraAccessed));
// Simulate that an a video stream is now being captured.
blink::mojom::StreamDevices devices;
@@ -657,17 +656,17 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
GetContentSettings()->media_stream_requested_video_device());
EXPECT_EQ(example_video_id(),
GetContentSettings()->media_stream_selected_video_device());
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_ACCESSED |
- PageSpecificContentSettings::MICROPHONE_BLOCKED |
- PageSpecificContentSettings::CAMERA_ACCESSED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().HasAll(
+ {PageSpecificContentSettings::kMicrophoneAccessed,
+ PageSpecificContentSettings::kMicrophoneBlocked,
+ PageSpecificContentSettings::kCameraAccessed}));
// After ending the camera capture, the camera permission is no longer
// relevant, so it should no be included in the mic/cam state.
video_stream_ui.reset();
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_ACCESSED |
- PageSpecificContentSettings::MICROPHONE_BLOCKED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().HasAll(
+ {PageSpecificContentSettings::kMicrophoneAccessed,
+ PageSpecificContentSettings::kMicrophoneBlocked}));
}
// Stores the ContentSettings inputs for a particular test and has functions
@@ -978,8 +977,10 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
// PermissionRequestManager for the WebContents and uses that reference in its
// destructor, it has to be destroyed before the tab.
prompt_factory_.reset();
- ASSERT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt(
- prompt_contents_index, TabCloseTypes::CLOSE_USER_GESTURE));
+ int previous_tab_count = browser()->tab_strip_model()->count();
+ browser()->tab_strip_model()->CloseWebContentsAt(
+ prompt_contents_index, TabCloseTypes::CLOSE_USER_GESTURE);
+ EXPECT_EQ(previous_tab_count - 1, browser()->tab_strip_model()->count());
base::RunLoop().RunUntilIdle();
VerifyResultState(
@@ -1043,8 +1044,7 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
VerifyResultState(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
false, false);
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().Empty());
}
IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
@@ -1075,8 +1075,7 @@ IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
VerifyResultState(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED,
false, false);
- EXPECT_EQ(PageSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED,
- GetContentSettings()->GetMicrophoneCameraState());
+ EXPECT_TRUE(GetContentSettings()->GetMicrophoneCameraState().Empty());
}
IN_PROC_BROWSER_TEST_F(MediaStreamDevicesControllerTest,
diff --git a/chromium/chrome/browser/media/webrtc/native_desktop_media_list.cc b/chromium/chrome/browser/media/webrtc/native_desktop_media_list.cc
index b548c9fbd3c..c2c7d86ed1f 100644
--- a/chromium/chrome/browser/media/webrtc/native_desktop_media_list.cc
+++ b/chromium/chrome/browser/media/webrtc/native_desktop_media_list.cc
@@ -1,15 +1,16 @@
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-
#include "chrome/browser/media/webrtc/native_desktop_media_list.h"
#include <memory>
#include <utility>
+#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/hash/hash.h"
#include "base/message_loop/message_pump_type.h"
+#include "base/metrics/field_trial_params.h"
#include "base/numerics/checked_math.h"
#include "base/ranges/algorithm.h"
#include "base/strings/utf_string_conversions.h"
@@ -53,6 +54,18 @@ using content::DesktopMediaID;
namespace {
+// The enable/disable property of this feature has no impact. The feature is
+// used solely to pass on the parameter below.
+BASE_FEATURE(kNativeDesktopMediaList,
+ "NativeDesktopMediaList",
+ base::FEATURE_ENABLED_BY_DEFAULT);
+
+// The maximum number of window thumbnails that are concurrently captured when
+// the frame delivery mode is set to kMultipleSourcesRecurrent.
+// ThumbnailCapturerMac is the only capturer at the moment that implements this.
+const base::FeatureParam<int> kNativeDesktopMediaListMaxConcurrentStreams{
+ &kNativeDesktopMediaList, "max_concurrent_streams", 100};
+
// Update the list every second.
const int kDefaultNativeDesktopMediaListUpdatePeriod = 1000;
@@ -153,13 +166,13 @@ BASE_FEATURE(kWindowCaptureMacV2,
} // namespace
class NativeDesktopMediaList::Worker
- : public webrtc::DesktopCapturer::Callback,
+ : public ThumbnailCapturer::Consumer,
public webrtc::DelegatedSourceListController::Observer {
public:
Worker(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
base::WeakPtr<NativeDesktopMediaList> media_list,
DesktopMediaList::Type type,
- std::unique_ptr<webrtc::DesktopCapturer> capturer,
+ std::unique_ptr<ThumbnailCapturer> capturer,
bool add_current_process_windows);
Worker(const Worker&) = delete;
@@ -168,7 +181,7 @@ class NativeDesktopMediaList::Worker
~Worker() override;
void Start();
- void Refresh(const DesktopMediaID::Id& view_dialog_id, bool update_thumnails);
+ void Refresh(bool update_thumbnails);
void RefreshThumbnails(std::vector<DesktopMediaID> native_ids,
const gfx::Size& thumbnail_size);
@@ -176,6 +189,12 @@ class NativeDesktopMediaList::Worker
void HideList();
void ClearDelegatedSourceListSelection();
+ // If |excluded_window_id| is not kNullId, then that ID will
+ // be ignored by `this`.
+ void SetExcludedWindow(DesktopMediaID::Id excluded_window_id);
+
+ void SetThumbnailSize(const gfx::Size& thumbnail_size);
+
private:
typedef std::map<DesktopMediaID, size_t> ImageHashesMap;
@@ -189,10 +208,12 @@ class NativeDesktopMediaList::Worker
// These must be members because |SourceDescription| is a protected type from
// |DesktopMediaListBase|.
+ // |excluded_window_id|, if different from kNullId, indicates a window
+ // which should be excluded from the list produced.
static std::vector<SourceDescription> FormatSources(
const webrtc::DesktopCapturer::SourceList& sources,
- const DesktopMediaID::Id& view_dialog_id,
- const DesktopMediaList::Type& list_type);
+ const DesktopMediaList::Type& list_type,
+ DesktopMediaID::Id excluded_window_id);
#if BUILDFLAG(IS_WIN)
static std::vector<SourceDescription> GetCurrentProcessWindows();
@@ -204,9 +225,13 @@ class NativeDesktopMediaList::Worker
void RefreshNextThumbnail();
- // webrtc::DesktopCapturer::Callback interface.
+ // ThumbnailCapturer::Consumer interface.
void OnCaptureResult(webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) override;
+ void OnRecurrentCaptureResult(ThumbnailCapturer::Result result,
+ std::unique_ptr<webrtc::DesktopFrame> frame,
+ ThumbnailCapturer::SourceId source_id) override;
+ void OnSourceListUpdated() override;
// webrtc::DelegatedSourceListController::Observer interface.
void OnSelection() override;
@@ -219,16 +244,29 @@ class NativeDesktopMediaList::Worker
base::WeakPtr<NativeDesktopMediaList> media_list_;
DesktopMediaList::Type type_;
- std::unique_ptr<webrtc::DesktopCapturer> capturer_;
+ const std::unique_ptr<ThumbnailCapturer> capturer_;
+ const ThumbnailCapturer::FrameDeliveryMethod frame_delivery_method_;
const bool add_current_process_windows_;
bool delegated_source_list_has_selection_ = false;
bool focused_ = false;
+ // If this ID is different than kNullId, then windows with this ID
+ // may not be captured.
+ // Used to keep track of the view dialog where the thumbnails are displayed,
+ // so as to avoid offering the user to capture that dialog, which will
+ // disappear as soon as the user makes that choice.
+ // TODO(https://crbug.com/1471931): Set this earlier to avoid frames being
+ // dropped because it's not set. If possible set it in the constructor.
+ DesktopMediaID::Id excluded_window_id_ = DesktopMediaID::kNullId;
+
+ gfx::Size thumbnail_size_ = kDefaultThumbnailSize;
+
// Stores hashes of snapshots previously captured.
ImageHashesMap image_hashes_;
- // Non-null when RefreshThumbnails hasn't yet completed.
+ // Non-null when RefreshThumbnails hasn't yet completed. Must only be accessed
+ // on `task_runner_` thread.
std::unique_ptr<RefreshThumbnailsState> refresh_thumbnails_state_;
base::WeakPtrFactory<Worker> weak_factory_{this};
@@ -238,12 +276,13 @@ NativeDesktopMediaList::Worker::Worker(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
base::WeakPtr<NativeDesktopMediaList> media_list,
DesktopMediaList::Type type,
- std::unique_ptr<webrtc::DesktopCapturer> capturer,
+ std::unique_ptr<ThumbnailCapturer> capturer,
bool add_current_process_windows)
: task_runner_(task_runner),
media_list_(media_list),
type_(type),
capturer_(std::move(capturer)),
+ frame_delivery_method_(capturer_->GetFrameDeliveryMethod()),
add_current_process_windows_(add_current_process_windows) {
DCHECK(capturer_);
@@ -263,18 +302,33 @@ void NativeDesktopMediaList::Worker::Start() {
capturer_->GetDelegatedSourceListController()->Observe(this);
}
-void NativeDesktopMediaList::Worker::Refresh(
- const DesktopMediaID::Id& view_dialog_id,
- bool update_thumnails) {
+void NativeDesktopMediaList::Worker::Refresh(bool update_thumbnails) {
DCHECK(task_runner_->BelongsToCurrentThread());
+
webrtc::DesktopCapturer::SourceList sources;
if (!capturer_->GetSourceList(&sources)) {
// Will pass empty results list to RefreshForVizFrameSinkWindows().
sources.clear();
}
+ if (capturer_->GetFrameDeliveryMethod() ==
+ ThumbnailCapturer::FrameDeliveryMethod::kMultipleSourcesRecurrent) {
+ // TODO(https://crbug.com/1471931): Select windows to stream based on what's
+ // visible. For now, select the first N windows.
+ const size_t target_size = std::min(
+ static_cast<size_t>(kNativeDesktopMediaListMaxConcurrentStreams.Get()),
+ sources.size());
+ std::vector<ThumbnailCapturer::SourceId> source_ids;
+ for (size_t i = 0; i < target_size; ++i) {
+ if (sources[i].id != excluded_window_id_) {
+ source_ids.push_back(sources[i].id);
+ }
+ }
+ capturer_->SelectSources(source_ids, thumbnail_size_);
+ }
+
std::vector<SourceDescription> source_descriptions =
- FormatSources(sources, view_dialog_id, type_);
+ FormatSources(sources, type_, excluded_window_id_);
#if BUILDFLAG(IS_WIN)
// If |add_current_process_windows_| is set to false, |capturer_| will have
@@ -293,7 +347,7 @@ void NativeDesktopMediaList::Worker::Refresh(
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&NativeDesktopMediaList::RefreshForVizFrameSinkWindows,
- media_list_, source_descriptions, update_thumnails));
+ media_list_, source_descriptions, update_thumbnails));
}
void NativeDesktopMediaList::Worker::RefreshThumbnails(
@@ -301,10 +355,11 @@ void NativeDesktopMediaList::Worker::RefreshThumbnails(
const gfx::Size& thumbnail_size) {
DCHECK(task_runner_->BelongsToCurrentThread());
- // Ignore if refresh is already in progress.
- if (refresh_thumbnails_state_)
- return;
-
+ // The refresh of thumbnails follows different steps depending on if the frame
+ // deliver method is kOnRequest or kMultipleSourcesRecurrent.
+ //
+ // For kOnRequest
+ // ==============
// To refresh thumbnails, a snapshot of each window is captured and scaled
// down to the specified size. Snapshotting can be asynchronous, and so
// the process looks like the following steps:
@@ -318,10 +373,27 @@ void NativeDesktopMediaList::Worker::RefreshThumbnails(
//
// |image_hashes_| is used to help avoid updating thumbnails that haven't
// changed since the last refresh.
+ //
+ // For kMultipleSourcesRecurrent
+ // =============================
+ // The refresh of thumbnails begins as soon as SelectSources() is called with
+ // the specific sources that should be captured. This will result in several
+ // callbacks to OnRecurrentCaptureResult for each source. If the source list
+ // is updated, there will be a call to OnSourceListUpdated() and the selection
+ // of sources may be changed.
+
+ // Ignore if refresh is already in progress or the frame delivery method is
+ // multiple sources recurrent.
+ if (refresh_thumbnails_state_ ||
+ frame_delivery_method_ ==
+ ThumbnailCapturer::FrameDeliveryMethod::kMultipleSourcesRecurrent) {
+ return;
+ }
refresh_thumbnails_state_ = std::make_unique<RefreshThumbnailsState>();
refresh_thumbnails_state_->source_ids = std::move(native_ids);
refresh_thumbnails_state_->thumbnail_size = thumbnail_size;
+
RefreshNextThumbnail();
}
@@ -329,8 +401,8 @@ void NativeDesktopMediaList::Worker::RefreshThumbnails(
std::vector<DesktopMediaListBase::SourceDescription>
NativeDesktopMediaList::Worker::FormatSources(
const webrtc::DesktopCapturer::SourceList& sources,
- const DesktopMediaID::Id& view_dialog_id,
- const DesktopMediaList::Type& list_type) {
+ const DesktopMediaList::Type& list_type,
+ DesktopMediaID::Id excluded_window_id) {
std::vector<SourceDescription> source_descriptions;
std::u16string title;
for (size_t i = 0; i < sources.size(); ++i) {
@@ -351,8 +423,9 @@ NativeDesktopMediaList::Worker::FormatSources(
case DesktopMediaList::Type::kWindow:
source_type = DesktopMediaID::Type::TYPE_WINDOW;
// Skip the picker dialog window.
- if (sources[i].id == view_dialog_id)
+ if (sources[i].id == excluded_window_id) {
continue;
+ }
title = base::UTF8ToUTF16(sources[i].title);
break;
@@ -437,6 +510,7 @@ NativeDesktopMediaList::Worker::MergeAndSortWindowSources(
#endif // BUILDFLAG(IS_WIN)
void NativeDesktopMediaList::Worker::RefreshNextThumbnail() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(refresh_thumbnails_state_);
for (size_t index = refresh_thumbnails_state_->next_source_index;
@@ -462,13 +536,15 @@ void NativeDesktopMediaList::Worker::RefreshNextThumbnail() {
void NativeDesktopMediaList::Worker::OnCaptureResult(
webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) {
- auto index = refresh_thumbnails_state_->next_source_index - 1;
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ const size_t index = refresh_thumbnails_state_->next_source_index - 1;
DCHECK(index < refresh_thumbnails_state_->source_ids.size());
DesktopMediaID id = refresh_thumbnails_state_->source_ids[index];
// |frame| may be null if capture failed (e.g. because window has been
// closed).
- auto frame_hash = GetFrameHash(frame.get());
+ const absl::optional<size_t> frame_hash = GetFrameHash(frame.get());
if (frame_hash) {
refresh_thumbnails_state_->new_image_hashes[id] = *frame_hash;
@@ -491,6 +567,28 @@ void NativeDesktopMediaList::Worker::OnCaptureResult(
weak_factory_.GetWeakPtr()));
}
+void NativeDesktopMediaList::Worker::OnRecurrentCaptureResult(
+ ThumbnailCapturer::Result result,
+ std::unique_ptr<webrtc::DesktopFrame> frame,
+ ThumbnailCapturer::SourceId source_id) {
+ // |frame| may be null if capture failed (e.g. because window has been
+ // closed).
+ if (!frame) {
+ return;
+ }
+
+ gfx::ImageSkia thumbnail =
+ ScaleDesktopFrame(std::move(frame), thumbnail_size_);
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
+ base::BindOnce(&NativeDesktopMediaList::UpdateSourceThumbnailForId,
+ media_list_, source_id, thumbnail));
+}
+
+void NativeDesktopMediaList::Worker::OnSourceListUpdated() {
+ Refresh(/*update_thumbnails=*/false);
+}
+
void NativeDesktopMediaList::Worker::ClearDelegatedSourceListSelection() {
DCHECK(capturer_->GetDelegatedSourceListController());
if (!delegated_source_list_has_selection_)
@@ -504,6 +602,16 @@ void NativeDesktopMediaList::Worker::ClearDelegatedSourceListSelection() {
capturer_->GetDelegatedSourceListController()->EnsureVisible();
}
+void NativeDesktopMediaList::Worker::SetExcludedWindow(
+ DesktopMediaID::Id excluded_window_id) {
+ excluded_window_id_ = excluded_window_id;
+}
+
+void NativeDesktopMediaList::Worker::SetThumbnailSize(
+ const gfx::Size& thumbnail_size) {
+ thumbnail_size_ = thumbnail_size;
+}
+
void NativeDesktopMediaList::Worker::FocusList() {
focused_ = true;
// If the capturer uses a delegated source list, then we need to ensure that
@@ -550,14 +658,14 @@ void NativeDesktopMediaList::Worker::OnError() {
NativeDesktopMediaList::NativeDesktopMediaList(
DesktopMediaList::Type type,
- std::unique_ptr<webrtc::DesktopCapturer> capturer)
+ std::unique_ptr<ThumbnailCapturer> capturer)
: NativeDesktopMediaList(type,
std::move(capturer),
/*add_current_process_windows=*/false) {}
NativeDesktopMediaList::NativeDesktopMediaList(
DesktopMediaList::Type type,
- std::unique_ptr<webrtc::DesktopCapturer> capturer,
+ std::unique_ptr<ThumbnailCapturer> capturer,
bool add_current_process_windows)
: DesktopMediaListBase(
base::Milliseconds(kDefaultNativeDesktopMediaListUpdatePeriod)),
@@ -602,6 +710,33 @@ NativeDesktopMediaList::~NativeDesktopMediaList() {
thread_.Stop();
}
+void NativeDesktopMediaList::SetViewDialogWindowId(DesktopMediaID dialog_id) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ DesktopMediaListBase::SetViewDialogWindowId(dialog_id);
+
+ // base::Unretained is safe here because we own the lifetime of both the
+ // worker and the thread and ensure that destroying the worker is the last
+ // thing the thread does before stopping.
+ thread_.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&Worker::SetExcludedWindow,
+ base::Unretained(worker_.get()), dialog_id.id));
+}
+
+void NativeDesktopMediaList::SetThumbnailSize(const gfx::Size& thumbnail_size) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ DesktopMediaListBase::SetThumbnailSize(thumbnail_size);
+
+ // base::Unretained is safe here because we own the lifetime of both the
+ // worker and the thread and ensure that destroying the worker is the last
+ // thing the thread does before stopping.
+ thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Worker::SetThumbnailSize, base::Unretained(worker_.get()),
+ thumbnail_size));
+}
+
bool NativeDesktopMediaList::IsSourceListDelegated() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return is_source_list_delegated_;
@@ -655,7 +790,7 @@ void NativeDesktopMediaList::HideList() {
base::BindOnce(&Worker::HideList, base::Unretained(worker_.get())));
}
-void NativeDesktopMediaList::Refresh(bool update_thumnails) {
+void NativeDesktopMediaList::Refresh(bool update_thumbnails) {
DCHECK(can_refresh());
#if defined(USE_AURA)
@@ -670,12 +805,12 @@ void NativeDesktopMediaList::Refresh(bool update_thumnails) {
thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&Worker::Refresh, base::Unretained(worker_.get()),
- view_dialog_id_.id, update_thumnails));
+ update_thumbnails));
}
void NativeDesktopMediaList::RefreshForVizFrameSinkWindows(
std::vector<SourceDescription> sources,
- bool update_thumnails) {
+ bool update_thumbnails) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(can_refresh());
@@ -784,7 +919,7 @@ void NativeDesktopMediaList::RefreshForVizFrameSinkWindows(
UpdateSourcesList(sources);
- if (!update_thumnails) {
+ if (!update_thumbnails) {
OnRefreshComplete();
return;
}
diff --git a/chromium/chrome/browser/media/webrtc/native_desktop_media_list.h b/chromium/chrome/browser/media/webrtc/native_desktop_media_list.h
index 17638e7d1e3..8dad4f59a5c 100644
--- a/chromium/chrome/browser/media/webrtc/native_desktop_media_list.h
+++ b/chromium/chrome/browser/media/webrtc/native_desktop_media_list.h
@@ -11,15 +11,13 @@
#include "base/memory/weak_ptr.h"
#include "base/threading/thread.h"
#include "chrome/browser/media/webrtc/desktop_media_list_base.h"
+#include "chrome/browser/media/webrtc/thumbnail_capturer.h"
#include "content/public/browser/desktop_media_id.h"
#include "ui/gfx/image/image.h"
namespace base {
class SingleThreadTaskRunner;
}
-namespace webrtc {
-class DesktopCapturer;
-}
// Implementation of DesktopMediaList that shows native screens and
// native windows.
@@ -27,10 +25,10 @@ class NativeDesktopMediaList final : public DesktopMediaListBase {
public:
// |capturer| must exist.
NativeDesktopMediaList(DesktopMediaList::Type type,
- std::unique_ptr<webrtc::DesktopCapturer> capturer);
+ std::unique_ptr<ThumbnailCapturer> capturer);
NativeDesktopMediaList(DesktopMediaList::Type type,
- std::unique_ptr<webrtc::DesktopCapturer> capturer,
+ std::unique_ptr<ThumbnailCapturer> capturer,
bool add_current_process_windows);
NativeDesktopMediaList(const NativeDesktopMediaList&) = delete;
@@ -38,6 +36,8 @@ class NativeDesktopMediaList final : public DesktopMediaListBase {
~NativeDesktopMediaList() override;
+ void SetViewDialogWindowId(content::DesktopMediaID dialog_id) override;
+ void SetThumbnailSize(const gfx::Size& thumbnail_size) override;
bool IsSourceListDelegated() const override;
void ClearDelegatedSourceListSelection() override;
void FocusList() override;
diff --git a/chromium/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc b/chromium/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc
index 3d3f39fd250..5c9a9a25fa6 100644
--- a/chromium/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/native_desktop_media_list_unittest.cc
@@ -130,7 +130,7 @@ class MockObserver : public DesktopMediaListObserver {
MOCK_METHOD0(OnDelegatedSourceListDismissed, void());
};
-class FakeScreenCapturer : public webrtc::DesktopCapturer {
+class FakeScreenCapturer : public ThumbnailCapturer {
public:
FakeScreenCapturer() {}
@@ -139,14 +139,18 @@ class FakeScreenCapturer : public webrtc::DesktopCapturer {
~FakeScreenCapturer() override {}
- // webrtc::ScreenCapturer implementation.
- void Start(Callback* callback) override { callback_ = callback; }
+ // ThumbnailCapturer implementation.
+ void Start(Consumer* consumer) override { consumer_ = consumer; }
+
+ FrameDeliveryMethod GetFrameDeliveryMethod() const override {
+ return FrameDeliveryMethod::kOnRequest;
+ }
void CaptureFrame() override {
- DCHECK(callback_);
+ DCHECK(consumer_);
std::unique_ptr<webrtc::DesktopFrame> frame(
new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10)));
- callback_->OnCaptureResult(webrtc::DesktopCapturer::Result::SUCCESS,
+ consumer_->OnCaptureResult(webrtc::DesktopCapturer::Result::SUCCESS,
std::move(frame));
}
@@ -161,10 +165,10 @@ class FakeScreenCapturer : public webrtc::DesktopCapturer {
}
protected:
- raw_ptr<Callback> callback_;
+ raw_ptr<Consumer> consumer_;
};
-class FakeWindowCapturer : public webrtc::DesktopCapturer {
+class FakeWindowCapturer : public ThumbnailCapturer {
public:
FakeWindowCapturer() = default;
explicit FakeWindowCapturer(const webrtc::DesktopCaptureOptions& options)
@@ -187,11 +191,15 @@ class FakeWindowCapturer : public webrtc::DesktopCapturer {
frame_values_[window_id] = value;
}
- // webrtc::WindowCapturer implementation.
- void Start(Callback* callback) override { callback_ = callback; }
+ // ThumbnailCapturer implementation.
+ void Start(Consumer* consumer) override { consumer_ = consumer; }
+
+ FrameDeliveryMethod GetFrameDeliveryMethod() const override {
+ return FrameDeliveryMethod::kOnRequest;
+ }
void CaptureFrame() override {
- DCHECK(callback_);
+ DCHECK(consumer_);
base::AutoLock lock(frame_values_lock_);
@@ -200,7 +208,7 @@ class FakeWindowCapturer : public webrtc::DesktopCapturer {
std::unique_ptr<webrtc::DesktopFrame> frame(
new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10)));
memset(frame->data(), value, frame->stride() * frame->size().height());
- callback_->OnCaptureResult(webrtc::DesktopCapturer::Result::SUCCESS,
+ consumer_->OnCaptureResult(webrtc::DesktopCapturer::Result::SUCCESS,
std::move(frame));
}
@@ -231,10 +239,8 @@ class FakeWindowCapturer : public webrtc::DesktopCapturer {
return true;
}
- bool FocusOnSelectedSource() override { return true; }
-
private:
- raw_ptr<Callback> callback_;
+ raw_ptr<Consumer> consumer_;
webrtc::DesktopCaptureOptions options_ =
webrtc::DesktopCaptureOptions::CreateDefault();
SourceList window_list_;
@@ -375,9 +381,9 @@ class NativeDesktopMediaListTest : public ChromeViewsTestBase {
void UpdateModel() {
base::RunLoop run_loop;
- base::OnceClosure update_callback =
+ base::OnceClosure update_consumer =
base::BindLambdaForTesting([&]() { run_loop.Quit(); });
- model_->Update(std::move(update_callback));
+ model_->Update(std::move(update_consumer));
run_loop.Run();
}
diff --git a/chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc b/chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
index e09cf953ed0..8c10488cf37 100644
--- a/chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
+++ b/chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
@@ -18,7 +18,6 @@
#include "chrome/common/pref_names.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/permissions/permission_result.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
#include "components/pref_registry/pref_registry_syncable.h"
@@ -79,8 +78,7 @@ void UpdatePageSpecificContentSettings(
return;
content_settings::PageSpecificContentSettings::MicrophoneCameraState
- microphone_camera_state = content_settings::PageSpecificContentSettings::
- MICROPHONE_CAMERA_NOT_ACCESSED;
+ microphone_camera_state;
std::string selected_audio_device;
std::string selected_video_device;
std::string requested_audio_device = request.requested_audio_device_id;
@@ -95,12 +93,12 @@ void UpdatePageSpecificContentSettings(
requested_audio_device.empty()
? profile->GetPrefs()->GetString(prefs::kDefaultAudioCaptureDevice)
: requested_audio_device;
- microphone_camera_state |=
- content_settings::PageSpecificContentSettings::MICROPHONE_ACCESSED |
- (audio_setting == CONTENT_SETTING_ALLOW
- ? 0
- : content_settings::PageSpecificContentSettings::
- MICROPHONE_BLOCKED);
+ microphone_camera_state.Put(
+ content_settings::PageSpecificContentSettings::kMicrophoneAccessed);
+ if (audio_setting != CONTENT_SETTING_ALLOW) {
+ microphone_camera_state.Put(
+ content_settings::PageSpecificContentSettings::kMicrophoneBlocked);
+ }
}
if (video_setting != CONTENT_SETTING_DEFAULT) {
@@ -108,11 +106,12 @@ void UpdatePageSpecificContentSettings(
requested_video_device.empty()
? profile->GetPrefs()->GetString(prefs::kDefaultVideoCaptureDevice)
: requested_video_device;
- microphone_camera_state |=
- content_settings::PageSpecificContentSettings::CAMERA_ACCESSED |
- (video_setting == CONTENT_SETTING_ALLOW
- ? 0
- : content_settings::PageSpecificContentSettings::CAMERA_BLOCKED);
+ microphone_camera_state.Put(
+ content_settings::PageSpecificContentSettings::kCameraAccessed);
+ if (video_setting != CONTENT_SETTING_ALLOW) {
+ microphone_camera_state.Put(
+ content_settings::PageSpecificContentSettings::kCameraBlocked);
+ }
}
// We should always use `GetLastCommittedURL` if web_contents represent NTP.
@@ -230,11 +229,15 @@ void PermissionBubbleMediaAccessHandler::HandleRequest(
}
void PermissionBubbleMediaAccessHandler::ProcessQueuedAccessRequest(
- content::WebContents* web_contents) {
+ MayBeDangling<content::WebContents> web_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // The queue is iterated through using a chain of PostTasks, and between these
+ // executions, `web_contents` might have been destroyed. In that case, the
+ // observer will have removed it from the map. Because all the accesses to the
+ // pending_requests_ map are made from the UI thread, we do not need to use a
+ // lock and simply verify that the WebContents is still there.
auto it = pending_requests_.find(web_contents);
-
if (it == pending_requests_.end() || it->second.empty()) {
// Don't do anything if the tab was closed.
return;
@@ -397,7 +400,7 @@ void PermissionBubbleMediaAccessHandler::OnAccessRequestResponse(
if (system_audio_permission == SystemPermission::kNotDetermined) {
// Using WeakPtr since callback can come at any time and we might be
// destroyed.
- system_media_permissions::RequestSystemAudioCapturePermisson(
+ system_media_permissions::RequestSystemAudioCapturePermission(
base::BindOnce(&PermissionBubbleMediaAccessHandler::
OnAccessRequestResponseForBinding,
weak_factory_.GetWeakPtr(), web_contents, request_id,
@@ -424,7 +427,7 @@ void PermissionBubbleMediaAccessHandler::OnAccessRequestResponse(
if (system_video_permission == SystemPermission::kNotDetermined) {
// Using WeakPtr since callback can come at any time and we might be
// destroyed.
- system_media_permissions::RequestSystemVideoCapturePermisson(
+ system_media_permissions::RequestSystemVideoCapturePermission(
base::BindOnce(&PermissionBubbleMediaAccessHandler::
OnAccessRequestResponseForBinding,
weak_factory_.GetWeakPtr(), web_contents, request_id,
@@ -455,8 +458,7 @@ void PermissionBubbleMediaAccessHandler::OnAccessRequestResponse(
FROM_HERE,
base::BindOnce(
&PermissionBubbleMediaAccessHandler::ProcessQueuedAccessRequest,
- base::Unretained(this),
- base::UnsafeDanglingUntriaged(web_contents)));
+ base::Unretained(this), base::UnsafeDangling(web_contents)));
}
std::move(callback).Run(stream_devices_set, final_result, std::move(ui));
diff --git a/chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h b/chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h
index 18901afcf77..7ba1bdd063b 100644
--- a/chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h
+++ b/chromium/chrome/browser/media/webrtc/permission_bubble_media_access_handler.h
@@ -55,7 +55,8 @@ class PermissionBubbleMediaAccessHandler
using RequestsMap = std::map<int64_t, PendingAccessRequest>;
using RequestsMaps = std::map<content::WebContents*, RequestsMap>;
- void ProcessQueuedAccessRequest(content::WebContents* web_contents);
+ void ProcessQueuedAccessRequest(
+ MayBeDangling<content::WebContents> web_contents);
void OnMediaStreamRequestResponse(
content::WebContents* web_contents,
int64_t request_id,
diff --git a/chromium/chrome/browser/media/webrtc/region_capture_browsertest.cc b/chromium/chrome/browser/media/webrtc/region_capture_browsertest.cc
index f8c651a517d..22eff01d74d 100644
--- a/chromium/chrome/browser/media/webrtc/region_capture_browsertest.cc
+++ b/chromium/chrome/browser/media/webrtc/region_capture_browsertest.cc
@@ -242,8 +242,8 @@ struct TabInfo {
return CreateNewElement(frame, "div", id);
}
- raw_ptr<Browser, DanglingUntriaged> browser;
- raw_ptr<WebContents, DanglingUntriaged> web_contents;
+ raw_ptr<Browser, AcrossTasksDanglingUntriaged> browser;
+ raw_ptr<WebContents, AcrossTasksDanglingUntriaged> web_contents;
int tab_strip_index;
};
@@ -709,15 +709,11 @@ IN_PROC_BROWSER_TEST_F(RegionCaptureClonesBrowserTest,
// Original track becomes unblocked for cropping after clone is GCed 1/3.
// TODO(crbug.com/1353349) Re-enable for macOS and ChromeOS after flakes are
// resolved.
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_CanCropOriginalTrackAfterCloneIsGarbageCollected \
- DISABLED_CanCropOriginalTrackAfterCloneIsGarbageCollected
-#else
-#define MAYBE_CanCropOriginalTrackAfterCloneIsGarbageCollected \
- CanCropOriginalTrackAfterCloneIsGarbageCollected
-#endif
-IN_PROC_BROWSER_TEST_F(RegionCaptureClonesBrowserTest,
- MAYBE_CanCropOriginalTrackAfterCloneIsGarbageCollected) {
+// TODO(crbug.com/1459313): Also flakes on linux-bfcache-rel, so turning the
+// test off entirely.
+IN_PROC_BROWSER_TEST_F(
+ RegionCaptureClonesBrowserTest,
+ DISABLED_CanCropOriginalTrackAfterCloneIsGarbageCollected) {
ManualSetUp();
ASSERT_TRUE(CloneTrack());
diff --git a/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h b/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h
index cff84c5b3d5..c16a577dd02 100644
--- a/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h
+++ b/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.h
@@ -21,25 +21,19 @@ enum class SystemPermission {
kMaxValue = kAllowed
};
-// On 10.14 and above: returns the system permission.
-// On 10.13 and below: returns |SystemPermission::kAllowed|, since there are no
-// system media capture permissions.
+// Returns the system permission to capture audio or video.
SystemPermission CheckSystemAudioCapturePermission();
SystemPermission CheckSystemVideoCapturePermission();
-// On 10.15 and above: returns the system permission.
-// On 10.14 and below: returns |SystemPermission::kAllowed|, since there are no
-// system screen capture permissions.
+// Returns the system permission to capture the screen.
SystemPermission CheckSystemScreenCapturePermission();
-// On 10.14 and above: requests system permission and returns. When requesting
-// permission, the OS will show a user dialog and respond asynchronously. At the
-// response, |callback| is posted as a reply on the requesting thread.
-// Note: these functions should really never be called for pre-10.14 since one
-// would normally check the permission first, and only call this if it's not
-// determined.
-void RequestSystemAudioCapturePermisson(base::OnceClosure callback);
-void RequestSystemVideoCapturePermisson(base::OnceClosure callback);
+// Requests the system permission to capture audio or video. This call
+// immediately returns. When requesting permission, the OS will show a user
+// dialog and respond asynchronously. At the response, |callback| is posted as a
+// reply on the requesting thread.
+void RequestSystemAudioCapturePermission(base::OnceClosure callback);
+void RequestSystemVideoCapturePermission(base::OnceClosure callback);
// Sets the wrapper object for OS calls. For test mocking purposes.
void SetMediaAuthorizationWrapperForTesting(MediaAuthorizationWrapper* wrapper);
diff --git a/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm b/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
index d9c0ffb9d3a..045024d326c 100644
--- a/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
+++ b/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
@@ -6,13 +6,13 @@
#import <AVFoundation/AVFoundation.h>
+#include "base/apple/foundation_util.h"
+#include "base/apple/scoped_cftyperef.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
-#include "base/mac/foundation_util.h"
-#include "base/mac/scoped_cftyperef.h"
#include "base/no_destructor.h"
#import "base/task/sequenced_task_runner.h"
#include "base/task/sequenced_task_runner.h"
@@ -22,10 +22,6 @@
#include "media/base/media_switches.h"
#include "ui/base/cocoa/permissions_utils.h"
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
namespace system_media_permissions {
namespace {
@@ -49,29 +45,21 @@ class MediaAuthorizationWrapperImpl final : public MediaAuthorizationWrapper {
~MediaAuthorizationWrapperImpl() override = default;
- NSInteger AuthorizationStatusForMediaType(AVMediaType media_type) override {
- if (@available(macOS 10.14, *)) {
- return [AVCaptureDevice authorizationStatusForMediaType:media_type];
- } else {
- CHECK(false);
- return 0;
- }
+ AVAuthorizationStatus AuthorizationStatusForMediaType(
+ AVMediaType media_type) override {
+ return [AVCaptureDevice authorizationStatusForMediaType:media_type];
}
void RequestAccessForMediaType(AVMediaType media_type,
base::OnceClosure callback) override {
- if (@available(macOS 10.14, *)) {
- __block base::OnceClosure block_callback = std::move(callback);
- __block scoped_refptr<base::SequencedTaskRunner> requesting_thread =
- base::SequencedTaskRunner::GetCurrentDefault();
- [AVCaptureDevice requestAccessForMediaType:media_type
- completionHandler:^(BOOL granted) {
- requesting_thread->PostTask(
- FROM_HERE, std::move(block_callback));
- }];
- } else {
- CHECK(false);
- }
+ __block base::OnceClosure block_callback = std::move(callback);
+ __block scoped_refptr<base::SequencedTaskRunner> requesting_thread =
+ base::SequencedTaskRunner::GetCurrentDefault();
+ [AVCaptureDevice requestAccessForMediaType:media_type
+ completionHandler:^(BOOL granted) {
+ requesting_thread->PostTask(
+ FROM_HERE, std::move(block_callback));
+ }];
}
};
@@ -84,38 +72,29 @@ MediaAuthorizationWrapper& GetMediaAuthorizationWrapper() {
return *media_authorization_wrapper;
}
-NSInteger MediaAuthorizationStatus(AVMediaType media_type) {
- if (@available(macOS 10.14, *)) {
- return GetMediaAuthorizationWrapper().AuthorizationStatusForMediaType(
- media_type);
- }
-
- CHECK(false);
- return 0;
+AVAuthorizationStatus MediaAuthorizationStatus(AVMediaType media_type) {
+ return GetMediaAuthorizationWrapper().AuthorizationStatusForMediaType(
+ media_type);
}
SystemPermission CheckSystemMediaCapturePermission(AVMediaType media_type) {
- if (UsingFakeMediaDevices())
+ if (UsingFakeMediaDevices()) {
return SystemPermission::kAllowed;
-
- if (@available(macOS 10.14, *)) {
- NSInteger auth_status = MediaAuthorizationStatus(media_type);
- switch (auth_status) {
- case AVAuthorizationStatusNotDetermined:
- return SystemPermission::kNotDetermined;
- case AVAuthorizationStatusRestricted:
- return SystemPermission::kRestricted;
- case AVAuthorizationStatusDenied:
- return SystemPermission::kDenied;
- case AVAuthorizationStatusAuthorized:
- return SystemPermission::kAllowed;
- default:
- NOTREACHED_NORETURN();
- }
}
- // On pre-10.14, there are no system permissions, so we return allowed.
- return SystemPermission::kAllowed;
+ AVAuthorizationStatus auth_status = MediaAuthorizationStatus(media_type);
+ switch (auth_status) {
+ case AVAuthorizationStatusNotDetermined:
+ return SystemPermission::kNotDetermined;
+ case AVAuthorizationStatusRestricted:
+ return SystemPermission::kRestricted;
+ case AVAuthorizationStatusDenied:
+ return SystemPermission::kDenied;
+ case AVAuthorizationStatusAuthorized:
+ return SystemPermission::kAllowed;
+ default:
+ NOTREACHED_NORETURN();
+ }
}
void RequestSystemMediaCapturePermission(AVMediaType media_type,
@@ -126,20 +105,14 @@ void RequestSystemMediaCapturePermission(AVMediaType media_type,
return;
}
- if (@available(macOS 10.14, *)) {
- GetMediaAuthorizationWrapper().RequestAccessForMediaType(
- media_type, std::move(callback));
- } else {
- CHECK(false);
- }
+ GetMediaAuthorizationWrapper().RequestAccessForMediaType(media_type,
+ std::move(callback));
}
bool IsScreenCaptureAllowed() {
- if (@available(macOS 10.15, *)) {
- if (!base::FeatureList::IsEnabled(
- features::kMacSystemScreenCapturePermissionCheck)) {
- return true;
- }
+ if (!base::FeatureList::IsEnabled(
+ features::kMacSystemScreenCapturePermissionCheck)) {
+ return true;
}
bool allowed = ui::IsScreenCaptureAllowed();
@@ -162,11 +135,11 @@ SystemPermission CheckSystemScreenCapturePermission() {
: SystemPermission::kDenied;
}
-void RequestSystemAudioCapturePermisson(base::OnceClosure callback) {
+void RequestSystemAudioCapturePermission(base::OnceClosure callback) {
RequestSystemMediaCapturePermission(AVMediaTypeAudio, std::move(callback));
}
-void RequestSystemVideoCapturePermisson(base::OnceClosure callback) {
+void RequestSystemVideoCapturePermission(base::OnceClosure callback) {
RequestSystemMediaCapturePermission(AVMediaTypeVideo, std::move(callback));
}
diff --git a/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.mm b/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.mm
index c8ab96a87f5..a1ec954bc20 100644
--- a/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.mm
+++ b/chromium/chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.mm
@@ -10,10 +10,6 @@
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
namespace system_media_permissions {
namespace {
@@ -130,49 +126,37 @@ void MaybeLogAdditionalCameraSystemPermissionStats(
} // namespace
void RegisterSystemMediaPermissionStatesPrefs(PrefRegistrySimple* registry) {
- if (@available(macOS 10.14, *)) {
- registry->RegisterTimePref(kSystemPermissionMicFirstBlockedTimePref,
- base::Time());
- registry->RegisterTimePref(kSystemPermissionMicLastBlockedTimePref,
- base::Time());
- registry->RegisterTimePref(kSystemPermissionCameraFirstBlockedTimePref,
- base::Time());
- registry->RegisterTimePref(kSystemPermissionCameraLastBlockedTimePref,
- base::Time());
- }
+ registry->RegisterTimePref(kSystemPermissionMicFirstBlockedTimePref,
+ base::Time());
+ registry->RegisterTimePref(kSystemPermissionMicLastBlockedTimePref,
+ base::Time());
+ registry->RegisterTimePref(kSystemPermissionCameraFirstBlockedTimePref,
+ base::Time());
+ registry->RegisterTimePref(kSystemPermissionCameraLastBlockedTimePref,
+ base::Time());
}
void LogSystemMediaPermissionsStartupStats() {
- if (@available(macOS 10.14, *)) {
- const SystemPermission audio_permission =
- CheckSystemAudioCapturePermission();
- LogStartupMicSystemPermission(audio_permission);
- MaybeLogAdditionalMicSystemPermissionStats(audio_permission);
-
- const SystemPermission video_permission =
- CheckSystemVideoCapturePermission();
- LogStartupCameraSystemPermission(video_permission);
- MaybeLogAdditionalCameraSystemPermissionStats(video_permission);
- }
+ const SystemPermission audio_permission = CheckSystemAudioCapturePermission();
+ LogStartupMicSystemPermission(audio_permission);
+ MaybeLogAdditionalMicSystemPermissionStats(audio_permission);
- if (@available(macOS 10.15, *)) {
- // CheckSystemScreenCapturePermission() will log a sample of the permission.
- CheckSystemScreenCapturePermission();
- }
+ const SystemPermission video_permission = CheckSystemVideoCapturePermission();
+ LogStartupCameraSystemPermission(video_permission);
+ MaybeLogAdditionalCameraSystemPermissionStats(video_permission);
+
+ // CheckSystemScreenCapturePermission() will log a sample of the permission.
+ CheckSystemScreenCapturePermission();
}
void SystemAudioCapturePermissionDetermined(SystemPermission permission) {
- if (@available(macOS 10.14, *)) {
- DCHECK_NE(permission, SystemPermission::kNotDetermined);
- LogStartupMicSystemPermission(permission);
- }
+ DCHECK_NE(permission, SystemPermission::kNotDetermined);
+ LogStartupMicSystemPermission(permission);
}
void SystemVideoCapturePermissionDetermined(SystemPermission permission) {
- if (@available(macOS 10.14, *)) {
- DCHECK_NE(permission, SystemPermission::kNotDetermined);
- LogStartupCameraSystemPermission(permission);
- }
+ DCHECK_NE(permission, SystemPermission::kNotDetermined);
+ LogStartupCameraSystemPermission(permission);
}
void LogSystemScreenCapturePermission(bool allowed) {
@@ -181,26 +165,20 @@ void LogSystemScreenCapturePermission(bool allowed) {
}
void SystemAudioCapturePermissionBlocked() {
- if (@available(macOS 10.14, *)) {
- PrefService* prefs = g_browser_process->local_state();
- if (!prefs->HasPrefPath(kSystemPermissionMicFirstBlockedTimePref)) {
- prefs->SetTime(kSystemPermissionMicFirstBlockedTimePref,
- base::Time::Now());
- }
- prefs->SetTime(kSystemPermissionMicLastBlockedTimePref, base::Time::Now());
+ PrefService* prefs = g_browser_process->local_state();
+ if (!prefs->HasPrefPath(kSystemPermissionMicFirstBlockedTimePref)) {
+ prefs->SetTime(kSystemPermissionMicFirstBlockedTimePref, base::Time::Now());
}
+ prefs->SetTime(kSystemPermissionMicLastBlockedTimePref, base::Time::Now());
}
void SystemVideoCapturePermissionBlocked() {
- if (@available(macOS 10.14, *)) {
- PrefService* prefs = g_browser_process->local_state();
- if (!prefs->HasPrefPath(kSystemPermissionCameraFirstBlockedTimePref)) {
- prefs->SetTime(kSystemPermissionCameraFirstBlockedTimePref,
- base::Time::Now());
- }
- prefs->SetTime(kSystemPermissionCameraLastBlockedTimePref,
+ PrefService* prefs = g_browser_process->local_state();
+ if (!prefs->HasPrefPath(kSystemPermissionCameraFirstBlockedTimePref)) {
+ prefs->SetTime(kSystemPermissionCameraFirstBlockedTimePref,
base::Time::Now());
}
+ prefs->SetTime(kSystemPermissionCameraLastBlockedTimePref, base::Time::Now());
}
} // namespace system_media_permissions
diff --git a/chromium/chrome/browser/media/webrtc/tab_capture_access_handler_unittest.cc b/chromium/chrome/browser/media/webrtc/tab_capture_access_handler_unittest.cc
index d26fc4fe4c7..e2d7e56bc41 100644
--- a/chromium/chrome/browser/media/webrtc/tab_capture_access_handler_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/tab_capture_access_handler_unittest.cc
@@ -25,7 +25,7 @@
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
-#include "chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_content_manager.h"
#endif // BUILDFLAG(IS_CHROMEOS)
namespace {
diff --git a/chromium/chrome/browser/media/webrtc/tab_desktop_media_list_unittest.cc b/chromium/chrome/browser/media/webrtc/tab_desktop_media_list_unittest.cc
index 5064ae76c22..5c4c7d98303 100644
--- a/chromium/chrome/browser/media/webrtc/tab_desktop_media_list_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/tab_desktop_media_list_unittest.cc
@@ -83,8 +83,6 @@ gfx::Image CreateGrayscaleImage(gfx::Size size, uint8_t greyscale_value) {
return gfx::Image::CreateFrom1xBitmap(result);
}
-} // namespace
-
class MockObserver : public DesktopMediaListObserver {
public:
MOCK_METHOD1(OnSourceAdded, void(int index));
@@ -152,6 +150,8 @@ class TestAppWindow : public content::WebContentsObserver {
raw_ptr<extensions::AppWindow> window_;
};
+} // namespace
+
class TabDesktopMediaListTest : public testing::Test,
public testing::WithParamInterface<bool> {
public:
diff --git a/chromium/chrome/browser/media/webrtc/thumbnail_capturer.cc b/chromium/chrome/browser/media/webrtc/thumbnail_capturer.cc
new file mode 100644
index 00000000000..623a4e471b1
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/thumbnail_capturer.cc
@@ -0,0 +1,29 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/webrtc/thumbnail_capturer.h"
+
+#include "base/notreached.h"
+
+void ThumbnailCapturer::SetMaxFrameRate(uint32_t max_frame_rate) {
+ NOTREACHED_NORETURN();
+}
+
+webrtc::DelegatedSourceListController*
+ThumbnailCapturer::GetDelegatedSourceListController() {
+ return nullptr;
+}
+
+void ThumbnailCapturer::CaptureFrame() {
+ NOTREACHED_NORETURN();
+}
+
+bool ThumbnailCapturer::SelectSource(SourceId id) {
+ NOTREACHED_NORETURN();
+}
+
+void ThumbnailCapturer::SelectSources(const std::vector<SourceId>& ids,
+ gfx::Size thumbnail_size) {
+ NOTREACHED_NORETURN();
+}
diff --git a/chromium/chrome/browser/media/webrtc/thumbnail_capturer.h b/chromium/chrome/browser/media/webrtc/thumbnail_capturer.h
new file mode 100644
index 00000000000..72033dcca5c
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/thumbnail_capturer.h
@@ -0,0 +1,114 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_WEBRTC_THUMBNAIL_CAPTURER_H_
+#define CHROME_BROWSER_MEDIA_WEBRTC_THUMBNAIL_CAPTURER_H_
+
+#include "third_party/webrtc/modules/desktop_capture/delegated_source_list_controller.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
+#include "ui/gfx/geometry/size.h"
+
+// Class that is used to produces native desktop media thumbnails. Two modes are
+// supported, delivering frames for a selected source on request (compatible
+// with webrtc::DesktopCapturer) and delivering frames for selected sources on
+// change.
+class ThumbnailCapturer {
+ public:
+ // Reuse the existing types in webrtc::DesktopCapturer.
+ using Result = webrtc::DesktopCapturer::Result;
+ using SourceId = webrtc::DesktopCapturer::SourceId;
+ using Source = webrtc::DesktopCapturer::Source;
+ using SourceList = webrtc::DesktopCapturer::SourceList;
+
+ enum class FrameDeliveryMethod {
+ // Frames are delivered on request for the selected source when
+ // CaptureFrame() is called.
+ kOnRequest,
+ // Frames are delivered recurrently at the specified maximum frame rate when
+ // the selected sources are updated.
+ kMultipleSourcesRecurrent,
+ };
+
+ class Consumer : public webrtc::DesktopCapturer::Callback {
+ public:
+ // Called recurrently after a new frame has been captured. `frame` is not
+ // nullptr if and only if `result` is SUCCESS. `source_id` specifies the id
+ // of the captured source.
+ virtual void OnRecurrentCaptureResult(
+ Result result,
+ std::unique_ptr<webrtc::DesktopFrame> frame,
+ SourceId source_id) = 0;
+
+ // Called after the list of sources has been updated if the frame deliver
+ // method is set to kMultipleSourcesRecurrent.
+ virtual void OnSourceListUpdated() = 0;
+ };
+
+ virtual ~ThumbnailCapturer() = default;
+
+ // Called at the beginning of a capturing session. If the frame
+ // delivery method is kMultipleSourcesRecurrent, the capturer will begin
+ // capturing the sources in the list that have been selected through the call
+ // to SelectSources() and call Consumer::OnRecurrentCaptureResult() for each
+ // captured frame. Consumer::OnSourceListUpdated() is called whenever the
+ // source list is changed. `consumer` must remain valid until capturer is
+ // destroyed.
+ virtual void Start(Consumer* consumer) = 0;
+
+ // Returns the frame delivery method that is used by the capturer.
+ virtual FrameDeliveryMethod GetFrameDeliveryMethod() const = 0;
+
+ // Sets max frame rate for the capturer. This is best effort and may not be
+ // supported by all capturers. This will only affect the frequency at which
+ // new frames are available, not the frequency at which you are allowed to
+ // capture the frames.
+ virtual void SetMaxFrameRate(uint32_t max_frame_rate);
+
+ // Returns a valid pointer if the capturer requires the user to make a
+ // selection from a source list provided by the capturer.
+ // Returns nullptr if the capturer does not provide a UI for the user to make
+ // a selection.
+ //
+ // Callers should not take ownership of the returned pointer, but it is
+ // guaranteed to be valid as long as the desktop_capturer is valid.
+ // Note that consumers should still use GetSourceList and SelectSource, but
+ // their behavior may be modified if this returns a value. See those methods
+ // for a more in-depth discussion of those potential modifications.
+ virtual webrtc::DelegatedSourceListController*
+ GetDelegatedSourceListController();
+
+ // Captures next frame, and involve callback provided by Start() function.
+ // Pending capture requests are canceled when DesktopCapturer is deleted. Can
+ // only be invoked if the frame delivery method is kOnRequest.
+ virtual void CaptureFrame();
+
+ // Gets a list of sources current capturer supports. Returns false in case of
+ // a failure.
+ // For DesktopCapturer implementations to capture screens, this function
+ // should return monitors.
+ // For DesktopCapturer implementations to capture windows, this function
+ // should only return root windows owned by applications.
+ //
+ // Note that capturers who use a delegated source list will return a
+ // SourceList with exactly one value, but it may not be viable for capture
+ // (e.g. CaptureFrame will return ERROR_TEMPORARY) until a selection has been
+ // made.
+ virtual bool GetSourceList(SourceList* sources) = 0;
+
+ // Selects a source to be captured. Returns false in case of a failure (e.g.
+ // if there is no source with the specified type and id.)
+ //
+ // Note that some capturers with delegated source lists may also support
+ // selecting a SourceID that is not in the returned source list as a form of
+ // restore token. Can only be invoked if the frame delivery method is
+ // kOnRequest.
+ virtual bool SelectSource(SourceId id);
+
+ // Selects sources to be captured simultaneously. Multiple sources can only be
+ // selected if the frame delivery method is kMultipleSourcesRecurrent.
+ virtual void SelectSources(const std::vector<SourceId>& ids,
+ gfx::Size thumbnail_size);
+};
+
+#endif // CHROME_BROWSER_MEDIA_WEBRTC_THUMBNAIL_CAPTURER_H_
diff --git a/chromium/chrome/browser/media/webrtc/thumbnail_capturer_mac.h b/chromium/chrome/browser/media/webrtc/thumbnail_capturer_mac.h
new file mode 100644
index 00000000000..56a3b0b04c5
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/thumbnail_capturer_mac.h
@@ -0,0 +1,17 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_WEBRTC_THUMBNAIL_CAPTURER_MAC_H_
+#define CHROME_BROWSER_MEDIA_WEBRTC_THUMBNAIL_CAPTURER_MAC_H_
+
+#include "chrome/browser/media/webrtc/thumbnail_capturer.h"
+
+// Returns true if the SCK thumbnail capturer is available and enabled.
+bool ShouldUseThumbnailCapturerMac();
+
+// Creates a ThumbnailCaptureMac object. Must only be called is
+// ShouldUseThumbnailCapturerMac() returns true.
+std::unique_ptr<ThumbnailCapturer> CreateThumbnailCapturerMac();
+
+#endif // CHROME_BROWSER_MEDIA_WEBRTC_THUMBNAIL_CAPTURER_MAC_H_
diff --git a/chromium/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm b/chromium/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
new file mode 100644
index 00000000000..ac5b1a7f604
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
@@ -0,0 +1,876 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/webrtc/thumbnail_capturer_mac.h"
+
+#include <AvailabilityMacros.h>
+#include <CoreGraphics/CoreGraphics.h>
+#import <Foundation/Foundation.h>
+#import <ScreenCaptureKit/ScreenCaptureKit.h>
+#include <VideoToolbox/VideoToolbox.h>
+
+#include <cmath>
+#include <deque>
+
+#include "base/apple/bridging.h"
+#include "base/apple/foundation_util.h"
+#include "base/apple/scoped_cftyperef.h"
+#include "base/containers/adapters.h"
+#include "base/containers/contains.h"
+#include "base/containers/flat_map.h"
+#include "base/feature_list.h"
+#include "base/functional/callback.h"
+#include "base/logging.h"
+#include "base/memory/raw_ptr.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/task/bind_post_task.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/media/webrtc/desktop_media_list_base.h"
+#include "media/capture/video_capture_types.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/webrtc/modules/desktop_capture/mac/desktop_frame_utils.h"
+
+// Declaration of SCScreenshotManager that is part of the 14.0 SDK.
+#if !defined(MAC_OS_VERSION_14_0) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_14_0
+
+NS_ASSUME_NONNULL_BEGIN
+
+API_AVAILABLE(macos(14.0))
+@interface SCScreenshotManager : NSObject
+
++ (void)captureImageWithFilter:(SCContentFilter*)contentFilter
+ configuration:(SCStreamConfiguration*)config
+ completionHandler:
+ (nullable void (^)(CGImageRef _Nullable sampleBuffer,
+ NSError* _Nullable error))completionHandler
+ API_AVAILABLE(macos(14.0));
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif // MAC_OS_X_VERSION_14_0
+
+using SampleCallback =
+ base::RepeatingCallback<void(base::apple::ScopedCFTypeRef<CGImageRef> image,
+ ThumbnailCapturer::SourceId source_id)>;
+using ErrorCallback = base::RepeatingClosure;
+
+API_AVAILABLE(macos(13.2))
+@interface SCKStreamDelegateAndOutput
+ : NSObject <SCStreamDelegate, SCStreamOutput>
+
+- (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback
+ errorCallback:(ErrorCallback)errorCallback
+ sourceId:(ThumbnailCapturer::SourceId)sourceId;
+@end
+
+@implementation SCKStreamDelegateAndOutput {
+ SampleCallback _sampleCallback;
+ ErrorCallback _errorCallback;
+ ThumbnailCapturer::SourceId _sourceId;
+}
+
+- (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback
+ errorCallback:(ErrorCallback)errorCallback
+ sourceId:(ThumbnailCapturer::SourceId)sourceId {
+ if (self = [super init]) {
+ _sampleCallback = sampleCallback;
+ _errorCallback = errorCallback;
+ _sourceId = sourceId;
+ }
+ return self;
+}
+
++ (CGRect)cropRegionFromSampleBuffer:(CMSampleBufferRef)sampleBuffer {
+ // Read out the content region from the sample buffer attachment metadata and
+ // use as crop region. The content region is determined from the attachments
+ // SCStreamFrameInfoContentRect and SCStreamFrameInfoScaleFactor.
+
+ CFArrayRef attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(
+ sampleBuffer, /*createIfNecessary=*/false);
+ if (!attachmentsArray || CFArrayGetCount(attachmentsArray) <= 0) {
+ return {};
+ }
+
+ CFDictionaryRef attachment = base::apple::CFCast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(attachmentsArray, 0));
+ if (!attachment) {
+ return {};
+ }
+
+ CFDictionaryRef contentRectValue = base::apple::CFCast<CFDictionaryRef>(
+ CFDictionaryGetValue(attachment, base::apple::NSToCFPtrCast(
+ SCStreamFrameInfoContentRect)));
+ CFNumberRef scaleFactorValue = base::apple::CFCast<CFNumberRef>(
+ CFDictionaryGetValue(attachment, base::apple::NSToCFPtrCast(
+ SCStreamFrameInfoScaleFactor)));
+ if (!contentRectValue || !scaleFactorValue) {
+ return {};
+ }
+
+ CGRect contentRect = {};
+ float scaleFactor = 1.0f;
+ if (!CGRectMakeWithDictionaryRepresentation(contentRectValue, &contentRect) ||
+ !CFNumberGetValue(scaleFactorValue, kCFNumberFloatType, &scaleFactor)) {
+ return {};
+ }
+
+ contentRect.size.width *= scaleFactor;
+ contentRect.size.height *= scaleFactor;
+ return contentRect;
+}
+
+- (void)stream:(SCStream*)stream
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ ofType:(SCStreamOutputType)type {
+ CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+ if (!pixelBuffer) {
+ return;
+ }
+ base::apple::ScopedCFTypeRef<CGImageRef> cgImage;
+ auto result = VTCreateCGImageFromCVPixelBuffer(pixelBuffer, nil,
+ cgImage.InitializeInto());
+
+ if (result != 0) {
+ return;
+ }
+
+ // Avoid having black regions on the sides by cropping the image to the
+ // content region.
+ CGRect cropRegion =
+ [SCKStreamDelegateAndOutput cropRegionFromSampleBuffer:sampleBuffer];
+ if (CGRectIsEmpty(cropRegion)) {
+ return;
+ }
+
+ base::apple::ScopedCFTypeRef<CGImageRef> croppedImage(
+ CGImageCreateWithImageInRect(cgImage, cropRegion));
+ _sampleCallback.Run(croppedImage, _sourceId);
+}
+
+- (void)stream:(SCStream*)stream didStopWithError:(NSError*)error {
+ _errorCallback.Run();
+}
+
+@end
+
+namespace {
+
+BASE_FEATURE(kScreenCaptureKitStreamPickerSonoma,
+ "ScreenCaptureKitStreamPickerSonoma",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
+BASE_FEATURE(kScreenCaptureKitStreamPickerVentura,
+ "ScreenCaptureKitStreamPickerVentura",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
+// The enable/disable property of this feature has no impact. The feature is
+// used solely to pass on the parameters below.
+BASE_FEATURE(kThumbnailCapturerMac,
+ "ThumbnailCapturerMac",
+ base::FEATURE_ENABLED_BY_DEFAULT);
+
+// Default max frame rate that is used when capturing thumbnails. This is per
+// source so the combined frame rate can be higher if there are multiple
+// sources.
+const base::FeatureParam<int> kThumbnailCapturerMacMaxFrameRate{
+ &kThumbnailCapturerMac, "max_frame_rate", 1};
+
+// Refresh interval that is used to query for the list of shareable content.
+const base::FeatureParam<base::TimeDelta>
+ kThumbnailCapturerMacRefreshTimerInterval{&kThumbnailCapturerMac,
+ "refresh_timer_interval",
+ base::Milliseconds(250)};
+
+// The capture mode controls how the thumbnails are captured.
+enum class CaptureMode {
+ // Create an SCStream for each selected source. In this mode frames are pushed
+ // by the OS at the specified maximum frame rate.
+ kSCStream = 0,
+ // Use SCScreenshotManager to capture frames. In this mode a timer is used to
+ // periodically capture the selected windows in a pull-based fashion. Please
+ // note that this mode is only available in macOS 14.0 and later.
+ kSCScreenshotManager = 1
+};
+const base::FeatureParam<CaptureMode>::Option capture_mode_options[] = {
+ {CaptureMode::kSCStream, "sc_stream"},
+ {CaptureMode::kSCScreenshotManager, "sc_screenshot_manager"},
+};
+const base::FeatureParam<CaptureMode> kThumbnailCapturerMacCaptureMode{
+ &kThumbnailCapturerMac, "capture_mode", CaptureMode::kSCStream,
+ &capture_mode_options};
+
+CaptureMode GetCaptureModeFeatureParam() {
+ if (@available(macOS 14.0, *)) {
+ return kThumbnailCapturerMacCaptureMode.Get();
+ }
+ return CaptureMode::kSCStream;
+}
+
+// The sort mode controls the order of the source list that is returned from
+// GetSourceList().
+enum class SortMode {
+ // No extra sorting, same order as returned by SCShareableContent.
+ kNone = 0,
+ // Same order as returned by CGWindowListCopyWindowInfo().
+ kCGWindowList = 1,
+ // Static order where new windows are put last in the list.
+ kNewWindowsLast = 2
+};
+const base::FeatureParam<SortMode>::Option sort_mode_options[] = {
+ {SortMode::kNone, "none"},
+ {SortMode::kCGWindowList, "cg_window_list"},
+ {SortMode::kNewWindowsLast, "new_windows_last"},
+};
+const base::FeatureParam<SortMode> kThumbnailCapturerMacSortMode{
+ &kThumbnailCapturerMac, "sort_mode", SortMode::kCGWindowList,
+ &sort_mode_options};
+
+// The minimum window size that is still considered to be a shareable window.
+// Windows with smaller height or widht are filtered out.
+const base::FeatureParam<int> kThumbnailCapturerMacMinWindowSize{
+ &kThumbnailCapturerMac, "min_window_size", 40};
+
+// Controls the maximum number of sources that are captured during each capture
+// cycle if the capture mode is set to kSCScreenshotManager. By having a limit
+// and cycling through what windows are captured we get a graceful degradation.
+const base::FeatureParam<int> kThumbnailCapturerMacMaxSourcesPerCycles{
+ &kThumbnailCapturerMac, "max_sources_per_cycles", 25};
+
+bool API_AVAILABLE(macos(12.3))
+ IsWindowFullscreen(SCWindow* window, NSArray<SCDisplay*>* displays) {
+ for (SCDisplay* display : displays) {
+ if (CGRectEqualToRect(window.frame, display.frame)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+SCWindow* API_AVAILABLE(macos(12.3))
+ FindWindow(NSArray<SCWindow*>* array, CGWindowID window_id) {
+ for (SCWindow* window in array) {
+ if ([window windowID] == window_id) {
+ return window;
+ }
+ }
+ return nil;
+}
+
+CGWindowID GetWindowId(CFArrayRef window_array, CFIndex index) {
+ CFDictionaryRef window_ref = reinterpret_cast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(window_array, index));
+ if (!window_ref) {
+ return kCGNullWindowID;
+ }
+
+ CFNumberRef window_id_ref = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window_ref, kCGWindowNumber));
+ if (!window_id_ref) {
+ return kCGNullWindowID;
+ }
+ CGWindowID window_id;
+ if (!CFNumberGetValue(window_id_ref, kCFNumberIntType, &window_id)) {
+ return kCGNullWindowID;
+ }
+ return window_id;
+}
+
+class API_AVAILABLE(macos(12.3)) ScreenshotManagerCapturer {
+ public:
+ using GetShareableWindowCallback =
+ base::RepeatingCallback<SCWindow*(ThumbnailCapturer::SourceId source_id)>;
+
+ ScreenshotManagerCapturer(
+ int max_frame_rate,
+ GetShareableWindowCallback get_shareable_window_callback,
+ SampleCallback sample_callback);
+ void SelectSources(const std::vector<ThumbnailCapturer::SourceId>& ids,
+ gfx::Size thumbnail_size);
+
+ private:
+ void API_AVAILABLE(macos(14.0)) OnRecurrentCaptureTimer();
+ void API_AVAILABLE(macos(14.0)) SCScreenshotCaptureWindow(SCWindow* window);
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ // Callback to retrieve an SCWindow* based on windowID.
+ GetShareableWindowCallback get_shareable_window_callback_;
+
+ // Callback that is used whenever a thumbnail is captured.
+ SampleCallback sample_callback_;
+
+ // The maximum number of sources that can be captured in each capture cycle.
+ // We have a limit here to not spawn hundreds of capturers at the same time
+ // since this could degrade the system performance.
+ const size_t max_sources_per_cycle_;
+
+ // The selected sources, this is used to determine if a selected source was
+ // not selected before and give priority to the source in this case.
+ std::vector<ThumbnailCapturer::SourceId> selected_sources_;
+
+ // The capture queue is used to maintain a list of all selected sources and
+ // keep track of what source should be captured next in the case that too many
+ // sources are selected and we cannot capture all sources in each capture
+ // cycle.
+ std::deque<ThumbnailCapturer::SourceId> capture_queue_;
+
+ gfx::Size thumbnail_size_ = kDefaultThumbnailSize;
+
+ base::RepeatingTimer capture_frame_timer_;
+};
+
+ScreenshotManagerCapturer::ScreenshotManagerCapturer(
+ int max_frame_rate,
+ GetShareableWindowCallback get_shareable_window_callback,
+ SampleCallback sample_callback)
+ : task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
+ get_shareable_window_callback_(get_shareable_window_callback),
+ sample_callback_(sample_callback),
+ max_sources_per_cycle_(kThumbnailCapturerMacMaxSourcesPerCycles.Get()) {
+ if (@available(macOS 14.0, *)) {
+ capture_frame_timer_.Start(
+ FROM_HERE, base::Milliseconds(1000.0 / max_frame_rate), this,
+ &ScreenshotManagerCapturer::OnRecurrentCaptureTimer);
+ }
+}
+
+void ScreenshotManagerCapturer::SelectSources(
+ const std::vector<ThumbnailCapturer::SourceId>& ids,
+ gfx::Size thumbnail_size) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ // The iteration is in reverse order so that the sources
+ // first in the list are captured first. This way we make sure that the first
+ // thumbnails in the view are captured first.
+ bool new_sources_added = false;
+ for (ThumbnailCapturer::SourceId source_id : base::Reversed(ids)) {
+ if (!base::Contains(selected_sources_, source_id)) {
+ capture_queue_.push_front(source_id);
+ new_sources_added = true;
+ }
+ }
+
+ selected_sources_ = ids;
+ if (new_sources_added) {
+ // Run the capture code immediately to avoid a short period with empty
+ // thumbnails at the top of the list. This is especially useful in the first
+ // call to SelectSources().
+ if (@available(macOS 14.0, *)) {
+ OnRecurrentCaptureTimer();
+ capture_frame_timer_.Reset();
+ }
+ }
+}
+
+void ScreenshotManagerCapturer::OnRecurrentCaptureTimer() {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ if (capture_queue_.empty()) {
+ return;
+ }
+
+ // Take source ids from the top of the queue and capture the corresponding
+ // window if it is still selected and exists in the list of shareable windows.
+ // Finally put the source at the back of the queue to be captured again later.
+ size_t sources_to_capture =
+ std::min(capture_queue_.size(), max_sources_per_cycle_);
+ for (size_t i = 0; i < sources_to_capture; ++i) {
+ ThumbnailCapturer::SourceId source_id = capture_queue_.front();
+ capture_queue_.pop_front();
+ if (!base::Contains(selected_sources_, source_id)) {
+ continue;
+ }
+
+ // Find the corresponding SCWindow in the list.
+ SCWindow* selected_window = get_shareable_window_callback_.Run(source_id);
+ if (!selected_window) {
+ continue;
+ }
+
+ SCScreenshotCaptureWindow(selected_window);
+
+ // We want to capture the source again eventually, so put it last in the
+ // queue.
+ capture_queue_.push_back(source_id);
+ }
+}
+
+void ScreenshotManagerCapturer::SCScreenshotCaptureWindow(SCWindow* window) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ // Create SCStreamConfiguration.
+ SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init];
+ config.scalesToFit = YES;
+ config.showsCursor = NO;
+
+ // Avoid black regions in the captured frame by setting width and height to
+ // the same aspect ratio as the window.
+ float thumbnail_aspect_ratio = static_cast<float>(thumbnail_size_.width()) /
+ static_cast<float>(thumbnail_size_.height());
+ float window_aspect_ratio =
+ window.frame.size.width / window.frame.size.height;
+ if (window_aspect_ratio > thumbnail_aspect_ratio) {
+ config.width = thumbnail_size_.width();
+ config.height = std::round(thumbnail_size_.width() / window_aspect_ratio);
+ } else {
+ config.height = thumbnail_size_.height();
+ config.width = std::round(thumbnail_size_.height() * window_aspect_ratio);
+ }
+
+ SCContentFilter* filter =
+ [[SCContentFilter alloc] initWithDesktopIndependentWindow:window];
+
+ auto captured_frame_callback =
+ base::BindPostTask(task_runner_, sample_callback_);
+
+ auto handler = ^(CGImageRef sampleBuffer, NSError* error) {
+ if (error) {
+ return;
+ }
+ base::apple::ScopedCFTypeRef<CGImageRef> scopedImage(
+ sampleBuffer, base::scoped_policy::RETAIN);
+ captured_frame_callback.Run(scopedImage, [window windowID]);
+ };
+
+ static Class sc_screenshot_manager_class =
+ NSClassFromString(@"SCScreenshotManager");
+ if (!sc_screenshot_manager_class) {
+ return;
+ }
+ [sc_screenshot_manager_class captureImageWithFilter:filter
+ configuration:config
+ completionHandler:handler];
+}
+
+class API_AVAILABLE(macos(13.2)) ThumbnailCapturerMac
+ : public ThumbnailCapturer {
+ public:
+ ThumbnailCapturerMac();
+ ~ThumbnailCapturerMac() override{};
+
+ void Start(Consumer* callback) override;
+
+ FrameDeliveryMethod GetFrameDeliveryMethod() const override {
+ return FrameDeliveryMethod::kMultipleSourcesRecurrent;
+ }
+
+ // Sets the maximum frame rate for the thumbnail streams. This should be
+ // called before the call to Start because any stream that is already created
+ // will not be affected by the change to max frame rate.
+ void SetMaxFrameRate(uint32_t max_frame_rate) override;
+
+ bool GetSourceList(SourceList* sources) override;
+
+ void SelectSources(const std::vector<SourceId>& ids,
+ gfx::Size thumbnail_size) override;
+
+ private:
+ struct StreamAndDelegate {
+ SCStream* __strong stream;
+ SCKStreamDelegateAndOutput* __strong delegate;
+ };
+
+ void UpdateWindowsList();
+ void OnRecurrentShareableContent(SCShareableContent* content);
+
+ void UpdateShareableWindows(NSArray<SCWindow*>* content_windows);
+ SCWindow* GetShareableWindow(SourceId source_id) const;
+
+ // Returns the supplied list of windows sorted to have the same order as
+ // returned from CGWindowListCopyWindowInfo.
+ NSArray<SCWindow*>* SortOrderByCGWindowList(
+ NSArray<SCWindow*>* current_windows) const;
+
+ // Returns the supplied list of windows sorted so that new windows (i.e., not
+ // currently in shareable_windows_) are put last in the list.
+ NSArray<SCWindow*>* SortOrderByNewWindowsLast(
+ NSArray<SCWindow*>* current_windows) const;
+
+ bool IsShareable(SCWindow* window) const;
+ NSArray<SCWindow*>* FilterOutUnshareable(NSArray<SCWindow*>* windows);
+ void RemoveInactiveStreams();
+ void OnCapturedFrame(base::apple::ScopedCFTypeRef<CGImageRef> image,
+ SourceId source_id);
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ const CaptureMode capture_mode_;
+ const SortMode sort_mode_;
+ int max_frame_rate_;
+ const int minimum_window_size_;
+ raw_ptr<Consumer> consumer_;
+
+ // A cache of the shareable windows and shareable displays. sharable_windows_
+ // is used to produce the source list. shareable_displays_ is used to
+ // determine if a window is fullscreen or not. Both are updated continuously
+ // by the refresh_timer_.
+ NSArray<SCWindow*>* __strong shareable_windows_;
+ NSArray<SCDisplay*>* __strong shareable_displays_;
+
+ base::flat_map<SourceId, StreamAndDelegate> streams_;
+ base::RepeatingTimer refresh_timer_;
+
+ std::unique_ptr<ScreenshotManagerCapturer> screenshot_manager_capturer_;
+
+ base::WeakPtrFactory<ThumbnailCapturerMac> weak_factory_{this};
+};
+
+ThumbnailCapturerMac::ThumbnailCapturerMac()
+ : capture_mode_(GetCaptureModeFeatureParam()),
+ sort_mode_(kThumbnailCapturerMacSortMode.Get()),
+ max_frame_rate_(kThumbnailCapturerMacMaxFrameRate.Get()),
+ minimum_window_size_(kThumbnailCapturerMacMinWindowSize.Get()),
+ shareable_windows_([[NSArray<SCWindow*> alloc] init]) {}
+
+void ThumbnailCapturerMac::Start(Consumer* consumer) {
+ consumer_ = consumer;
+ task_runner_ = base::SingleThreadTaskRunner::GetCurrentDefault();
+ UpdateWindowsList();
+ // Start a timer that periodically update the list of sharable windows.
+ refresh_timer_.Start(FROM_HERE,
+ kThumbnailCapturerMacRefreshTimerInterval.Get(), this,
+ &ThumbnailCapturerMac::UpdateWindowsList);
+
+ if (capture_mode_ == CaptureMode::kSCScreenshotManager) {
+ CHECK(!screenshot_manager_capturer_);
+ // Unretained is safe because `screenshot_manager_capturer_ ` is owned by
+ // `this`, and hence has a shorter lifetime than `this`.
+ screenshot_manager_capturer_ = std::make_unique<ScreenshotManagerCapturer>(
+ max_frame_rate_,
+ base::BindRepeating(&ThumbnailCapturerMac::GetShareableWindow,
+ base::Unretained(this)),
+ base::BindRepeating(&ThumbnailCapturerMac::OnCapturedFrame,
+ weak_factory_.GetWeakPtr()));
+ }
+}
+
+void ThumbnailCapturerMac::SetMaxFrameRate(uint32_t max_frame_rate) {
+ max_frame_rate_ = max_frame_rate;
+}
+
+bool ThumbnailCapturerMac::GetSourceList(SourceList* sources) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ sources->clear();
+
+ // Discover how many windows are associated with each application,
+ // so as to use this as part of the set of conditions for which
+ // windows are valid sources.
+ std::unordered_map<pid_t, size_t> application_to_window_count;
+ for (SCWindow* window in shareable_windows_) {
+ const pid_t pid = window.owningApplication.processID;
+ if (!base::Contains(application_to_window_count, pid)) {
+ application_to_window_count[pid] = 1;
+ } else {
+ ++application_to_window_count[pid];
+ }
+ }
+
+ // Add relevant sources.
+ for (SCWindow* window in shareable_windows_) {
+ // Skip windows with empty titles, unless they are their app's only window
+ // or fullscreen.
+ const pid_t pid = window.owningApplication.processID;
+ bool is_title_empty = [window.title length] <= 0;
+ if (is_title_empty && application_to_window_count.at(pid) > 1 &&
+ !IsWindowFullscreen(window, shareable_displays_)) {
+ continue;
+ }
+
+ sources->push_back(ThumbnailCapturer::Source{
+ window.windowID,
+ base::SysNSStringToUTF8(is_title_empty
+ ? window.owningApplication.applicationName
+ : window.title)});
+ }
+
+ return true;
+}
+
+void ThumbnailCapturerMac::UpdateWindowsList() {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ auto content_callback = base::BindPostTask(
+ task_runner_,
+ base::BindRepeating(&ThumbnailCapturerMac::OnRecurrentShareableContent,
+ weak_factory_.GetWeakPtr()));
+
+ auto handler = ^(SCShareableContent* content, NSError* error) {
+ content_callback.Run(content);
+ };
+
+ // Exclude desktop windows (e.g., background image and deskktop icons) and
+ // windows that are not on screen (e.g., minimized and behind fullscreen
+ // windows).
+ [SCShareableContent getShareableContentExcludingDesktopWindows:true
+ onScreenWindowsOnly:true
+ completionHandler:handler];
+}
+
+void ThumbnailCapturerMac::OnRecurrentShareableContent(
+ SCShareableContent* content) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ if (!content) {
+ return;
+ }
+
+ shareable_displays_ = [content displays];
+ UpdateShareableWindows([content windows]);
+
+ // TODO(https://crbug.com/1471931): Only call update if the list is changed:
+ // windows opened/closed, order of the list, and title.
+ consumer_->OnSourceListUpdated();
+}
+
+void ThumbnailCapturerMac::UpdateShareableWindows(
+ NSArray<SCWindow*>* content_windows) {
+ // Narrow down the list to shareable windows.
+ content_windows = FilterOutUnshareable(content_windows);
+
+ // Update shareable_streams_ from current_windows.
+ switch (sort_mode_) {
+ case SortMode::kNone:
+ shareable_windows_ = content_windows;
+ break;
+ case SortMode::kCGWindowList:
+ shareable_windows_ = SortOrderByCGWindowList(content_windows);
+ break;
+ case SortMode::kNewWindowsLast:
+ shareable_windows_ = SortOrderByNewWindowsLast(content_windows);
+ break;
+ }
+
+ RemoveInactiveStreams();
+}
+
+SCWindow* ThumbnailCapturerMac::GetShareableWindow(SourceId source_id) const {
+ return FindWindow(shareable_windows_, source_id);
+}
+
+NSArray<SCWindow*>* ThumbnailCapturerMac::SortOrderByCGWindowList(
+ NSArray<SCWindow*>* current_windows) const {
+ CHECK_EQ(sort_mode_, SortMode::kCGWindowList);
+
+ // Only get on screen, non-desktop windows.
+ // According to
+ // https://developer.apple.com/documentation/coregraphics/cgwindowlistoption/1454105-optiononscreenonly
+ // when kCGWindowListOptionOnScreenOnly is used, the order of windows are
+ // in decreasing z-order.
+ base::apple::ScopedCFTypeRef<CFArrayRef> window_array(
+ CGWindowListCopyWindowInfo(
+ kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
+ kCGNullWindowID));
+ if (!window_array) {
+ DVLOG(2) << "Cannot sort list, nothing returned from "
+ "CGWindowListCopyWindowInfo.";
+ return current_windows;
+ }
+
+ // Sort `current_windows` to match the order returned
+ // by CGWindowListCopyWindowInfo.
+ // The windowID is the key matching entries in these containers.
+ NSMutableArray<SCWindow*>* sorted_windows = [[NSMutableArray alloc] init];
+ CFIndex count = CFArrayGetCount(window_array.get());
+ for (CFIndex i = 0; i < count; i++) {
+ CGWindowID window_id = GetWindowId(window_array.get(), i);
+ SCWindow* window = FindWindow(current_windows, window_id);
+ if (window) {
+ [sorted_windows addObject:window];
+ }
+ }
+ return sorted_windows;
+}
+
+NSArray<SCWindow*>* ThumbnailCapturerMac::SortOrderByNewWindowsLast(
+ NSArray<SCWindow*>* current_windows) const {
+ CHECK_EQ(sort_mode_, SortMode::kNewWindowsLast);
+
+ // Prepare to segment the list of new window as pre-existing / newly-added.
+ NSMutableArray<SCWindow*>* existing_windows = [[NSMutableArray alloc] init];
+ NSMutableArray<SCWindow*>* added_windows = [[NSMutableArray alloc] init];
+
+ // Iterate over the windows from last time and ensure that all of them
+ // which are still relevant, are maintained in their original order.
+ for (SCWindow* window in shareable_windows_) {
+ SCWindow* current_window = FindWindow(current_windows, window.windowID);
+ if (current_window) {
+ // Please note that current_window may not be equal to the previous window
+ // despite that they have the same WindowID if for example the title has
+ // changed.
+ [existing_windows addObject:current_window];
+ }
+ }
+
+ // All other windows in `current_windows` are new by definition.
+ for (SCWindow* window in current_windows) {
+ if (!FindWindow(existing_windows, window.windowID)) {
+ [added_windows addObject:window];
+ }
+ }
+
+ return [existing_windows arrayByAddingObjectsFromArray:added_windows];
+}
+
+bool ThumbnailCapturerMac::IsShareable(SCWindow* window) const {
+ // Always exclude windows from the source list based on the following
+ // conditions:
+ // 1. Exclude windows with layer!=0 (menu, dock).
+ // 2. Exclude small windows with either height or width less than the minimum.
+ // Such windows are generally of no interest to the user, cluttering the
+ // thumbnail picker and serving only as a footgun for the user.
+ // For example, on macOS 14, each window that is captured has an indicator
+ // that the window is being captured. This indicator is a window itself,
+ // but is of no use for the user.
+ return window.windowLayer == 0 &&
+ window.frame.size.height >= minimum_window_size_ &&
+ window.frame.size.width >= minimum_window_size_;
+}
+
+NSArray<SCWindow*>* ThumbnailCapturerMac::FilterOutUnshareable(
+ NSArray<SCWindow*>* windows) {
+ NSMutableArray<SCWindow*>* result = [[NSMutableArray<SCWindow*> alloc] init];
+ for (SCWindow* window in windows) {
+ if (IsShareable(window)) {
+ [result addObject:window];
+ }
+ }
+ return result;
+}
+
+void ThumbnailCapturerMac::RemoveInactiveStreams() {
+ // Remove all streams for windows that are not active anymore. New streams are
+ // created once the consumer calls SelectSources().
+ for (auto it = streams_.begin(); it != streams_.end();) {
+ if (!FindWindow(shareable_windows_, it->first)) {
+ it = streams_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+void ThumbnailCapturerMac::SelectSources(const std::vector<SourceId>& ids,
+ gfx::Size thumbnail_size) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ if (capture_mode_ == CaptureMode::kSCScreenshotManager) {
+ CHECK(screenshot_manager_capturer_);
+ screenshot_manager_capturer_->SelectSources(ids, thumbnail_size);
+ return;
+ }
+
+ // Create SCStreamConfiguration.
+ SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init];
+ config.width = thumbnail_size.width();
+ config.height = thumbnail_size.height();
+ config.scalesToFit = YES;
+ config.showsCursor = NO;
+ config.minimumFrameInterval = CMTimeMake(
+ media::kFrameRatePrecision,
+ static_cast<int>(max_frame_rate_ * media::kFrameRatePrecision));
+
+ SampleCallback sample_callback = base::BindPostTask(
+ task_runner_, base::BindRepeating(&ThumbnailCapturerMac::OnCapturedFrame,
+ weak_factory_.GetWeakPtr()));
+ ErrorCallback error_callback = base::DoNothing();
+
+ // Create a stream for any source that doesn't have a stream.
+ for (SourceId id : ids) {
+ if (streams_.find(id) != streams_.end()) {
+ continue;
+ }
+ // Find the corresponding SCWindow in the list.
+ SCWindow* selected_window = nil;
+ for (SCWindow* window in shareable_windows_) {
+ if (window.windowID == id) {
+ selected_window = window;
+ break;
+ }
+ }
+
+ if (!selected_window) {
+ continue;
+ }
+ SCContentFilter* filter = [[SCContentFilter alloc]
+ initWithDesktopIndependentWindow:selected_window];
+
+ SCKStreamDelegateAndOutput* delegate = [[SCKStreamDelegateAndOutput alloc]
+ initWithSampleCallback:sample_callback
+ errorCallback:error_callback
+ sourceId:selected_window.windowID];
+
+ // Create and start stream.
+ SCStream* stream = [[SCStream alloc] initWithFilter:filter
+ configuration:config
+ delegate:delegate];
+
+ NSError* error = nil;
+ bool add_stream_output_result =
+ [stream addStreamOutput:delegate
+ type:SCStreamOutputTypeScreen
+ sampleHandlerQueue:dispatch_get_main_queue()
+ error:&error];
+ if (error || !add_stream_output_result) {
+ DVLOG(2) << "Something went wrong while adding stream output";
+ continue;
+ }
+
+ auto handler = ^(NSError* e) {
+ if (e) {
+ DVLOG(2) << "Error while starting the capturer.";
+ }
+ };
+ [stream startCaptureWithCompletionHandler:handler];
+
+ // Keep track of the stream and delegate.
+ streams_[id] = StreamAndDelegate{stream, delegate};
+ }
+
+ // Remove any stream that is not in the list of selected sources anymore.
+ for (auto it = streams_.begin(); it != streams_.end();) {
+ if (base::Contains(ids, it->first)) {
+ ++it;
+ } else {
+ it = streams_.erase(it);
+ }
+ }
+}
+
+void ThumbnailCapturerMac::OnCapturedFrame(
+ base::apple::ScopedCFTypeRef<CGImageRef> cg_image,
+ ThumbnailCapturer::SourceId source_id) {
+ if (!cg_image) {
+ return;
+ }
+
+ // The image has been captured, pass it on to the consumer as a DesktopFrame.
+ std::unique_ptr<webrtc::DesktopFrame> frame =
+ webrtc::CreateDesktopFrameFromCGImage(rtc::AdoptCF(cg_image.get()));
+ consumer_->OnRecurrentCaptureResult(Result::SUCCESS, std::move(frame),
+ source_id);
+}
+
+} // namespace
+
+bool ShouldUseThumbnailCapturerMac() {
+ if (@available(macOS 14.0, *)) {
+ return base::FeatureList::IsEnabled(kScreenCaptureKitStreamPickerSonoma);
+ }
+ if (@available(macOS 13.2, *)) {
+ return base::FeatureList::IsEnabled(kScreenCaptureKitStreamPickerVentura);
+ }
+ return false;
+}
+
+// Creates a ThumbnailCapturerMac object. Must only be called is
+// ShouldUseThumbnailCapturerMac() returns true.
+std::unique_ptr<ThumbnailCapturer> CreateThumbnailCapturerMac() {
+ CHECK(ShouldUseThumbnailCapturerMac());
+ if (@available(macOS 13.2, *)) {
+ return std::make_unique<ThumbnailCapturerMac>();
+ }
+ NOTREACHED_NORETURN();
+}
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_browsertest_base.cc b/chromium/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
index 5cb85303627..8d9b09fe29c 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
@@ -90,8 +90,8 @@ bool JavascriptErrorDetectingLogHandler(int severity,
return false;
bool contains_uncaught = str.find("\"Uncaught ") != std::string::npos;
- if (severity == logging::LOG_ERROR ||
- (severity == logging::LOG_INFO && contains_uncaught)) {
+ if (severity == logging::LOGGING_ERROR ||
+ (severity == logging::LOGGING_INFO && contains_uncaught)) {
hit_javascript_errors_.Get() = true;
}
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_desktop_capture_browsertest.cc b/chromium/chrome/browser/media/webrtc/webrtc_desktop_capture_browsertest.cc
index 8a4962a3aa4..0d4b76951ae 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_desktop_capture_browsertest.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_desktop_capture_browsertest.cc
@@ -307,8 +307,14 @@ class WebRtcDesktopCaptureBrowserTest : public WebRtcTestBase {
FakeDesktopMediaPickerFactory picker_factory_;
};
+// TODO(crbug.com/1449889): Fails on MAC.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_TabCaptureProvidesMinFps DISABLED_TabCaptureProvidesMinFps
+#else
+#define MAYBE_TabCaptureProvidesMinFps TabCaptureProvidesMinFps
+#endif
IN_PROC_BROWSER_TEST_F(WebRtcDesktopCaptureBrowserTest,
- TabCaptureProvidesMinFps) {
+ MAYBE_TabCaptureProvidesMinFps) {
constexpr int kFps = 30;
constexpr const char* const kFpsString = "30";
constexpr int kTestTimeSeconds = 2;
@@ -401,7 +407,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcDesktopCaptureBrowserTest,
}
// TODO(crbug.com/1450456) flaky on ASan bots
-#if defined(ADDRESS_SANITIZER)
+#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
#define MAYBE_RunP2PScreenshareWhileSharingTab \
DISABLED_RunP2PScreenshareWhileSharingTab
#else
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc
index 591f814d434..d58d6c92a2d 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc
@@ -4,13 +4,13 @@
#include "chrome/browser/media/webrtc/webrtc_event_log_manager_common.h"
-#include <cctype>
#include <limits>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
+#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
@@ -23,6 +23,7 @@
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
+#include "third_party/abseil-cpp/absl/strings/ascii.h"
#include "third_party/zlib/zlib.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -738,10 +739,8 @@ size_t ExtractWebAppId(base::StringPiece str) {
DCHECK_EQ(str.length(), kWebAppIdLength);
// Avoid leading '+', etc.
- for (size_t i = 0; i < str.length(); i++) {
- if (!std::isdigit(str[i])) {
- return kInvalidWebRtcEventLogWebAppId;
- }
+ if (!base::ranges::all_of(str, absl::ascii_isdigit)) {
+ return kInvalidWebRtcEventLogWebAppId;
}
size_t result;
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common_unittest.cc b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common_unittest.cc
index 3512bffb571..eba1fc101c1 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_common_unittest.cc
@@ -25,6 +25,7 @@
#include "chrome/test/base/testing_profile.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/scoped_user_manager.h"
+#include "components/user_manager/user_names.h"
#include "content/public/test/browser_task_environment.h"
#endif
@@ -692,7 +693,7 @@ TEST_P(DoesProfileDefaultToLoggingEnabledForUserTypeParametrizedTest,
account_id, false, test_case.user_type, testing_profile.get());
break;
case user_manager::USER_TYPE_GUEST:
- account_id = fake_user_manager_->GetGuestAccountId();
+ account_id = user_manager::GuestAccountId();
fake_user_manager_->AddGuestUser();
break;
case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
@@ -707,12 +708,6 @@ TEST_P(DoesProfileDefaultToLoggingEnabledForUserTypeParametrizedTest,
case user_manager::USER_TYPE_ARC_KIOSK_APP:
fake_user_manager_->AddArcKioskAppUser(account_id);
break;
- case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
- account_id =
- AccountId::AdFromUserEmailObjGuid(account_id.GetUserEmail(), "guid");
- fake_user_manager_->AddUserWithAffiliationAndTypeAndProfile(
- account_id, false, test_case.user_type, testing_profile.get());
- break;
default:
FAIL() << "Invalid test setup. Unexpected user type.";
}
@@ -737,7 +732,7 @@ INSTANTIATE_TEST_SUITE_P(
{user_manager::USER_TYPE_KIOSK_APP, false},
{user_manager::USER_TYPE_CHILD, false},
{user_manager::USER_TYPE_ARC_KIOSK_APP, false},
- {user_manager::USER_TYPE_ACTIVE_DIRECTORY, false}}));
+ }));
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.cc b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.cc
index 566f7c5e0ea..cd3dee431c1 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.cc
@@ -36,10 +36,11 @@ bool WebRtcEventLogManagerKeyedServiceFactory::
return true;
}
-KeyedService* WebRtcEventLogManagerKeyedServiceFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+ WebRtcEventLogManagerKeyedServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
DCHECK(!context->IsOffTheRecord());
- return new WebRtcEventLogManagerKeyedService(context);
+ return std::make_unique<WebRtcEventLogManagerKeyedService>(context);
}
} // namespace webrtc_event_logging
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.h b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.h
index baf21236db6..62c9cda9fca 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.h
+++ b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_keyed_service_factory.h
@@ -36,7 +36,7 @@ class WebRtcEventLogManagerKeyedServiceFactory
WebRtcEventLogManagerKeyedServiceFactory();
~WebRtcEventLogManagerKeyedServiceFactory() override;
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
index 0ea284f5c5d..877c6b51bfe 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
@@ -3894,6 +3894,10 @@ TEST_F(WebRtcEventLogManagerTestCacheClearing,
ClearCacheForBrowserContext(browser_context_.get(), base::Time::Min(),
base::Time::Max());
WaitForPendingTasks(&run_loop);
+
+ // Restore factory before `run_loop` goes out of scope.
+ SetWebRtcEventLogUploaderFactoryForTesting(
+ std::make_unique<NullWebRtcEventLogUploader::Factory>(true));
}
TEST_P(WebRtcEventLogManagerTestWithRemoteLoggingDisabled,
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc b/chromium/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
index db25787b88a..7d13a66c076 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
@@ -53,8 +53,8 @@
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager_test_helper.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h"
+#include "chrome/browser/chromeos/policy/dlp/test/dlp_content_manager_test_helper.h"
#endif
namespace {
@@ -864,7 +864,7 @@ class GetDisplayMediaVideoTrackBrowserTest
const DisplaySurfaceType display_surface_type_;
private:
- raw_ptr<content::WebContents, DanglingUntriaged> tab_ = nullptr;
+ raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> tab_ = nullptr;
};
INSTANTIATE_TEST_SUITE_P(
@@ -1002,7 +1002,7 @@ class GetDisplayMediaHiDpiBrowserTest
base::test::ScopedFeatureList feature_list_;
const TestConfigForHiDpi test_config_;
- raw_ptr<content::WebContents, DanglingUntriaged> tab_ = nullptr;
+ raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> tab_ = nullptr;
};
IN_PROC_BROWSER_TEST_P(GetDisplayMediaHiDpiBrowserTest, Capture) {
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_interactive_uitest.cc b/chromium/chrome/browser/media/webrtc/webrtc_interactive_uitest.cc
index 0367caa645a..76886495372 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_interactive_uitest.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_interactive_uitest.cc
@@ -158,8 +158,8 @@ class WebRtcBrowserTest : public WebRtcTestBase {
HangUp(right_tab_);
}
- raw_ptr<content::WebContents, DanglingUntriaged> left_tab_;
- raw_ptr<content::WebContents, DanglingUntriaged> right_tab_;
+ raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> left_tab_;
+ raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> right_tab_;
};
IN_PROC_BROWSER_TEST_F(WebRtcBrowserTest,
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_mediadevices_interactive_uitest.cc b/chromium/chrome/browser/media/webrtc/webrtc_mediadevices_interactive_uitest.cc
index 319a65085dd..770a73d96e9 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_mediadevices_interactive_uitest.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_mediadevices_interactive_uitest.cc
@@ -7,6 +7,7 @@
#include "base/json/json_reader.h"
#include "base/strings/string_util.h"
#include "base/test/bind.h"
+#include "base/test/gmock_expected_support.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
@@ -20,6 +21,7 @@
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/core/browser/cookie_settings.h"
+#include "components/media_device_salt/media_device_salt_service.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/test/permission_request_observer.h"
#include "content/public/browser/browser_context.h"
@@ -42,6 +44,7 @@
namespace {
const char kMainWebrtcTestHtmlPage[] = "/webrtc/webrtc_jsep01_test.html";
+const char kClearCookiesPage[] = "/clear_cookies";
const char kDeviceKindAudioInput[] = "audioinput";
const char kDeviceKindVideoInput[] = "videoinput";
@@ -60,10 +63,22 @@ class WebRtcMediaDevicesInteractiveUITest
WebRtcMediaDevicesInteractiveUITest()
: has_audio_output_devices_initialized_(false),
has_audio_output_devices_(false) {
- scoped_feature_list_.InitAndEnableFeature(
- features::kUserMediaCaptureOnFocus);
+ if (IsMediaDeviceIdRandomSaltsPerStorageKeyEnabled()) {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kUserMediaCaptureOnFocus,
+ media_device_salt::kMediaDeviceIdPartitioning,
+ media_device_salt::kMediaDeviceIdRandomSaltsPerStorageKey},
+ {});
+ } else {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kUserMediaCaptureOnFocus},
+ {media_device_salt::kMediaDeviceIdPartitioning,
+ media_device_salt::kMediaDeviceIdRandomSaltsPerStorageKey});
+ }
}
+ bool IsMediaDeviceIdRandomSaltsPerStorageKeyEnabled() { return GetParam(); }
+
void SetUpInProcessBrowserTestFixture() override {
DetectErrorsInJavaScript(); // Look for errors in our rather complex js.
}
@@ -87,15 +102,16 @@ class WebRtcMediaDevicesInteractiveUITest
std::string devices_as_json = ExecuteJavascript("enumerateDevices()", tab);
EXPECT_FALSE(devices_as_json.empty());
- auto parsed_json = base::JSONReader::ReadAndReturnValueWithError(
- devices_as_json, base::JSON_ALLOW_TRAILING_COMMAS);
- ASSERT_TRUE(parsed_json.has_value()) << parsed_json.error().message;
- ASSERT_TRUE(parsed_json->is_list());
- ASSERT_FALSE(parsed_json->GetList().empty());
+ ASSERT_OK_AND_ASSIGN(
+ auto parsed_json,
+ base::JSONReader::ReadAndReturnValueWithError(
+ devices_as_json, base::JSON_ALLOW_TRAILING_COMMAS));
+ ASSERT_TRUE(parsed_json.is_list());
+ ASSERT_FALSE(parsed_json.GetList().empty());
bool found_audio_input = false;
bool found_video_input = false;
- for (const auto& value : parsed_json->GetList()) {
+ for (const auto& value : parsed_json.GetList()) {
const base::Value::Dict* dict = value.GetIfDict();
ASSERT_TRUE(dict);
MediaDeviceInfo device;
@@ -162,7 +178,7 @@ class WebRtcMediaDevicesInteractiveUITest
base::test::ScopedFeatureList scoped_feature_list_;
};
-IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesInteractiveUITest,
EnumerateDevicesWithoutAccess) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -181,7 +197,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
}
}
-IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesInteractiveUITest,
EnumerateDevicesWithAccess) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -203,7 +219,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
}
}
-IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesInteractiveUITest,
GetUserMediaOnUnFocusedTab) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -226,7 +242,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
#else
#define MAYBE_GetUserMediaTabRegainsFocus GetUserMediaTabRegainsFocus
#endif
-IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesInteractiveUITest,
MAYBE_GetUserMediaTabRegainsFocus) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -242,7 +258,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
tab, kAudioVideoCallConstraints));
}
-IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesInteractiveUITest,
DeviceIdSameGroupIdDiffersAcrossTabs) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -273,7 +289,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
}
}
-IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesInteractiveUITest,
DeviceIdDiffersAfterClearingCookies) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -302,7 +318,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
CheckEnumerationsAreDifferent(devices, devices2);
}
-IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesInteractiveUITest,
DeviceIdDiffersAcrossTabsWithCookiesDisabled) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -331,7 +347,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
CheckEnumerationsAreDifferent(devices, devices2);
}
-IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesInteractiveUITest,
DeviceIdDiffersSameTabAfterReloadWithCookiesDisabled) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -357,6 +373,54 @@ IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesInteractiveUITest,
CheckEnumerationsAreDifferent(devices, devices2);
}
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesInteractiveUITest,
+ DeviceIdDiffersAfterClearSiteDataHeader) {
+ if (!IsMediaDeviceIdRandomSaltsPerStorageKeyEnabled()) {
+ return;
+ }
+ embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
+ [](const net::test_server::HttpRequest& request)
+ -> std::unique_ptr<net::test_server::HttpResponse> {
+ if (request.GetURL().path() == kClearCookiesPage) {
+ auto response =
+ std::make_unique<net::test_server::BasicHttpResponse>();
+ response->AddCustomHeader("Clear-Site-Data", "\"cookies\"");
+ response->set_code(net::HTTP_OK);
+ response->set_content_type("text/html");
+ response->set_content(std::string());
+ return response;
+ }
+
+ // Use the default handler for other requests.
+ return nullptr;
+ }));
+
+ ASSERT_TRUE(embedded_test_server()->Start());
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage)));
+ content::WebContents* tab =
+ browser()->tab_strip_model()->GetActiveWebContents();
+
+ EXPECT_TRUE(GetUserMediaAndAccept(tab));
+ std::vector<MediaDeviceInfo> devices;
+ EnumerateDevices(tab, &devices);
+
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL(kClearCookiesPage)));
+
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage)));
+
+ std::vector<MediaDeviceInfo> devices2;
+ EnumerateDevices(tab, &devices2);
+ EXPECT_EQ(devices.size(), devices2.size());
+ CheckEnumerationsAreDifferent(devices, devices2);
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+ WebRtcMediaDevicesInteractiveUITest,
+ testing::Bool());
+
class WebRtcMediaDevicesPrerenderingBrowserTest
: public WebRtcMediaDevicesInteractiveUITest {
public:
@@ -378,7 +442,7 @@ class WebRtcMediaDevicesPrerenderingBrowserTest
content::test::PrerenderTestHelper prerender_helper_;
};
-IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesPrerenderingBrowserTest,
+IN_PROC_BROWSER_TEST_P(WebRtcMediaDevicesPrerenderingBrowserTest,
EnumerateDevicesInPrerendering) {
#if BUILDFLAG(IS_MAC)
// Test will fail if the window it's running in contains the mouse pointer.
@@ -419,3 +483,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcMediaDevicesPrerenderingBrowserTest,
// The prerendered page should show the permission request's bubble UI.
EXPECT_TRUE(observer.request_shown());
}
+
+INSTANTIATE_TEST_SUITE_P(All,
+ WebRtcMediaDevicesPrerenderingBrowserTest,
+ testing::Bool());
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc b/chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
index 6c9748ac81d..cd9bd09c564 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
@@ -483,7 +483,7 @@ void WebRtcTextLogHandler::OnGetNetworkInterfaceListFinish(
// Computer model
std::string computer_model = "Not available";
#if BUILDFLAG(IS_MAC)
- computer_model = base::mac::GetModelIdentifier();
+ computer_model = base::SysInfo::HardwareModelName();
#elif BUILDFLAG(IS_CHROMEOS_ASH)
if (const absl::optional<base::StringPiece> computer_model_statistic =
ash::system::StatisticsProvider::GetInstance()->GetMachineStatistic(
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc b/chromium/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc
index d95e91c89e0..14184668303 100644
--- a/chromium/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc
+++ b/chromium/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc
@@ -317,7 +317,8 @@ class WebRtcVideoQualityBrowserTest : public WebRtcTestBase,
base::FilePath GetTestBinaryDir() {
base::FilePath browser_dir;
- EXPECT_TRUE(base::PathService::Get(base::DIR_ASSETS, &browser_dir));
+ EXPECT_TRUE(
+ base::PathService::Get(base::DIR_OUT_TEST_DATA_ROOT, &browser_dir));
return browser_dir;
}
diff --git a/chromium/chrome/browser/media/webrtc/window_icon_util_mac.mm b/chromium/chrome/browser/media/webrtc/window_icon_util_mac.mm
index b075efd130f..dc28e45acab 100644
--- a/chromium/chrome/browser/media/webrtc/window_icon_util_mac.mm
+++ b/chromium/chrome/browser/media/webrtc/window_icon_util_mac.mm
@@ -6,32 +6,28 @@
#import <Cocoa/Cocoa.h>
-#include "base/mac/foundation_util.h"
-#include "base/mac/scoped_cftyperef.h"
+#include "base/apple/foundation_util.h"
+#include "base/apple/scoped_cftyperef.h"
#include "third_party/libyuv/include/libyuv/convert_argb.h"
#include "third_party/skia/include/core/SkBitmap.h"
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
gfx::ImageSkia GetWindowIcon(content::DesktopMediaID id) {
DCHECK(id.type == content::DesktopMediaID::TYPE_WINDOW);
CGWindowID ids[1];
ids[0] = id.id;
- base::ScopedCFTypeRef<CFArrayRef> window_id_array(CFArrayCreate(
+ base::apple::ScopedCFTypeRef<CFArrayRef> window_id_array(CFArrayCreate(
nullptr, reinterpret_cast<const void**>(&ids), std::size(ids), nullptr));
- base::ScopedCFTypeRef<CFArrayRef> window_array(
+ base::apple::ScopedCFTypeRef<CFArrayRef> window_array(
CGWindowListCreateDescriptionFromArray(window_id_array));
if (!window_array || 0 == CFArrayGetCount(window_array)) {
return gfx::ImageSkia();
}
- CFDictionaryRef window = base::mac::CFCastStrict<CFDictionaryRef>(
+ CFDictionaryRef window = base::apple::CFCastStrict<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, 0));
- CFNumberRef pid_ref =
- base::mac::GetValueFromDictionary<CFNumberRef>(window, kCGWindowOwnerPID);
+ CFNumberRef pid_ref = base::apple::GetValueFromDictionary<CFNumberRef>(
+ window, kCGWindowOwnerPID);
int pid;
CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
@@ -63,7 +59,8 @@ gfx::ImageSkia GetWindowIcon(content::DesktopMediaID id) {
}
CGDataProviderRef provider = CGImageGetDataProvider(cg_icon_image);
- base::ScopedCFTypeRef<CFDataRef> cf_data(CGDataProviderCopyData(provider));
+ base::apple::ScopedCFTypeRef<CFDataRef> cf_data(
+ CGDataProviderCopyData(provider));
int width = CGImageGetWidth(cg_icon_image);
int height = CGImageGetHeight(cg_icon_image);
diff --git a/chromium/chrome/browser/metrics/variations/BUILD.gn b/chromium/chrome/browser/metrics/variations/BUILD.gn
index 4938873302d..c3d4900aa93 100644
--- a/chromium/chrome/browser/metrics/variations/BUILD.gn
+++ b/chromium/chrome/browser/metrics/variations/BUILD.gn
@@ -8,17 +8,13 @@ generate_ui_string_overrider("chrome_ui_string_overrider_factory") {
inputs = [
"$root_gen_dir/chrome/grit/chromium_strings.h",
"$root_gen_dir/chrome/grit/generated_resources.h",
- "$root_gen_dir/chrome/grit/google_chrome_strings.h",
"$root_gen_dir/components/strings/grit/components_chromium_strings.h",
- "$root_gen_dir/components/strings/grit/components_google_chrome_strings.h",
"$root_gen_dir/components/strings/grit/components_strings.h",
]
deps = [
"//chrome/app:chromium_strings",
"//chrome/app:generated_resources",
- "//chrome/app:google_chrome_strings",
"//components/strings:components_chromium_strings",
- "//components/strings:components_google_chrome_strings",
"//components/strings:components_strings",
"//components/variations:variations",
]
diff --git a/chromium/chrome/browser/nearby_sharing/certificates/BUILD.gn b/chromium/chrome/browser/nearby_sharing/certificates/BUILD.gn
index 9b01197c70e..716efec1bb3 100644
--- a/chromium/chrome/browser/nearby_sharing/certificates/BUILD.gn
+++ b/chromium/chrome/browser/nearby_sharing/certificates/BUILD.gn
@@ -38,6 +38,7 @@ source_set("certificates") {
"//chromeos/ash/components/nearby/common/proto",
"//chromeos/ash/components/nearby/common/scheduling",
"//chromeos/ash/services/nearby/public/mojom",
+ "//components/cross_device/logging:logging",
"//components/leveldb_proto",
"//components/prefs",
"//crypto",
diff --git a/chromium/chrome/browser/nearby_sharing/client/BUILD.gn b/chromium/chrome/browser/nearby_sharing/client/BUILD.gn
index ec19808ad9e..915faaaf5f1 100644
--- a/chromium/chrome/browser/nearby_sharing/client/BUILD.gn
+++ b/chromium/chrome/browser/nearby_sharing/client/BUILD.gn
@@ -19,6 +19,7 @@ source_set("client") {
"//chrome/browser/nearby_sharing/logging",
"//chrome/browser/nearby_sharing/proto",
"//chromeos/ash/components/nearby/common/client",
+ "//components/cross_device/logging:logging",
"//components/signin/public/identity_manager",
"//net",
"//net/traffic_annotation",
diff --git a/chromium/chrome/browser/nearby_sharing/contacts/BUILD.gn b/chromium/chrome/browser/nearby_sharing/contacts/BUILD.gn
index 3f798a35d68..4c388ff0767 100644
--- a/chromium/chrome/browser/nearby_sharing/contacts/BUILD.gn
+++ b/chromium/chrome/browser/nearby_sharing/contacts/BUILD.gn
@@ -28,6 +28,7 @@ source_set("contacts") {
"//chromeos/ash/components/nearby/common/client",
"//chromeos/ash/components/nearby/common/scheduling",
"//chromeos/ash/services/nearby/public/mojom",
+ "//components/cross_device/logging:logging",
"//components/prefs",
"//crypto",
]
diff --git a/chromium/chrome/browser/nearby_sharing/local_device_data/BUILD.gn b/chromium/chrome/browser/nearby_sharing/local_device_data/BUILD.gn
index 29167820975..394557c7fc6 100644
--- a/chromium/chrome/browser/nearby_sharing/local_device_data/BUILD.gn
+++ b/chromium/chrome/browser/nearby_sharing/local_device_data/BUILD.gn
@@ -28,6 +28,7 @@ source_set("local_device_data") {
"//chrome/browser/nearby_sharing/proto",
"//chromeos/ash/components/nearby/common/client",
"//chromeos/ash/components/nearby/common/scheduling",
+ "//components/cross_device/logging:logging",
"//components/prefs",
"//ui/base",
"//ui/chromeos",
diff --git a/chromium/chrome/browser/nearby_sharing/public/cpp/BUILD.gn b/chromium/chrome/browser/nearby_sharing/public/cpp/BUILD.gn
index acf1c757bd5..95262ca6366 100644
--- a/chromium/chrome/browser/nearby_sharing/public/cpp/BUILD.gn
+++ b/chromium/chrome/browser/nearby_sharing/public/cpp/BUILD.gn
@@ -6,6 +6,8 @@ assert(is_chromeos, "Nearby Share is CrOS only")
static_library("cpp") {
sources = [
+ "fake_nearby_connections_manager.cc",
+ "fake_nearby_connections_manager.h",
"nearby_connection.h",
"nearby_connections_manager.cc",
"nearby_connections_manager.h",
diff --git a/chromium/chrome/browser/net/OWNERS b/chromium/chrome/browser/net/OWNERS
index f341b880a0f..a5c1a4be265 100644
--- a/chromium/chrome/browser/net/OWNERS
+++ b/chromium/chrome/browser/net/OWNERS
@@ -1,5 +1,4 @@
file://net/OWNERS
-per-file android_network_service_browsertest.cc=wfh@chromium.org
per-file network_quality*=file://net/nqe/OWNERS
-per-file local_network_access_browsertest*=titouan@chromium.org
+per-file private_network_access_browsertest*=titouan@chromium.org
diff --git a/chromium/chrome/browser/net/android_network_service_browsertest.cc b/chromium/chrome/browser/net/android_network_service_browsertest.cc
deleted file mode 100644
index 46206923c6f..00000000000
--- a/chromium/chrome/browser/net/android_network_service_browsertest.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/functional/bind.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/test_file_util.h"
-#include "base/threading/scoped_blocking_call.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/android/android_browser_test.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "content/public/test/browser_test.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-
-namespace {
-
-class AndroidChromeNetworkContextCleanupBrowserTest
- : public AndroidBrowserTest,
- public testing::WithParamInterface<std::tuple<bool, bool>> {
- public:
- AndroidChromeNetworkContextCleanupBrowserTest() = default;
- ~AndroidChromeNetworkContextCleanupBrowserTest() override = default;
-
- protected:
- const base::FilePath& user_data_dir() const {
- return user_data_dir_.GetPath();
- }
-
- // Helper method to perform navigation. This makes sure the various network
- // contexts are initialized when it is time to check for test expectations.
- void Navigate() {
- ASSERT_TRUE(content::NavigateToURL(
- GetActiveWebContents(),
- embedded_test_server()->GetURL("/android/google.html")));
- }
-
- // Whether to create the `NetworkContextFilePaths.data_directory` before
- // initializing the network service.
- bool create_data_directory_;
-
- // Whether to write protect the `data_directory`. Used to check errors on
- // deletion.
- bool write_protect_data_directory_;
-
- // The `data_directory` prepared by the test.
- base::FilePath data_directory_;
-
- // A file in the stale network context directory. Should be wiped by
- // initialization.
- base::FilePath network_context_file_;
-
- // A file in the profile directory that should remain present after
- // initialization of the network service.
- base::FilePath profile_file_;
-
- // The histogram tester snapshots the state at construction time to avoid race
- // conditions. The network context initialization (being tested) starts
- // running during AndroidBrowserTest::SetUp().
- base::HistogramTester histogram_tester_;
-
- private:
- void SetUpOnMainThread() override {
- ASSERT_TRUE(embedded_test_server()->Start());
- PlatformBrowserTest::SetUpOnMainThread();
- }
-
- void SetUpCommandLine(base::CommandLine* command_line) override {
- // Create a new directory that serves as a user data dir. Enlist it as a
- // command line option. This way the SetUp() does not override it.
- ASSERT_TRUE(user_data_dir_.CreateUniqueTempDir());
- ASSERT_TRUE(base::IsDirectoryEmpty(user_data_dir()));
- command_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir());
-
- // Initialize the parameters.
- create_data_directory_ = std::get<0>(GetParam());
- write_protect_data_directory_ = std::get<1>(GetParam());
- data_directory_ = user_data_dir()
- .AppendASCII(chrome::kInitialProfile)
- .AppendASCII(chrome::kNetworkDataDirname);
-
- // Create the profile directory.
- base::FilePath profile_dir =
- user_data_dir().AppendASCII(chrome::kInitialProfile);
- ASSERT_TRUE(base::CreateDirectory(profile_dir));
-
- // Create a temporary file in the profile directory that should _not_ be
- // removed.
- ASSERT_TRUE(base::CreateTemporaryFileInDir(profile_dir, &profile_file_));
- ASSERT_TRUE(base::PathExists(profile_file_));
-
- if (create_data_directory_) {
- // Create a temporary file in the `data_directory` to check that it is
- // deleted recursively.
- ASSERT_TRUE(base::CreateDirectory(data_directory_));
- ASSERT_TRUE(base::CreateTemporaryFileInDir(data_directory_,
- &network_context_file_));
- ASSERT_TRUE(base::PathExists(network_context_file_));
- if (write_protect_data_directory_) {
- ASSERT_TRUE(base::MakeFileUnwritable(data_directory_));
- }
- }
- }
-
- content::WebContents* GetActiveWebContents() {
- return chrome_test_utils::GetActiveWebContents(this);
- }
-
- base::ScopedTempDir user_data_dir_;
-};
-
-constexpr char kClearHistogramName[] =
- "NetworkService.ClearStaleDataDirectoryResult";
-
-bool HasSample(const base::HistogramTester& histogram_tester,
- base::HistogramBase::Sample sample) {
- if (histogram_tester.GetBucketCount(kClearHistogramName, sample) > 0)
- return true;
- return false;
-}
-
-void QuitLoopIfHasSample(const base::HistogramTester& histogram_tester,
- base::HistogramBase::Sample expected_sample,
- base::RunLoop& run_loop,
- const char* histogram_name_ignored,
- uint64_t name_hash_ignored,
- base::HistogramBase::Sample arrived_sample_ignored) {
- if (HasSample(histogram_tester, expected_sample))
- run_loop.Quit();
-}
-
-void WaitForSample(const base::HistogramTester& histogram_tester,
- base::HistogramBase::Sample sample) {
- if (HasSample(histogram_tester, sample))
- return;
-
- base::RunLoop run_loop;
- base::StatisticsRecorder::ScopedHistogramSampleObserver observer(
- kClearHistogramName,
- base::BindRepeating(&QuitLoopIfHasSample, std::ref(histogram_tester),
- sample, std::ref(run_loop)));
-
- run_loop.Run();
-}
-
-} // namespace
-
-// Instantiate two tests: for the main network service context directory, and
-// for the SafeBrowsing one. Both of them check that the directory is deleted.
-INSTANTIATE_TEST_SUITE_P(
- WithDataDirectory,
- AndroidChromeNetworkContextCleanupBrowserTest,
- testing::Combine(
- /* create_data_directory_= */ testing::Values(true),
- /* write_protect_data_directory_= */ testing::Values(false)));
-
-// Check that when the directories are already cleaned up, the histogram with
-// the corresponding bucket is recorded.
-INSTANTIATE_TEST_SUITE_P(
- WithoutDataDirectory,
- AndroidChromeNetworkContextCleanupBrowserTest,
- testing::Combine(
- /* create_data_directory_= */ testing::Values(false),
- /* write_protect_data_directory_= */ testing::Values(false)));
-
-// Check that failure to delete a network context data directory is recorded in
-// UMA.
-INSTANTIATE_TEST_SUITE_P(
- WriteProtectedDataDirectory,
- AndroidChromeNetworkContextCleanupBrowserTest,
- testing::Combine(
- /* create_data_directory_= */ testing::Values(true),
- /* write_protect_data_directory_= */ testing::Values(true)));
-
-IN_PROC_BROWSER_TEST_P(AndroidChromeNetworkContextCleanupBrowserTest,
- NavigateAndCheck) {
- Navigate();
-
- // Enable blocking calls to inspect files on disk.
- base::ScopedAllowBlockingForTesting allow_blocking;
-
- // Wait for the histogram to be recorded. Two network contexts are not created
- // simultaneously by the test, hence after the histogram is recorded the
- // contents of the tested files on disk are _not_ going to change.
- if (create_data_directory_ && !write_protect_data_directory_) {
- // DeleteResult::kDeleted == 1.
- WaitForSample(histogram_tester_, base::HistogramBase::Sample(1));
- } else if (!create_data_directory_) {
- // DeleteResult::kNotFound == 0.
- WaitForSample(histogram_tester_, base::HistogramBase::Sample(0));
- } else if (write_protect_data_directory_) {
- // Wait for delete error. DeleteResult::kDeleteError == 2.
- WaitForSample(histogram_tester_, base::HistogramBase::Sample(2));
- }
-
- // Expect delete errors only when the `data_directory` is write protected.
- int delete_sample_count = write_protect_data_directory_ ? 1 : 0;
- histogram_tester_.ExpectBucketCount(
- kClearHistogramName, base::HistogramBase::Sample(2), delete_sample_count);
-
- // Expect that the file outside all of the network context directories remains
- // present.
- EXPECT_TRUE(base::PathExists(profile_file_));
-
- if (create_data_directory_) {
- if (write_protect_data_directory_) {
- EXPECT_TRUE(base::PathExists(network_context_file_));
-
- // Allow the test to clean up after itself.
- EXPECT_EQ(0, HANDLE_EINTR(chmod(data_directory_.value().c_str(),
- S_IWUSR | S_IWGRP | S_IWOTH)));
- } else {
- EXPECT_FALSE(base::PathExists(network_context_file_));
- }
- }
-}
diff --git a/chromium/chrome/browser/net/chrome_accept_encoding_header_browsertest.cc b/chromium/chrome/browser/net/chrome_accept_encoding_header_browsertest.cc
index b7746153941..c30e6770185 100644
--- a/chromium/chrome/browser/net/chrome_accept_encoding_header_browsertest.cc
+++ b/chromium/chrome/browser/net/chrome_accept_encoding_header_browsertest.cc
@@ -4,14 +4,25 @@
#include "base/run_loop.h"
#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "media/media_buildflags.h"
+#include "net/base/features.h"
#include "net/test/embedded_test_server/http_request.h"
-using ChromeAcceptEncodingHeaderTest = InProcessBrowserTest;
+class ChromeAcceptEncodingHeaderTest : public InProcessBrowserTest {
+ public:
+ ChromeAcceptEncodingHeaderTest() {
+ feature_list_.InitAndEnableFeature(net::features::kZstdContentEncoding);
+ }
+ ~ChromeAcceptEncodingHeaderTest() override = default;
+
+ private:
+ base::test::ScopedFeatureList feature_list_;
+};
IN_PROC_BROWSER_TEST_F(ChromeAcceptEncodingHeaderTest, Check) {
net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTPS);
@@ -40,8 +51,8 @@ IN_PROC_BROWSER_TEST_F(ChromeAcceptEncodingHeaderTest, Check) {
navigation_loop.Run();
subresource_loop.Run();
- ASSERT_EQ("gzip, deflate, br", navigation_accept_encoding_header);
- ASSERT_EQ("gzip, deflate, br", subresource_accept_encoding_header);
+ ASSERT_EQ("gzip, deflate, br, zstd", navigation_accept_encoding_header);
+ ASSERT_EQ("gzip, deflate, br, zstd", subresource_accept_encoding_header);
// Since the server uses local variables.
ASSERT_TRUE(server.ShutdownAndWaitUntilComplete());
diff --git a/chromium/chrome/browser/net/chrome_network_delegate.cc b/chromium/chrome/browser/net/chrome_network_delegate.cc
index 136bb81baa9..b77aa758c1b 100644
--- a/chromium/chrome/browser/net/chrome_network_delegate.cc
+++ b/chromium/chrome/browser/net/chrome_network_delegate.cc
@@ -42,8 +42,10 @@ bool IsPathOnAllowlist(const base::FilePath& path,
#if BUILDFLAG(IS_CHROMEOS)
bool IsLacrosLogFile(const base::FilePath& path) {
- return fnmatch("/home/chronos/user/lacros/lacros*.log", path.value().c_str(),
- FNM_NOESCAPE) == 0;
+ return (fnmatch("/home/chronos/user/lacros/lacros*.log", path.value().c_str(),
+ FNM_NOESCAPE) == 0) ||
+ (fnmatch("/var/log/lacros/lacros*.log", path.value().c_str(),
+ FNM_NOESCAPE) == 0);
}
// Returns true if access is allowed for |path| for a user with |profile_path).
diff --git a/chromium/chrome/browser/net/chrome_network_delegate_unittest.cc b/chromium/chrome/browser/net/chrome_network_delegate_unittest.cc
index 7a187899192..67dc6d20404 100644
--- a/chromium/chrome/browser/net/chrome_network_delegate_unittest.cc
+++ b/chromium/chrome/browser/net/chrome_network_delegate_unittest.cc
@@ -64,6 +64,7 @@ TEST(ChromeNetworkDelegateStaticTest, IsAccessAllowed) {
EXPECT_TRUE(IsAccessAllowed("/usr/share/chromeos-assets", ""));
EXPECT_TRUE(IsAccessAllowed(temp_dir.AsUTF8Unsafe(), ""));
EXPECT_TRUE(IsAccessAllowed("/var/log", ""));
+ EXPECT_TRUE(IsAccessAllowed("/var/log/lacros/lacros.log", ""));
// Files under the directories are allowed.
EXPECT_TRUE(IsAccessAllowed("/var/log/foo.txt", ""));
// Make sure similar paths are not allowed.
diff --git a/chromium/chrome/browser/net/chrome_network_service_browsertest.cc b/chromium/chrome/browser/net/chrome_network_service_browsertest.cc
index 724bdde76c4..2384eb7737a 100644
--- a/chromium/chrome/browser/net/chrome_network_service_browsertest.cc
+++ b/chromium/chrome/browser/net/chrome_network_service_browsertest.cc
@@ -19,6 +19,7 @@
#include "components/cookie_config/cookie_store_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/network_service_util.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
@@ -86,9 +87,11 @@ class ChromeNetworkServiceBrowserTest
ChromeNetworkServiceBrowserTest() {
bool in_process = GetParam();
// Verify that cookie encryption works both in-process and out of process.
- if (in_process)
- scoped_feature_list_.InitAndEnableFeature(
- features::kNetworkServiceInProcess);
+ if (in_process) {
+ content::ForceInProcessNetworkService();
+ } else {
+ content::ForceOutOfProcessNetworkService();
+ }
}
ChromeNetworkServiceBrowserTest(const ChromeNetworkServiceBrowserTest&) =
@@ -115,9 +118,6 @@ class ChromeNetworkServiceBrowserTest
std::move(context_params));
return network_context;
}
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_P(ChromeNetworkServiceBrowserTest, PRE_EncryptedCookies) {
diff --git a/chromium/chrome/browser/net/chrome_shared_dictionary_browsertest.cc b/chromium/chrome/browser/net/chrome_shared_dictionary_browsertest.cc
new file mode 100644
index 00000000000..7d1857fe56a
--- /dev/null
+++ b/chromium/chrome/browser/net/chrome_shared_dictionary_browsertest.cc
@@ -0,0 +1,659 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <utility>
+
+#include "base/functional/callback.h"
+#include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/browsing_data/counters/site_data_counting_helper.h"
+#include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/browsing_data/core/counters/browsing_data_counter.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/mojom/shared_dictionary_access_observer.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
+#include "url/url_constants.h"
+
+namespace {
+
+constexpr base::StringPiece kTestDictionaryString = "A dictionary";
+
+constexpr base::StringPiece kCompressedDataOriginalString =
+ "This is compressed test data using a test dictionary";
+
+// kBrotliCompressedData is generated using the following commands:
+// $ echo -n "A dictionary" > /tmp/dict
+// $ echo -n "This is compressed test data using a test dictionary" > /tmp/data
+// $ ./brotli -o /tmp/out.sbr -D /tmp/dict /tmp/data
+// $ xxd -i /tmp/out.sbr
+constexpr uint8_t kBrotliCompressedData[] = {
+ 0xa1, 0x98, 0x01, 0x80, 0x22, 0xe0, 0x26, 0x4b, 0x95, 0x5c, 0x19,
+ 0x18, 0x9d, 0xc1, 0xc3, 0x44, 0x0e, 0x5c, 0x6a, 0x09, 0x9d, 0xf0,
+ 0xb0, 0x01, 0x47, 0x14, 0x87, 0x14, 0x6d, 0xfb, 0x60, 0x96, 0xdb,
+ 0xae, 0x9e, 0x79, 0x54, 0xe3, 0x69, 0x03, 0x29};
+
+// NOLINTNEXTLINE(runtime/string)
+const std::string kBrotliCompressedDataString =
+ std::string(reinterpret_cast<const char*>(kBrotliCompressedData),
+ sizeof(kBrotliCompressedData));
+
+// kZstdCompressedData is generated using the following commands:
+// $ echo -n "A dictionary" > /tmp/dict
+// $ echo -n "This is compressed test data using a test dictionary" > /tmp/data
+// $ zstd -o /tmp/out.szstd -D /tmp/dict /tmp/data
+// $ xxd -i /tmp/out.szstd
+constexpr uint8_t kZstdCompressedData[] = {
+ 0x28, 0xb5, 0x2f, 0xfd, 0x24, 0x34, 0xa1, 0x01, 0x00, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72,
+ 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20,
+ 0x64, 0x61, 0x74, 0x61, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
+ 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x64, 0x69, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x61, 0x72, 0x79, 0x9e, 0x99, 0xf2, 0xbc};
+
+// NOLINTNEXTLINE(runtime/string)
+const std::string kZstdCompressedDataString =
+ std::string(reinterpret_cast<const char*>(kZstdCompressedData),
+ sizeof(kZstdCompressedData));
+
+class SharedDictionaryAccessObserver : public content::WebContentsObserver {
+ public:
+ SharedDictionaryAccessObserver(content::WebContents* web_contents,
+ base::RepeatingClosure on_accessed_callback)
+ : WebContentsObserver(web_contents),
+ on_accessed_callback_(std::move(on_accessed_callback)) {}
+
+ const network::mojom::SharedDictionaryAccessDetailsPtr& details() const {
+ return details_;
+ }
+
+ private:
+ // WebContentsObserver overrides:
+ void OnSharedDictionaryAccessed(
+ content::NavigationHandle* navigation,
+ const network::mojom::SharedDictionaryAccessDetails& details) override {
+ details_ = details.Clone();
+ on_accessed_callback_.Run();
+ }
+ void OnSharedDictionaryAccessed(
+ content::RenderFrameHost* rfh,
+ const network::mojom::SharedDictionaryAccessDetails& details) override {
+ details_ = details.Clone();
+ on_accessed_callback_.Run();
+ }
+
+ base::RepeatingClosure on_accessed_callback_;
+ network::mojom::SharedDictionaryAccessDetailsPtr details_;
+};
+
+absl::optional<std::string> GetSecAvailableDictionary(
+ const net::test_server::HttpRequest::HeaderMap& headers) {
+ auto it = headers.find("sec-available-dictionary");
+ if (it == headers.end()) {
+ return absl::nullopt;
+ }
+ return it->second;
+}
+
+void CheckSharedDictionaryUseCounter(
+ base::HistogramTester& histograms,
+ int expected_used_count_with_sbr,
+ int expected_used_count_with_zstd_d,
+ int expected_used_for_navigation_count,
+ int expected_used_for_main_frame_navigation_count,
+ int expected_used_for_sub_frame_navigation_count,
+ int expected_used_for_subresource_count) {
+ histograms.ExpectBucketCount(
+ "Blink.UseCounter.Features",
+ blink::mojom::WebFeature::kSharedDictionaryUsed,
+ expected_used_count_with_sbr + expected_used_count_with_zstd_d);
+ histograms.ExpectBucketCount(
+ "Blink.UseCounter.Features",
+ blink::mojom::WebFeature::kSharedDictionaryUsedForNavigation,
+ expected_used_for_navigation_count);
+ histograms.ExpectBucketCount(
+ "Blink.UseCounter.Features",
+ blink::mojom::WebFeature::kSharedDictionaryUsedForMainFrameNavigation,
+ expected_used_for_main_frame_navigation_count);
+ histograms.ExpectBucketCount(
+ "Blink.UseCounter.Features",
+ blink::mojom::WebFeature::kSharedDictionaryUsedForSubFrameNavigation,
+ expected_used_for_sub_frame_navigation_count);
+ histograms.ExpectBucketCount(
+ "Blink.UseCounter.Features",
+ blink::mojom::WebFeature::kSharedDictionaryUsedForSubresource,
+ expected_used_for_subresource_count);
+ histograms.ExpectBucketCount(
+ "Blink.UseCounter.Features",
+ blink::mojom::WebFeature::kSharedDictionaryUsedWithSharedBrotli,
+ expected_used_count_with_sbr);
+ histograms.ExpectBucketCount(
+ "Blink.UseCounter.Features",
+ blink::mojom::WebFeature::kSharedDictionaryUsedWithSharedZstd,
+ expected_used_count_with_zstd_d);
+}
+
+} // namespace
+
+// `ChromeSharedDictionaryBrowserTest` is required to test Chrome
+// specific code such as Site Settings.
+// See `SharedDictionaryBrowserTest` for content's version of tests.
+class ChromeSharedDictionaryBrowserTest : public InProcessBrowserTest {
+ public:
+ ChromeSharedDictionaryBrowserTest() {
+ scoped_feature_list_.InitWithFeatures(
+ /*enabled_features=*/
+ {network::features::kCompressionDictionaryTransportBackend,
+ network::features::kCompressionDictionaryTransport,
+ network::features::kSharedZstd},
+ /*disabled_features=*/{});
+
+ embedded_test_server()->RegisterRequestHandler(
+ base::BindRepeating(&ChromeSharedDictionaryBrowserTest::RequestHandler,
+ base::Unretained(this)));
+ EXPECT_TRUE(embedded_test_server()->Start());
+
+ cross_origin_server_ = std::make_unique<net::EmbeddedTestServer>();
+ cross_origin_server()->RegisterRequestHandler(
+ base::BindRepeating(&ChromeSharedDictionaryBrowserTest::RequestHandler,
+ base::Unretained(this)));
+ CHECK(cross_origin_server()->Start());
+ }
+
+ ChromeSharedDictionaryBrowserTest(const ChromeSharedDictionaryBrowserTest&) =
+ delete;
+ ChromeSharedDictionaryBrowserTest& operator=(
+ const ChromeSharedDictionaryBrowserTest&) = delete;
+
+ protected:
+ net::EmbeddedTestServer* cross_origin_server() {
+ return cross_origin_server_.get();
+ }
+
+ std::string FetchStringInActiveContents(const GURL& url) {
+ return EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+ content::JsReplace(R"(
+ (async () => {
+ return await (await fetch($1)).text();
+ })();
+ )",
+ url))
+ .ExtractString();
+ }
+
+ bool TryRegisterDictionary(const net::EmbeddedTestServer& server) {
+ base::RunLoop loop;
+ SharedDictionaryAccessObserver observer(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ loop.QuitClosure());
+
+ EXPECT_EQ(kTestDictionaryString,
+ FetchStringInActiveContents(server.GetURL("/dictionary")));
+ loop.Run();
+ CHECK(observer.details());
+ EXPECT_EQ(network::mojom::SharedDictionaryAccessDetails::Type::kWrite,
+ observer.details()->type);
+ return !observer.details()->is_blocked;
+ }
+
+ bool CheckDictionaryHeader(const net::EmbeddedTestServer& server,
+ bool expect_blocked) {
+ base::RunLoop loop;
+ SharedDictionaryAccessObserver observer(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ loop.QuitClosure());
+ return CheckResultOfDictionaryHeaderAndObserver(
+ FetchStringInActiveContents(server.GetURL("/path/check_header")), loop,
+ observer, expect_blocked);
+ }
+
+ bool CheckDictionaryHeaderByNavigation(const GURL& url, bool expect_blocked) {
+ base::RunLoop loop;
+ SharedDictionaryAccessObserver observer(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ loop.QuitClosure());
+
+ CHECK(ui_test_utils::NavigateToURL(browser(), url));
+
+ return CheckResultOfDictionaryHeaderAndObserver(
+ EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+ "document.body.innerText")
+ .ExtractString(),
+ loop, observer, expect_blocked);
+ }
+
+ bool CheckDictionaryHeaderByIframeNavigation(const GURL& url,
+ bool expect_blocked) {
+ base::RunLoop loop;
+ SharedDictionaryAccessObserver observer(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ loop.QuitClosure());
+
+ return CheckResultOfDictionaryHeaderAndObserver(
+ EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+ content::JsReplace(R"(
+ (async () => {
+ const iframe = document.createElement('iframe');
+ iframe.src = $1;
+ const promise =
+ new Promise(resolve => { iframe.addEventListener('load', resolve); });
+ document.body.appendChild(iframe);
+ await promise;
+ return iframe.contentDocument.body.innerText;
+ })()
+ )",
+ url))
+ .ExtractString(),
+ loop, observer, expect_blocked);
+ }
+
+ bool CheckResultOfDictionaryHeaderAndObserver(
+ const std::string& check_result,
+ base::RunLoop& loop,
+ SharedDictionaryAccessObserver& observer,
+ bool expect_blocked) {
+ if (check_result == "Dictionary header available") {
+ loop.Run();
+ CHECK(observer.details());
+ EXPECT_EQ(network::mojom::SharedDictionaryAccessDetails::Type::kRead,
+ observer.details()->type);
+ EXPECT_FALSE(observer.details()->is_blocked);
+ EXPECT_FALSE(expect_blocked);
+ return true;
+ }
+ if (expect_blocked) {
+ loop.Run();
+ CHECK(observer.details());
+ EXPECT_EQ(network::mojom::SharedDictionaryAccessDetails::Type::kRead,
+ observer.details()->type);
+ EXPECT_TRUE(observer.details()->is_blocked);
+ }
+ return false;
+ }
+
+ void WaitForDictionaryReady(net::EmbeddedTestServer& server) {
+ // Need the polling because writing the dictionary to the storage and the
+ // database may take time.
+ while (!CheckDictionaryHeader(server, /*expect_blocked=*/false)) {
+ base::PlatformThread::Sleep(base::Milliseconds(5));
+ }
+ }
+
+ int GetSiteDataCount(base::Time begin_time, base::Time end_time) {
+ base::test::TestFuture<int> result;
+ auto* helper = new SiteDataCountingHelper(browser()->profile(), begin_time,
+ end_time, result.GetCallback());
+ helper->CountAndDestroySelfWhenFinished();
+ return result.Get();
+ ;
+ }
+
+ private:
+ std::unique_ptr<net::test_server::HttpResponse> RequestHandler(
+ const net::test_server::HttpRequest& request) {
+ auto response = std::make_unique<net::test_server::BasicHttpResponse>();
+ response->set_code(net::HTTP_OK);
+ response->AddCustomHeader("access-control-allow-origin", "*");
+ if (request.relative_url == "/dictionary") {
+ response->set_content_type("text/plain");
+ response->AddCustomHeader("use-as-dictionary", "match=\"/path/*\"");
+ response->set_content(kTestDictionaryString);
+ return response;
+ } else if (request.relative_url == "/path/check_header") {
+ response->set_content_type("text/plain");
+ absl::optional<std::string> dict_hash =
+ GetSecAvailableDictionary(request.headers);
+ response->set_content(dict_hash ? "Dictionary header available"
+ : "Dictionary header not available");
+ return response;
+ } else if (request.relative_url == "/path/check_header1.html" ||
+ request.relative_url == "/path/check_header2.html") {
+ response->set_content_type("text/html");
+ absl::optional<std::string> dict_hash =
+ GetSecAvailableDictionary(request.headers);
+ response->set_content(dict_hash ? "Dictionary header available"
+ : "Dictionary header not available");
+ return response;
+ } else if (request.relative_url == "/path/brotli_compressed") {
+ CHECK(GetSecAvailableDictionary(request.headers));
+ response->set_content_type("text/html");
+ response->AddCustomHeader("content-encoding", "sbr");
+ response->set_content(kBrotliCompressedDataString);
+ return response;
+ } else if (request.relative_url == "/path/zstd_compressed") {
+ CHECK(GetSecAvailableDictionary(request.headers));
+ response->set_content_type("text/html");
+ response->AddCustomHeader("content-encoding", "zstd-d");
+ response->set_content(kZstdCompressedDataString);
+ return response;
+ }
+ return nullptr;
+ }
+ std::unique_ptr<net::EmbeddedTestServer> cross_origin_server_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest, BlockWriting) {
+ content_settings::CookieSettings* settings =
+ CookieSettingsFactory::GetForProfile(browser()->profile()).get();
+ settings->SetCookieSetting(embedded_test_server()->GetURL("/"),
+ CONTENT_SETTING_BLOCK);
+
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_FALSE(TryRegisterDictionary(*embedded_test_server()));
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
+ BlockWritingCrossOrigin) {
+ content_settings::CookieSettings* settings =
+ CookieSettingsFactory::GetForProfile(browser()->profile()).get();
+ settings->SetCookieSetting(cross_origin_server()->GetURL("/"),
+ CONTENT_SETTING_BLOCK);
+
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_FALSE(TryRegisterDictionary(*cross_origin_server()));
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest, BlockReading) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
+ WaitForDictionaryReady(*embedded_test_server());
+
+ content_settings::CookieSettings* settings =
+ CookieSettingsFactory::GetForProfile(browser()->profile()).get();
+ settings->SetCookieSetting(embedded_test_server()->GetURL("/"),
+ CONTENT_SETTING_BLOCK);
+
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html?")));
+ EXPECT_FALSE(
+ CheckDictionaryHeader(*embedded_test_server(), /*expect_blocked=*/true));
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
+ BlockReadingCrossOrigin) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_TRUE(TryRegisterDictionary(*cross_origin_server()));
+ WaitForDictionaryReady(*cross_origin_server());
+
+ content_settings::CookieSettings* settings =
+ CookieSettingsFactory::GetForProfile(browser()->profile()).get();
+ settings->SetCookieSetting(cross_origin_server()->GetURL("/"),
+ CONTENT_SETTING_BLOCK);
+
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html?")));
+ EXPECT_FALSE(
+ CheckDictionaryHeader(*cross_origin_server(), /*expect_blocked=*/true));
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
+ BlockReadingWhileNavigation) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
+ WaitForDictionaryReady(*embedded_test_server());
+
+ EXPECT_TRUE(CheckDictionaryHeaderByNavigation(
+ embedded_test_server()->GetURL("/path/check_header1.html"),
+ /*expect_blocked=*/false));
+
+ content_settings::CookieSettings* settings =
+ CookieSettingsFactory::GetForProfile(browser()->profile()).get();
+ settings->SetCookieSetting(embedded_test_server()->GetURL("/"),
+ CONTENT_SETTING_BLOCK);
+
+ EXPECT_FALSE(CheckDictionaryHeaderByNavigation(
+ embedded_test_server()->GetURL("/path/check_header2.html"),
+ /*expect_blocked=*/true));
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
+ BlockReadingWhileIframeNavigation) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
+ WaitForDictionaryReady(*embedded_test_server());
+
+ EXPECT_TRUE(CheckDictionaryHeaderByIframeNavigation(
+ embedded_test_server()->GetURL("/path/check_header1.html"),
+ /*expect_blocked=*/false));
+
+ content_settings::CookieSettings* settings =
+ CookieSettingsFactory::GetForProfile(browser()->profile()).get();
+ settings->SetCookieSetting(embedded_test_server()->GetURL("/"),
+ CONTENT_SETTING_BLOCK);
+
+ EXPECT_FALSE(CheckDictionaryHeaderByIframeNavigation(
+ embedded_test_server()->GetURL("/path/check_header2.html"),
+ /*expect_blocked=*/true));
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
+ UseCounterMainFrameNavigation) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
+ WaitForDictionaryReady(*embedded_test_server());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ base::HistogramTester histograms;
+
+ EXPECT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/path/brotli_compressed")));
+ EXPECT_EQ(kCompressedDataOriginalString,
+ EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+ "document.body.innerText")
+ .ExtractString());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ CheckSharedDictionaryUseCounter(
+ histograms,
+ /*expected_used_count_with_sbr=*/1,
+ /*expected_used_count_with_zstd_d=*/0,
+ /*expected_used_for_navigation_count=*/1,
+ /*expected_used_for_main_frame_navigation_count=*/1,
+ /*expected_used_for_sub_frame_navigation_count=*/0,
+ /*expected_used_for_subresource_count=*/0);
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
+ UseCounterSubFrameNavigation) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
+ WaitForDictionaryReady(*embedded_test_server());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ base::HistogramTester histograms;
+
+ EXPECT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+
+ EXPECT_EQ(kCompressedDataOriginalString,
+ EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+ R"(
+ (async () => {
+ const iframe = document.createElement('iframe');
+ iframe.src = '/path/brotli_compressed';
+ const promise =
+ new Promise(resolve => { iframe.addEventListener('load', resolve); });
+ document.body.appendChild(iframe);
+ await promise;
+ return iframe.contentDocument.body.innerText;
+ })()
+ )")
+ .ExtractString());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ CheckSharedDictionaryUseCounter(
+ histograms,
+ /*expected_used_count_with_sbr=*/1,
+ /*expected_used_count_with_zstd_d=*/0,
+ /*expected_used_for_navigation_count=*/1,
+ /*expected_used_for_main_frame_navigation_count=*/0,
+ /*expected_used_for_sub_frame_navigation_count=*/1,
+ /*expected_used_for_subresource_count=*/0);
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
+ UseCounterSubresource) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
+ WaitForDictionaryReady(*embedded_test_server());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ base::HistogramTester histograms;
+
+ EXPECT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+
+ EXPECT_EQ(kCompressedDataOriginalString,
+ EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+ R"(
+ (async () => {
+ return await (await fetch('path/brotli_compressed')).text();
+ })()
+ )")
+ .ExtractString());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ CheckSharedDictionaryUseCounter(
+ histograms,
+ /*expected_used_count_with_sbr=*/1,
+ /*expected_used_count_with_zstd_d=*/0,
+ /*expected_used_for_navigation_count=*/0,
+ /*expected_used_for_main_frame_navigation_count=*/0,
+ /*expected_used_for_sub_frame_navigation_count=*/0,
+ /*expected_used_for_subresource_count=*/1);
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
+ UseCounterZstdMainFrameNavigation) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
+ WaitForDictionaryReady(*embedded_test_server());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ base::HistogramTester histograms;
+
+ EXPECT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/path/zstd_compressed")));
+ EXPECT_EQ(kCompressedDataOriginalString,
+ EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+ "document.body.innerText")
+ .ExtractString());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ CheckSharedDictionaryUseCounter(
+ histograms,
+ /*expected_used_count_with_sbr=*/0,
+ /*expected_used_count_with_zstd_d=*/1,
+ /*expected_used_for_navigation_count=*/1,
+ /*expected_used_for_main_frame_navigation_count=*/1,
+ /*expected_used_for_sub_frame_navigation_count=*/0,
+ /*expected_used_for_subresource_count=*/0);
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
+ UseCounterZstdSubresource) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
+ WaitForDictionaryReady(*embedded_test_server());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ base::HistogramTester histograms;
+
+ EXPECT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+
+ EXPECT_EQ(kCompressedDataOriginalString,
+ EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+ R"(
+ (async () => {
+ return await (await fetch('path/zstd_compressed')).text();
+ })()
+ )")
+ .ExtractString());
+
+ // Navigate away in order to flush use counters.
+ EXPECT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
+
+ CheckSharedDictionaryUseCounter(
+ histograms,
+ /*expected_used_count_with_sbr=*/0,
+ /*expected_used_count_with_zstd_d=*/1,
+ /*expected_used_for_navigation_count=*/0,
+ /*expected_used_for_main_frame_navigation_count=*/0,
+ /*expected_used_for_sub_frame_navigation_count=*/0,
+ /*expected_used_for_subresource_count=*/1);
+}
+
+// TODO(crbug.com/1472445): Fix flakiness in test and enable.
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_SiteDataCount DISABLED_SiteDataCount
+#else
+#define MAYBE_SiteDataCount SiteDataCount
+#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest, MAYBE_SiteDataCount) {
+ base::Time time1 = base::Time::Now();
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), embedded_test_server()->GetURL("/title1.html")));
+ base::Time time2 = base::Time::Now();
+ EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
+ WaitForDictionaryReady(*embedded_test_server());
+ base::Time time3 = base::Time::Now();
+
+ EXPECT_EQ(0, GetSiteDataCount(time1, time2));
+ EXPECT_EQ(1, GetSiteDataCount(time2, time3));
+}
diff --git a/chromium/chrome/browser/net/cookie_policy_browsertest.cc b/chromium/chrome/browser/net/cookie_policy_browsertest.cc
index ebb98f37eb2..a25647b5e6e 100644
--- a/chromium/chrome/browser/net/cookie_policy_browsertest.cc
+++ b/chromium/chrome/browser/net/cookie_policy_browsertest.cc
@@ -31,6 +31,7 @@
#include "net/base/features.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/blink/public/common/features_generated.h"
#include "ui/base/window_open_disposition.h"
using content::BrowserThread;
@@ -42,6 +43,19 @@ const char* kHostB = "b.test";
const char* kHostC = "c.test";
const char* kHostD = "d.test";
+bool ThirdPartyPartitionedStorageAllowedByDefault() {
+ return base::FeatureList::IsEnabled(
+ net::features::kThirdPartyPartitionedStorageAllowedByDefault) &&
+ base::FeatureList::IsEnabled(
+ net::features::kThirdPartyStoragePartitioning);
+}
+
+bool ThirdPartyPartitionedStorageAllowedByStorageAccessAPI() {
+ return base::FeatureList::IsEnabled(blink::features::kStorageAccessAPI) &&
+ base::FeatureList::IsEnabled(
+ net::features::kThirdPartyStoragePartitioning);
+}
+
class CookiePolicyBrowserTest : public InProcessBrowserTest {
public:
CookiePolicyBrowserTest(const CookiePolicyBrowserTest&) = delete;
@@ -447,7 +461,8 @@ IN_PROC_BROWSER_TEST_F(CookiePolicyBrowserTest, MultiTabTest) {
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
- storage::test::ExpectCrossTabInfoForFrame(GetFrame(), false);
+ storage::test::ExpectCrossTabInfoForFrame(
+ GetFrame(), ThirdPartyPartitionedStorageAllowedByDefault());
// Allow all requests to b.test to access cookies.
GURL a_url = https_server_.GetURL(kHostA, "/");
@@ -464,7 +479,8 @@ IN_PROC_BROWSER_TEST_F(CookiePolicyBrowserTest, MultiTabTest) {
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
- storage::test::ExpectCrossTabInfoForFrame(GetFrame(), false);
+ storage::test::ExpectCrossTabInfoForFrame(
+ GetFrame(), ThirdPartyPartitionedStorageAllowedByDefault());
// Allow all third-parties on a.test to access cookies.
cookie_settings()->SetThirdPartyCookieSetting(
@@ -498,7 +514,11 @@ IN_PROC_BROWSER_TEST_F(CookiePolicyBrowserTest, MultiTabNestedTest) {
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostA, "/browsing_data/site_data.html");
- storage::test::ExpectCrossTabInfoForFrame(GetNestedFrame(), false);
+ bool expected_storage =
+ ThirdPartyPartitionedStorageAllowedByDefault() ||
+ ThirdPartyPartitionedStorageAllowedByStorageAccessAPI();
+
+ storage::test::ExpectCrossTabInfoForFrame(GetNestedFrame(), expected_storage);
// Allow all requests to a.test to access cookies.
GURL a_url = https_server_.GetURL(kHostA, "/");
@@ -516,7 +536,8 @@ IN_PROC_BROWSER_TEST_F(CookiePolicyBrowserTest, MultiTabNestedTest) {
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostA, "/browsing_data/site_data.html");
- storage::test::ExpectCrossTabInfoForFrame(GetNestedFrame(), false);
+
+ storage::test::ExpectCrossTabInfoForFrame(GetNestedFrame(), expected_storage);
// Allow all third-parties on a.test to access cookies.
cookie_settings()->SetThirdPartyCookieSetting(
@@ -534,14 +555,16 @@ class CookiePolicyStorageBrowserTest
: public CookiePolicyBrowserTest,
public testing::WithParamInterface<ContextType> {
protected:
- void ExpectStorage(content::RenderFrameHost* frame, bool expected) {
+ void ExpectStorage(content::RenderFrameHost* frame,
+ bool expected_storage,
+ bool expected_cookie) {
switch (ContextType()) {
case ContextType::kFrame:
- storage::test::ExpectStorageForFrame(frame, /*include_cookies=*/true,
- expected);
+ storage::test::ExpectStorageForFrame(frame, expected_storage);
+ EXPECT_EQ(expected_cookie, content::EvalJs(frame, "hasCookie()"));
return;
case ContextType::kWorker:
- storage::test::ExpectStorageForWorker(frame, expected);
+ storage::test::ExpectStorageForWorker(frame, expected_storage);
return;
}
}
@@ -565,15 +588,19 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
- ExpectStorage(GetFrame(), false);
+ ExpectStorage(GetFrame(), /*expected_storage=*/false,
+ /*expected_cookie=*/false);
SetStorage(GetFrame());
- ExpectStorage(GetFrame(), true);
+ ExpectStorage(GetFrame(), /*expected_storage=*/true,
+ /*expected_cookie=*/true);
SetBlockThirdPartyCookies(true);
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
- ExpectStorage(GetFrame(), false);
+
+ ExpectStorage(GetFrame(), ThirdPartyPartitionedStorageAllowedByDefault(),
+ /*expected_cookie=*/false);
// Allow all requests to b.test to access storage.
GURL a_url = https_server_.GetURL(kHostA, "/");
@@ -583,14 +610,16 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
- ExpectStorage(GetFrame(), true);
+ ExpectStorage(GetFrame(), /*expected_storage=*/true,
+ /*expected_cookie=*/true);
// Remove ALLOW setting.
cookie_settings()->ResetCookieSetting(b_url);
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
- ExpectStorage(GetFrame(), false);
+ ExpectStorage(GetFrame(), ThirdPartyPartitionedStorageAllowedByDefault(),
+ /*expected_cookie=*/false);
// Allow all third-parties on a.test to access storage.
cookie_settings()->SetThirdPartyCookieSetting(
@@ -598,7 +627,8 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
- ExpectStorage(GetFrame(), true);
+ ExpectStorage(GetFrame(), /*expected_storage=*/true,
+ /*expected_cookie=*/true);
}
IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
@@ -607,16 +637,21 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostC, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), false);
+ ExpectStorage(GetNestedFrame(), /*expected_storage=*/false,
+ /*expected_cookie=*/false);
SetStorage(GetNestedFrame());
- ExpectStorage(GetNestedFrame(), true);
+ ExpectStorage(GetNestedFrame(), /*expected_storage=*/true,
+ /*expected_cookie=*/true);
SetBlockThirdPartyCookies(true);
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostC, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), false);
+
+ ExpectStorage(GetNestedFrame(),
+ ThirdPartyPartitionedStorageAllowedByDefault(),
+ /*expected_cookie=*/false);
// Allow all requests to b.test to access storage.
GURL a_url = https_server_.GetURL(kHostA, "/");
@@ -627,7 +662,8 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostC, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), true);
+ ExpectStorage(GetNestedFrame(), /*expected_storage=*/true,
+ /*expected_cookie=*/true);
// Remove ALLOW setting.
cookie_settings()->ResetCookieSetting(c_url);
@@ -635,7 +671,9 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostC, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), false);
+ ExpectStorage(GetNestedFrame(),
+ ThirdPartyPartitionedStorageAllowedByDefault(),
+ /*expected_cookie=*/false);
// Allow all third-parties on a.test to access storage.
cookie_settings()->SetThirdPartyCookieSetting(
@@ -644,7 +682,182 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostC, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), true);
+ ExpectStorage(GetNestedFrame(), /*expected_storage=*/true,
+ /*expected_cookie=*/true);
+}
+
+class ThirdPartyPartitionedStorageAccessibilityTest
+ : public CookiePolicyBrowserTest,
+ public testing::WithParamInterface<std::tuple<ContextType, bool>> {
+ public:
+ ThirdPartyPartitionedStorageAccessibilityTest() {
+ feature_list_.InitWithFeatureStates(
+ {{net::features::kThirdPartyStoragePartitioning,
+ StoragePartitioningEnabled()},
+ {net::features::kThirdPartyPartitionedStorageAllowedByDefault, true}});
+ }
+
+ void ExpectStorage(content::RenderFrameHost* frame, bool expected_storage) {
+ switch (ContextType()) {
+ case ContextType::kFrame:
+ storage::test::ExpectStorageForFrame(frame, expected_storage);
+ return;
+ case ContextType::kWorker:
+ storage::test::ExpectStorageForWorker(frame, expected_storage);
+ return;
+ }
+ }
+
+ void SetStorage(content::RenderFrameHost* frame) {
+ switch (ContextType()) {
+ case ContextType::kFrame:
+ storage::test::SetStorageForFrame(frame, /*include_cookies=*/false);
+ return;
+ case ContextType::kWorker:
+ storage::test::SetStorageForWorker(frame);
+ return;
+ }
+ }
+
+ bool StoragePartitioningEnabled() const { return std::get<1>(GetParam()); }
+
+ protected:
+ ContextType ContextType() const { return std::get<0>(GetParam()); }
+
+ private:
+ base::test::ScopedFeatureList feature_list_;
+};
+
+// When third-party cookies are disabled, third-party storage should only be
+// accessible when storage partitioning is enabled.
+IN_PROC_BROWSER_TEST_P(ThirdPartyPartitionedStorageAccessibilityTest, Basic) {
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+ SetStorage(GetFrame());
+ ExpectStorage(GetFrame(), true);
+
+ SetBlockThirdPartyCookies(true);
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+
+ ExpectStorage(GetFrame(), StoragePartitioningEnabled());
+}
+
+// Partitioned third-party storage shouldn't be accessible when cookies are
+// disabled by a user setting, even if 3p cookies are also disabled by
+// default.
+IN_PROC_BROWSER_TEST_P(ThirdPartyPartitionedStorageAccessibilityTest,
+ UserSetting) {
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+ SetStorage(GetFrame());
+ ExpectStorage(GetFrame(), true);
+
+ GURL b_url = https_server_.GetURL(kHostB, "/");
+ cookie_settings()->SetCookieSetting(b_url,
+ ContentSetting::CONTENT_SETTING_BLOCK);
+
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+ ExpectStorage(GetFrame(), false);
+
+ SetBlockThirdPartyCookies(true);
+
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+ ExpectStorage(GetFrame(), false);
+}
+
+// This "using" is to give the shared worker tests a different name in
+// order to allow us to instantiate different parameters for them.
+//
+// This is because Shared worker tests don't depend on the ContextType, so we
+// can just arbitrarily choose any single value. We don't want to use the same
+// parameters as ThirdPartyPartitionedStorageAccessibilityTest as that would
+// mean the shared worker tests would run twice as many times as necessary.
+using ThirdPartyPartitionedStorageAccessibilitySharedWorkerTest =
+ ThirdPartyPartitionedStorageAccessibilityTest;
+
+IN_PROC_BROWSER_TEST_P(
+ ThirdPartyPartitionedStorageAccessibilitySharedWorkerTest,
+ Basic) {
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+
+ storage::test::SetCrossTabInfoForFrame(GetFrame());
+ storage::test::ExpectCrossTabInfoForFrame(GetFrame(), true);
+
+ // Create a second tab to test shared worker between tabs.
+ NavigateToNewTabWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+ storage::test::ExpectCrossTabInfoForFrame(GetFrame(), true);
+
+ SetBlockThirdPartyCookies(true);
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+
+ storage::test::ExpectCrossTabInfoForFrame(GetFrame(),
+ StoragePartitioningEnabled());
+}
+
+IN_PROC_BROWSER_TEST_P(
+ ThirdPartyPartitionedStorageAccessibilitySharedWorkerTest,
+ UserSetting) {
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+
+ storage::test::SetCrossTabInfoForFrame(GetFrame());
+ storage::test::ExpectCrossTabInfoForFrame(GetFrame(), true);
+
+ // Create a second tab to test shared worker between tabs.
+ NavigateToNewTabWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+ storage::test::ExpectCrossTabInfoForFrame(GetFrame(), true);
+
+ GURL b_url = https_server_.GetURL(kHostB, "/");
+ cookie_settings()->SetCookieSetting(b_url,
+ ContentSetting::CONTENT_SETTING_BLOCK);
+
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+ storage::test::ExpectCrossTabInfoForFrame(GetFrame(), false);
+
+ SetBlockThirdPartyCookies(true);
+
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+ storage::test::ExpectCrossTabInfoForFrame(GetFrame(), false);
+}
+
+class ThirdPartyPartitionedStorageAccessibilityCanBeDisabledTest
+ : public ThirdPartyPartitionedStorageAccessibilityTest {
+ public:
+ ThirdPartyPartitionedStorageAccessibilityCanBeDisabledTest() {
+ feature_list_.InitAndDisableFeature(
+ net::features::kThirdPartyPartitionedStorageAllowedByDefault);
+ }
+
+ private:
+ base::test::ScopedFeatureList feature_list_;
+};
+
+// Tests that even if partitioned third-party storage would otherwise be
+// accessible, we can disable it with
+// kThirdPartyPartitionedStorageAllowedByDefault.
+IN_PROC_BROWSER_TEST_P(
+ ThirdPartyPartitionedStorageAccessibilityCanBeDisabledTest,
+ Basic) {
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+ SetStorage(GetFrame());
+ ExpectStorage(GetFrame(), true);
+
+ SetBlockThirdPartyCookies(true);
+ NavigateToPageWithFrame(kHostA);
+ NavigateFrameTo(kHostB, "/browsing_data/site_data.html");
+
+ // Third-party storage is now always inaccessible.
+ ExpectStorage(GetFrame(), false);
}
IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
@@ -660,16 +873,23 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostA, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), false);
+ ExpectStorage(GetNestedFrame(), /*expected_storage=*/false,
+ /*expected_cookie=*/false);
SetStorage(GetNestedFrame());
- ExpectStorage(GetNestedFrame(), true);
+ ExpectStorage(GetNestedFrame(), /*expected_storage=*/true,
+ /*expected_cookie=*/true);
SetBlockThirdPartyCookies(true);
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostA, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), false);
+ bool expected_storage =
+ ThirdPartyPartitionedStorageAllowedByDefault() ||
+ ThirdPartyPartitionedStorageAllowedByStorageAccessAPI();
+
+ ExpectStorage(GetNestedFrame(), expected_storage,
+ /*expected_cookie=*/false);
// Allow all requests to b.test to access storage.
GURL a_url = https_server_.GetURL(kHostA, "/");
@@ -679,7 +899,8 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostA, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), true);
+ ExpectStorage(GetNestedFrame(), /*expected_storage=*/true,
+ /*expected_cookie=*/true);
// Remove ALLOW setting.
cookie_settings()->ResetCookieSetting(a_url);
@@ -687,7 +908,9 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostA, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), false);
+
+ ExpectStorage(GetNestedFrame(), expected_storage,
+ /*expected_cookie=*/false);
// Allow all third-parties on a.test to access storage.
cookie_settings()->SetThirdPartyCookieSetting(
@@ -696,7 +919,8 @@ IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
NavigateToPageWithFrame(kHostA);
NavigateFrameTo(kHostB, "/iframe.html");
NavigateNestedFrameTo(kHostA, "/browsing_data/site_data.html");
- ExpectStorage(GetNestedFrame(), true);
+ ExpectStorage(GetNestedFrame(), /*expected_storage=*/true,
+ /*expected_cookie=*/true);
}
class ThirdPartyCookiePhaseoutPolicyStorageBrowserTest
@@ -726,7 +950,6 @@ IN_PROC_BROWSER_TEST_F(ThirdPartyCookiePhaseoutPolicyStorageBrowserTest,
storage::test::SetStorageForFrame(GetNestedFrame(),
/*include_cookies=*/false);
storage::test::ExpectStorageForFrame(GetNestedFrame(),
- /*include_cookies=*/false,
/*expected=*/true);
}
@@ -735,4 +958,21 @@ INSTANTIATE_TEST_SUITE_P(,
testing::Values(ContextType::kFrame,
ContextType::kWorker));
+INSTANTIATE_TEST_SUITE_P(,
+ ThirdPartyPartitionedStorageAccessibilityTest,
+ testing::Combine(testing::Values(ContextType::kFrame,
+ ContextType::kWorker),
+ testing::Bool()));
+
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ ThirdPartyPartitionedStorageAccessibilitySharedWorkerTest,
+ testing::Combine(testing::Values(ContextType::kFrame), testing::Bool()));
+
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ ThirdPartyPartitionedStorageAccessibilityCanBeDisabledTest,
+ testing::Combine(testing::Values(ContextType::kFrame, ContextType::kWorker),
+ testing::Values(true)));
+
} // namespace
diff --git a/chromium/chrome/browser/net/dns_probe_browsertest.cc b/chromium/chrome/browser/net/dns_probe_browsertest.cc
index 3e6ae2bb96a..a2fc5e5f5f2 100644
--- a/chromium/chrome/browser/net/dns_probe_browsertest.cc
+++ b/chromium/chrome/browser/net/dns_probe_browsertest.cc
@@ -182,13 +182,14 @@ class DnsProbeBrowserTest : public InProcessBrowserTest {
std::unique_ptr<FakeHostResolverNetworkContext> network_context_;
std::unique_ptr<FakeDnsConfigChangeManager> dns_config_change_manager_;
- raw_ptr<DelayingDnsProbeService, DanglingUntriaged>
+ raw_ptr<DelayingDnsProbeService, AcrossTasksDanglingUntriaged>
delaying_dns_probe_service_;
// Browser that methods apply to.
- raw_ptr<Browser, DanglingUntriaged> active_browser_;
+ raw_ptr<Browser, AcrossTasksDanglingUntriaged> active_browser_;
// Helper that current has its DnsProbeStatus messages monitored.
- raw_ptr<NetErrorTabHelper, DanglingUntriaged> monitored_tab_helper_;
+ raw_ptr<NetErrorTabHelper, AcrossTasksDanglingUntriaged>
+ monitored_tab_helper_;
std::unique_ptr<base::RunLoop> awaiting_dns_probe_status_run_loop_;
// Queue of statuses received but not yet consumed by WaitForSentStatus().
diff --git a/chromium/chrome/browser/net/dns_probe_service_factory.cc b/chromium/chrome/browser/net/dns_probe_service_factory.cc
index b6b09904938..a51d4185b30 100644
--- a/chromium/chrome/browser/net/dns_probe_service_factory.cc
+++ b/chromium/chrome/browser/net/dns_probe_service_factory.cc
@@ -11,7 +11,6 @@
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
-#include "base/metrics/histogram_macros.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/default_tick_clock.h"
@@ -47,15 +46,6 @@ const int kMaxResultAgeMs = 5000;
const uint8_t kGooglePublicDns1[] = {8, 8, 8, 8};
const uint8_t kGooglePublicDns2[] = {8, 8, 4, 4};
-void HistogramProbe(error_page::DnsProbeStatus status,
- base::TimeDelta elapsed) {
- DCHECK(error_page::DnsProbeStatusIsFinished(status));
-
- UMA_HISTOGRAM_ENUMERATION("DnsProbe.ProbeResult", status,
- error_page::DNS_PROBE_MAX);
- UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.ProbeDuration2", elapsed);
-}
-
network::mojom::NetworkContext* GetNetworkContextForProfile(
content::BrowserContext* context) {
return context->GetDefaultStoragePartition()->GetNetworkContext();
@@ -283,8 +273,6 @@ void DnsProbeServiceImpl::OnProbeComplete() {
google_config_runner_->result());
state_ = STATE_RESULT_CACHED;
- HistogramProbe(cached_result_, tick_clock_->NowTicks() - probe_start_time_);
-
CallCallbacks();
}
@@ -418,9 +406,10 @@ DnsProbeServiceFactory::DnsProbeServiceFactory()
DnsProbeServiceFactory::~DnsProbeServiceFactory() = default;
-KeyedService* DnsProbeServiceFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+DnsProbeServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
- return new DnsProbeServiceImpl(context);
+ return std::make_unique<DnsProbeServiceImpl>(context);
}
// static
diff --git a/chromium/chrome/browser/net/dns_probe_service_factory.h b/chromium/chrome/browser/net/dns_probe_service_factory.h
index a4fa505a3fb..53850d8df1f 100644
--- a/chromium/chrome/browser/net/dns_probe_service_factory.h
+++ b/chromium/chrome/browser/net/dns_probe_service_factory.h
@@ -63,7 +63,7 @@ class DnsProbeServiceFactory : public ProfileKeyedServiceFactory {
~DnsProbeServiceFactory() override;
// BrowserContextKeyedServiceFactory implementation:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};
diff --git a/chromium/chrome/browser/net/fake_nss_service.cc b/chromium/chrome/browser/net/fake_nss_service.cc
index 65b9c73ca89..c9967ac365a 100644
--- a/chromium/chrome/browser/net/fake_nss_service.cc
+++ b/chromium/chrome/browser/net/fake_nss_service.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/chromium/chrome/browser/net/fake_nss_service.h b/chromium/chrome/browser/net/fake_nss_service.h
index 1cf78b73ca5..dc3fcfca7bf 100644
--- a/chromium/chrome/browser/net/fake_nss_service.h
+++ b/chromium/chrome/browser/net/fake_nss_service.h
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/chromium/chrome/browser/net/net_error_tab_helper_browsertest.cc b/chromium/chrome/browser/net/net_error_tab_helper_browsertest.cc
index 19086e72c1b..1178e07c064 100644
--- a/chromium/chrome/browser/net/net_error_tab_helper_browsertest.cc
+++ b/chromium/chrome/browser/net/net_error_tab_helper_browsertest.cc
@@ -75,7 +75,7 @@ class NetErrorTabHelperTest : public InProcessBrowserTest {
}
private:
- raw_ptr<chrome_browser_net::NetErrorTabHelper, DanglingUntriaged>
+ raw_ptr<chrome_browser_net::NetErrorTabHelper, AcrossTasksDanglingUntriaged>
tab_helper_ = nullptr;
};
@@ -93,7 +93,7 @@ class NetErrorTabHelperWithPrerenderingTest : public NetErrorTabHelperTest {
const NetErrorTabHelperWithPrerenderingTest&) = delete;
void SetUp() override {
- prerender_helper_.SetUp(embedded_test_server());
+ prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
NetErrorTabHelperTest::SetUp();
}
diff --git a/chromium/chrome/browser/net/network_context_configuration_browsertest.cc b/chromium/chrome/browser/net/network_context_configuration_browsertest.cc
index 8bd52f54a11..433072c2fed 100644
--- a/chromium/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chromium/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -720,7 +720,7 @@ class NetworkContextConfigurationBrowserTest
FlushNetworkInterface();
}
- raw_ptr<Browser, DanglingUntriaged> incognito_ = nullptr;
+ raw_ptr<Browser, AcrossTasksDanglingUntriaged> incognito_ = nullptr;
net::EmbeddedTestServer https_server_;
std::unique_ptr<net::test_server::ControllableHttpResponse>
@@ -1031,7 +1031,14 @@ IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, PRE_DiskCache) {
// Check if the URL loaded in PRE_DiskCache is still in the cache, across a
// browser restart.
-IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, DiskCache) {
+// TODO(https://crbug.com/1464367): Fix test flake and re-enable
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_DiskCache DISABLED_DiskCache
+#else
+#define MAYBE_DiskCache DiskCache
+#endif
+IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
+ MAYBE_DiskCache) {
if (IsRestartStateWithInProcessNetworkService())
return;
// Crashing the network service may corrupt the disk cache, so skip this phase
diff --git a/chromium/chrome/browser/net/network_request_metrics_browsertest.cc b/chromium/chrome/browser/net/network_request_metrics_browsertest.cc
index 4142c6a3d6f..244d5eefe1c 100644
--- a/chromium/chrome/browser/net/network_request_metrics_browsertest.cc
+++ b/chromium/chrome/browser/net/network_request_metrics_browsertest.cc
@@ -13,7 +13,6 @@
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/predictors/loading_predictor_config.h"
@@ -106,11 +105,7 @@ class NetworkRequestMetricsBrowserTest
: public InProcessBrowserTest,
public testing::WithParamInterface<RequestType> {
public:
- NetworkRequestMetricsBrowserTest() {
- scoped_feature_list_.InitAndDisableFeature(
- predictors::kSpeculativePreconnectFeature);
- }
- ~NetworkRequestMetricsBrowserTest() override {}
+ ~NetworkRequestMetricsBrowserTest() override = default;
// ContentBrowserTest implementation:
void SetUpOnMainThread() override {
@@ -187,8 +182,6 @@ class NetworkRequestMetricsBrowserTest
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
if (GetParam() == RequestType::kMainFrame) {
- histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
-
histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4",
-expected_net_error, 1);
@@ -237,13 +230,6 @@ class NetworkRequestMetricsBrowserTest
}
EXPECT_TRUE(found_expected_load);
- if (GetParam() != RequestType::kImage) {
- histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
- } else {
- histograms_->ExpectUniqueSample("Net.ErrorCodesForImages2",
- -expected_net_error, 1);
- }
-
// A subresource load requires a main frame load, which is only logged for
// network URLs.
if (network_accessed == NetworkAccessed::kNetworkAccessed) {
@@ -276,8 +262,6 @@ class NetworkRequestMetricsBrowserTest
if (GetParam() == RequestType::kMainFrame) {
// Can't check Net.ErrorCodesForSubresources3, due to the favicon, which
// Chrome may or may not have attempted to load.
- histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
-
histograms_->ExpectTotalCount("Net.ErrorCodesForMainFrame4", 1);
EXPECT_EQ(1, histograms_->GetBucketCount("Net.ErrorCodesForMainFrame4",
-net::ERR_ABORTED));
@@ -309,13 +293,6 @@ class NetworkRequestMetricsBrowserTest
<< "Found unexpected load with result: " << bucket.min;
}
EXPECT_TRUE(found_expected_load);
-
- if (GetParam() != RequestType::kImage) {
- histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
- } else {
- histograms_->ExpectUniqueSample("Net.ErrorCodesForImages2",
- -net::ERR_ABORTED, 1);
- }
}
// Send headers and a partial body to |interesting_http_response_|. Doesn't
@@ -347,7 +324,6 @@ class NetworkRequestMetricsBrowserTest
base::HistogramTester* histograms() { return histograms_.get(); }
private:
- base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<net::test_server::ControllableHttpResponse>
uninteresting_main_frame_response_;
std::unique_ptr<net::test_server::ControllableHttpResponse>
@@ -507,7 +483,6 @@ IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, Download) {
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
if (GetParam() == RequestType::kMainFrame) {
- histograms()->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
histograms()->ExpectTotalCount("Net.ErrorCodesForMainFrame4", 0);
histograms()->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0);
// Favicon may or may not have been loaded.
diff --git a/chromium/chrome/browser/net/nss_service_factory.cc b/chromium/chrome/browser/net/nss_service_factory.cc
index 317c4b96092..abb0ee1254f 100644
--- a/chromium/chrome/browser/net/nss_service_factory.cc
+++ b/chromium/chrome/browser/net/nss_service_factory.cc
@@ -40,7 +40,8 @@ NssServiceFactory* NssServiceFactory::GetInstance() {
return instance.get();
}
-KeyedService* NssServiceFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+NssServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
- return new NssService(context);
+ return std::make_unique<NssService>(context);
}
diff --git a/chromium/chrome/browser/net/nss_service_factory.h b/chromium/chrome/browser/net/nss_service_factory.h
index 1f953034879..93f3784b698 100644
--- a/chromium/chrome/browser/net/nss_service_factory.h
+++ b/chromium/chrome/browser/net/nss_service_factory.h
@@ -27,7 +27,7 @@ class NssServiceFactory : public ProfileKeyedServiceFactory {
~NssServiceFactory() override;
// `BrowserContextKeyedServiceFactory` implementation:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};
diff --git a/chromium/chrome/browser/net/nss_temp_certs_cache_chromeos_unittest.cc b/chromium/chrome/browser/net/nss_temp_certs_cache_chromeos_unittest.cc
index b1065d105e1..4c8ef846332 100644
--- a/chromium/chrome/browser/net/nss_temp_certs_cache_chromeos_unittest.cc
+++ b/chromium/chrome/browser/net/nss_temp_certs_cache_chromeos_unittest.cc
@@ -13,7 +13,6 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
-#include "base/run_loop.h"
#include "net/cert/pem.h"
#include "net/cert/pki/cert_errors.h"
#include "net/cert/pki/parse_certificate.h"
@@ -64,7 +63,7 @@ class NSSTempCertsCacheChromeOSTest : public testing::Test {
// certificate data buffer. The caller must pass a buffer in
// |cert_contents_buffer| and ensure to only use *|out_subject| while
// *|cert_contents_buffer| is in scope.
- // Note: This funcion uses ASSERT_ macros, so the caller must verify for
+ // Note: This function uses ASSERT_ macros, so the caller must verify for
// failures after it returns.
void GetCertificateSubjectDN(const base::FilePath& pem_cert_file,
std::string* cert_contents_buffer,
@@ -84,7 +83,7 @@ class NSSTempCertsCacheChromeOSTest : public testing::Test {
net::der::BitString signature_value;
net::CertErrors errors;
ASSERT_TRUE(net::ParseCertificate(
- net::der::Input(cert_contents_buffer), &tbs_certificate_tlv,
+ net::der::Input(*cert_contents_buffer), &tbs_certificate_tlv,
&signature_algorithm_tlv, &signature_value, &errors));
net::ParsedTbsCertificate tbs;
diff --git a/chromium/chrome/browser/net/local_network_access_browsertest.cc b/chromium/chrome/browser/net/private_network_access_browsertest.cc
index 4377f36860b..7f542e4032a 100644
--- a/chromium/chrome/browser/net/local_network_access_browsertest.cc
+++ b/chromium/chrome/browser/net/private_network_access_browsertest.cc
@@ -38,14 +38,14 @@
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
-#include "content/public/test/local_network_access_util.h"
+#include "content/public/test/private_network_access_util.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/url_loader_interceptor.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_builder.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "services/network/public/cpp/local_network_access_check_result.h"
+#include "services/network/public/cpp/private_network_access_check_result.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/features.h"
@@ -61,13 +61,13 @@ using testing::IsEmpty;
// automatic request to /favicon.ico. This is because the automatic request
// messes with our tests, in which we want to trigger a single request from the
// web page to a resource of our choice and observe the side-effect in metrics.
-constexpr char kNoFaviconPath[] = "/local_network_access/no-favicon.html";
+constexpr char kNoFaviconPath[] = "/private_network_access/no-favicon.html";
// Same as kNoFaviconPath, except it carries a header that makes the browser
// consider it came from the `public` address space, irrespective of the fact
// that we loaded the web page from localhost.
constexpr char kTreatAsPublicAddressPath[] =
- "/local_network_access/no-favicon-treat-as-public-address.html";
+ "/private_network_access/no-favicon-treat-as-public-address.html";
GURL SecureURL(const net::EmbeddedTestServer& server, const std::string& path) {
// Test HTTPS servers cannot lie about their hostname, so they yield URLs
@@ -105,7 +105,7 @@ GURL LocalNonSecureWithCrossOriginCors(const net::EmbeddedTestServer& server) {
// Path to a worker script that posts a message to its creator once loaded.
constexpr char kWorkerScriptPath[] = "/workers/post_ready.js";
-// Same as above, but with LNA headers set correctly for preflight requests.
+// Same as above, but with PNA headers set correctly for preflight requests.
constexpr char kWorkerScriptWithPnaHeadersPath[] =
"/workers/post_ready_with_pna_headers.js";
@@ -130,7 +130,7 @@ std::string FetchWorkerScript(base::StringPiece relative_url) {
// Path to a worker script that posts a message to each client that connects.
constexpr char kSharedWorkerScriptPath[] = "/workers/shared_post_ready.js";
-// Same as above, but with LNA headers set correctly for preflight requests.
+// Same as above, but with PNA headers set correctly for preflight requests.
constexpr char kSharedWorkerScriptWithPnaHeadersPath[] =
"/workers/shared_post_ready_with_pna_headers.js";
@@ -182,8 +182,8 @@ std::vector<WebFeature> AllAddressSpaceFeatures() {
};
}
-// Local Network Access is a web platform specification aimed at securing
-// requests made from public websites to the local network and localhost.
+// Private Network Access is a web platform specification aimed at securing
+// requests made from public websites to the private network and localhost.
//
// It is mostly implemented in content/, but some of its integrations (
// (with Blink UseCounters, with chrome/-specific special schemes) cannot be
@@ -191,13 +191,13 @@ std::vector<WebFeature> AllAddressSpaceFeatures() {
//
// See also:
//
-// - specification: https://wicg.github.io/local-network-access.
+// - specification: https://wicg.github.io/private-network-access.
// - feature browsertests:
// //content/browser/renderer_host/private_network_access_browsertest.cc
//
-class LocalNetworkAccessBrowserTestBase : public InProcessBrowserTest {
+class PrivateNetworkAccessBrowserTestBase : public InProcessBrowserTest {
public:
- LocalNetworkAccessBrowserTestBase(
+ PrivateNetworkAccessBrowserTestBase(
std::vector<base::test::FeatureRef> enabled_features,
std::vector<base::test::FeatureRef> disabled_features) {
features_.InitWithFeatures(enabled_features, disabled_features);
@@ -242,11 +242,11 @@ class LocalNetworkAccessBrowserTestBase : public InProcessBrowserTest {
base::test::ScopedFeatureList features_;
};
-class LocalNetworkAccessWithFeatureDisabledBrowserTest
- : public LocalNetworkAccessBrowserTestBase {
+class PrivateNetworkAccessWithFeatureDisabledBrowserTest
+ : public PrivateNetworkAccessBrowserTestBase {
public:
- LocalNetworkAccessWithFeatureDisabledBrowserTest()
- : LocalNetworkAccessBrowserTestBase(
+ PrivateNetworkAccessWithFeatureDisabledBrowserTest()
+ : PrivateNetworkAccessBrowserTestBase(
{},
{
features::kBlockInsecurePrivateNetworkRequests,
@@ -260,18 +260,19 @@ struct IsWarningOnlyTestData {
const IsWarningOnlyTestData kIsWarningOnlyTestData[] = {{false}, {true}};
-class LocalNetworkAccessWithFeatureEnabledBrowserTest
- : public LocalNetworkAccessBrowserTestBase {
+class PrivateNetworkAccessWithFeatureEnabledBrowserTest
+ : public PrivateNetworkAccessBrowserTestBase {
public:
- explicit LocalNetworkAccessWithFeatureEnabledBrowserTest(
+ explicit PrivateNetworkAccessWithFeatureEnabledBrowserTest(
bool is_warning_only = false)
- : LocalNetworkAccessBrowserTestBase(
+ : PrivateNetworkAccessBrowserTestBase(
{
blink::features::kPlzDedicatedWorker,
features::kBlockInsecurePrivateNetworkRequests,
features::kBlockInsecurePrivateNetworkRequestsFromPrivate,
features::kBlockInsecurePrivateNetworkRequestsDeprecationTrial,
features::kPrivateNetworkAccessSendPreflights,
+ features::kPrivateNetworkAccessForIframes,
features::kPrivateNetworkAccessForWorkers,
dom_distiller::kReaderMode,
},
@@ -281,13 +282,13 @@ class LocalNetworkAccessWithFeatureEnabledBrowserTest
features::kPrivateNetworkAccessForWorkersWarningOnly,
})) {}
void SetUpCommandLine(base::CommandLine* command_line) override {
- LocalNetworkAccessBrowserTestBase::SetUpCommandLine(command_line);
+ PrivateNetworkAccessBrowserTestBase::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kEnableDomDistiller);
}
private:
void SetUpOnMainThread() override {
- LocalNetworkAccessBrowserTestBase::SetUpOnMainThread();
+ PrivateNetworkAccessBrowserTestBase::SetUpOnMainThread();
// The distiller needs to run in an isolated environment. For tests we
// can simply use the last value available.
if (!dom_distiller::DistillerJavaScriptWorldIdIsSet()) {
@@ -297,21 +298,21 @@ class LocalNetworkAccessWithFeatureEnabledBrowserTest
}
};
-class LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest
- : public LocalNetworkAccessWithFeatureEnabledBrowserTest,
+class PrivateNetworkAccessWithFeatureEnabledWorkerBrowserTest
+ : public PrivateNetworkAccessWithFeatureEnabledBrowserTest,
public testing::WithParamInterface<IsWarningOnlyTestData> {
public:
- LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest()
- : LocalNetworkAccessWithFeatureEnabledBrowserTest(
+ PrivateNetworkAccessWithFeatureEnabledWorkerBrowserTest()
+ : PrivateNetworkAccessWithFeatureEnabledBrowserTest(
GetParam().is_warning_only) {}
};
-class LocalNetworkAccessRespectPreflightResultsBrowserTest
- : public LocalNetworkAccessBrowserTestBase,
+class PrivateNetworkAccessRespectPreflightResultsBrowserTest
+ : public PrivateNetworkAccessBrowserTestBase,
public testing::WithParamInterface<IsWarningOnlyTestData> {
public:
- LocalNetworkAccessRespectPreflightResultsBrowserTest()
- : LocalNetworkAccessBrowserTestBase(
+ PrivateNetworkAccessRespectPreflightResultsBrowserTest()
+ : PrivateNetworkAccessBrowserTestBase(
{
blink::features::kPlzDedicatedWorker,
features::kBlockInsecurePrivateNetworkRequests,
@@ -332,13 +333,13 @@ class LocalNetworkAccessRespectPreflightResultsBrowserTest
//
// UseCounters are translated into UMA histograms at the chrome/ layer, by the
// page_load_metrics component. These tests verify that UseCounters are recorded
-// correctly by Local Network Access code in the right circumstances.
+// correctly by Private Network Access code in the right circumstances.
// This test verifies that no feature is counted for the initial navigation from
// a new tab to a page served by localhost.
//
// Regression test for https://crbug.com/1134601.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
DoesNotRecordAddressSpaceFeatureForInitialNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -356,7 +357,7 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
// TODO(crbug.com/1129326): Revisit this once the story around top-level
// navigations is closer to being resolved. Counting these events will help
// decide what to do.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
DoesNotRecordAddressSpaceFeatureForRegularNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -370,9 +371,10 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
}
// This test verifies that when a non-secure context served from the public
-// address space loads a resource from the local network, the correct WebFeature
+// address space loads a resource from the private network, the correct
+// WebFeature
// is use-counted.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
RecordsAddressSpaceFeatureForFetchInNonSecureContext) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -397,7 +399,7 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
// This test verifies that when the user navigates a `public` document to a
// document served by a non-public IP, no address space feature is recorded.
IN_PROC_BROWSER_TEST_F(
- LocalNetworkAccessWithFeatureEnabledBrowserTest,
+ PrivateNetworkAccessWithFeatureEnabledBrowserTest,
DoesNotRecordAddressSpaceFeatureForBrowserInitiatedNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -416,7 +418,7 @@ IN_PROC_BROWSER_TEST_F(
// This test verifies that when a `public` document navigates itself to a
// document served by a non-public IP, the correct address space feature is
// recorded.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
RecordsAddressSpaceFeatureForNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -443,7 +445,7 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
// recorded, even if the target document carries a CSP `treat-as-public-address`
// directive.
IN_PROC_BROWSER_TEST_F(
- LocalNetworkAccessWithFeatureEnabledBrowserTest,
+ PrivateNetworkAccessWithFeatureEnabledBrowserTest,
RecordsAddressSpaceFeatureForNavigationToTreatAsPublicAddress) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -474,7 +476,7 @@ IN_PROC_BROWSER_TEST_F(
// about:blank, no address space feature is recorded. It serves as a basis for
// comparison with the following tests, which test behavior with iframes.
IN_PROC_BROWSER_TEST_F(
- LocalNetworkAccessWithFeatureEnabledBrowserTest,
+ PrivateNetworkAccessWithFeatureDisabledBrowserTest,
DoesNotRecordAddressSpaceFeatureForChildAboutBlankNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -496,9 +498,9 @@ IN_PROC_BROWSER_TEST_F(
}
// This test verifies that when a non-secure context served from the public
-// address space loads a child frame from the local network, the correct
+// address space loads a child frame from the private network, the correct
// WebFeature is use-counted.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
RecordsAddressSpaceFeatureForChildNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -531,10 +533,10 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
}
// This test verifies that when a non-secure context served from the public
-// address space loads a grand-child frame from the local network, the correct
+// address space loads a grand-child frame from the private network, the correct
// WebFeature is use-counted. If inheritance did not work correctly, the
// intermediate about:blank frame might confuse the address space logic.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
RecordsAddressSpaceFeatureForGrandchildNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -573,18 +575,19 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
}
// This test verifies that the right address space feature is recorded when a
-// navigation results in a local network request. Specifically, in this test
+// navigation results in a private network request. Specifically, in this test
// the document being navigated is not the one initiating the navigation (the
// latter being the "remote initiator" referenced by the test name).
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
RecordsAddressSpaceFeatureForRemoteInitiatorNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
EXPECT_TRUE(content::NavigateToURL(
web_contents(),
- NonSecureURL(*server,
- "/local_network_access/remote-initiator-navigation.html")));
+ NonSecureURL(
+ *server,
+ "/private_network_access/remote-initiator-navigation.html")));
EXPECT_THAT(
feature_histogram_tester.GetNonZeroCounts(AllAddressSpaceFeatures()),
IsEmpty());
@@ -607,15 +610,16 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
// around by the time the navigation finishes, then no address space feature is
// recorded, and importantly: the browser does not crash.
IN_PROC_BROWSER_TEST_F(
- LocalNetworkAccessWithFeatureEnabledBrowserTest,
+ PrivateNetworkAccessWithFeatureDisabledBrowserTest,
DoesNotRecordAddressSpaceFeatureForClosedInitiatorNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
EXPECT_TRUE(content::NavigateToURL(
web_contents(),
- NonSecureURL(*server,
- "/local_network_access/remote-initiator-navigation.html")));
+ NonSecureURL(
+ *server,
+ "/private_network_access/remote-initiator-navigation.html")));
EXPECT_EQ(true, content::EvalJs(web_contents(), R"(
runTest({
@@ -633,15 +637,16 @@ IN_PROC_BROWSER_TEST_F(
// navigated itself by the time the navigation finishes, then no address space
// feature is recorded.
IN_PROC_BROWSER_TEST_F(
- LocalNetworkAccessWithFeatureEnabledBrowserTest,
+ PrivateNetworkAccessWithFeatureDisabledBrowserTest,
DoesNotRecordAddressSpaceFeatureForMissingInitiatorNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
EXPECT_TRUE(content::NavigateToURL(
web_contents(),
- NonSecureURL(*server,
- "/local_network_access/remote-initiator-navigation.html")));
+ NonSecureURL(
+ *server,
+ "/private_network_access/remote-initiator-navigation.html")));
EXPECT_EQ(true, content::EvalJs(web_contents(), R"(
runTest({
@@ -655,9 +660,9 @@ IN_PROC_BROWSER_TEST_F(
IsEmpty());
}
-// This test verifies that local network requests that are blocked are not
+// This test verifies that private network requests that are blocked are not
// use-counted.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
DoesNotRecordAddressSpaceFeatureForBlockedRequests) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -677,14 +682,14 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
base_histogram_tester.ExpectBucketCount(
"Security.PrivateNetworkAccess.CheckResult",
- network::LocalNetworkAccessCheckResult::kBlockedByPolicyBlock, 1);
+ network::PrivateNetworkAccessCheckResult::kBlockedByPolicyBlock, 1);
EXPECT_THAT(
feature_histogram_tester.GetNonZeroCounts(AllAddressSpaceFeatures()),
IsEmpty());
}
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
RecordsAddressSpaceFeatureForDeprecationTrial) {
WebFeatureHistogramTester feature_histogram_tester;
content::DeprecationTrialURLLoaderInterceptor interceptor;
@@ -701,7 +706,7 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
// This test verifies that resources proxied through a proxy on localhost can
// be fetched from documents in the public IP address space.
// Regression test for https://crbug.com/1253239.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
ProxiedResourcesAllowed) {
auto server = NewServer();
@@ -726,9 +731,9 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
IsEmpty());
}
-// This test verifies that resources fetched from cache are subject to Local
+// This test verifies that resources fetched from cache are subject to Private
// Network Access checks. When the fetch is blocked, it is not use-counted.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
DoesNotRecordAddressSpaceFeatureForCachedBlocked) {
auto server = NewServer();
@@ -759,9 +764,9 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
IsEmpty());
}
-// This test verifies that resources fetched from cache are subject to Local
+// This test verifies that resources fetched from cache are subject to Private
// Network Access checks. When the fetch is allowed, it is use-counted.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
RecordsAddressSpaceFeatureForCachedResource) {
auto server = NewServer();
@@ -793,9 +798,9 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a worker script from a non-secure context,
-// even when the LNA for workers feature is disabled.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
+// private network request to load a worker script from a non-secure context,
+// even when the PNA for workers feature is disabled.
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
RecordsFeatureForWorkerScriptFetchFromNonSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -815,9 +820,9 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a worker script from a non-secure context,
-// and the request fails due to LNA unless it's in warning-only mode.
-IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
+// private network request to load a worker script from a non-secure context,
+// and the request fails due to PNA unless it's in warning-only mode.
+IN_PROC_BROWSER_TEST_P(PrivateNetworkAccessWithFeatureEnabledWorkerBrowserTest,
RecordsFeatureForWorkerScriptFetchFromNonSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -838,9 +843,9 @@ IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a worker script from a secure context, even
-// when the LNA for workers feature is disabled.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
+// private network request to load a worker script from a secure context, even
+// when the PNA for workers feature is disabled.
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
RecordsFeatureForWorkerScriptFetchFromSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -859,10 +864,10 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a worker script from a secure context, does not
-// send preflights because the request is same-origin and the origin is
+// private network request to load a worker script from a secure context, does
+// not send preflights because the request is same-origin and the origin is
// potentially trustworthy, and loads the script anyway.
-IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_P(PrivateNetworkAccessWithFeatureEnabledWorkerBrowserTest,
RecordsFeatureForWorkerScriptFetchFromSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -882,10 +887,10 @@ IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a worker script from a secure context.
+// private network request to load a worker script from a secure context.
// The request should always succeed because it same origin and the origin is
// potentially trustworthy.
-IN_PROC_BROWSER_TEST_P(LocalNetworkAccessRespectPreflightResultsBrowserTest,
+IN_PROC_BROWSER_TEST_P(PrivateNetworkAccessRespectPreflightResultsBrowserTest,
RecordsFeatureForWorkerScriptFetchErrorFromSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -904,10 +909,10 @@ IN_PROC_BROWSER_TEST_P(LocalNetworkAccessRespectPreflightResultsBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a worker script from a secure context, does not
-// send a preflight request because the request is same-origin and the origin is
-// potentially trustworthy, and succeeds in loading the script.
-IN_PROC_BROWSER_TEST_P(LocalNetworkAccessRespectPreflightResultsBrowserTest,
+// private network request to load a worker script from a secure context, does
+// not send a preflight request because the request is same-origin and the
+// origin is potentially trustworthy, and succeeds in loading the script.
+IN_PROC_BROWSER_TEST_P(PrivateNetworkAccessRespectPreflightResultsBrowserTest,
RecordsFeatureForWorkerScriptFetchSuccessFromSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -927,9 +932,9 @@ IN_PROC_BROWSER_TEST_P(LocalNetworkAccessRespectPreflightResultsBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a shared worker script from a non-secure
-// context, even when the LNA for workers feature is disabled.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
+// private network request to load a shared worker script from a non-secure
+// context, even when the PNA for workers feature is disabled.
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
RecordsFeatureForSharedWorkerScriptFetchFromNonSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -950,9 +955,9 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a shared worker script from a non-secure
-// context, and the request fails due to LNA unless it's in warning-only mode.
-IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
+// private network request to load a shared worker script from a non-secure
+// context, and the request fails due to PNA unless it's in warning-only mode.
+IN_PROC_BROWSER_TEST_P(PrivateNetworkAccessWithFeatureEnabledWorkerBrowserTest,
RecordsFeatureForSharedWorkerScriptFetchFromNonSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -973,9 +978,9 @@ IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a shared worker script from a secure context,
-// even when the LNA for workers feature is disabled.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
+// private network request to load a shared worker script from a secure context,
+// even when the PNA for workers feature is disabled.
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureDisabledBrowserTest,
RecordsFeatureForSharedWorkerScriptFetchFromSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -995,10 +1000,10 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureDisabledBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a shared worker script from a secure context,
+// private network request to load a shared worker script from a secure context,
// does not send a preflight request because the request is same-origin and the
// origin is potentially trustworthy, and loads the script anyway.
-IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_P(PrivateNetworkAccessWithFeatureEnabledWorkerBrowserTest,
RecordsFeatureForSharedWorkerScriptFetchFromSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -1019,11 +1024,11 @@ IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a shared worker script from a secure context.
+// private network request to load a shared worker script from a secure context.
// The request should always succeed because it is same-origin and the origin is
// potentially trustworthy.
IN_PROC_BROWSER_TEST_P(
- LocalNetworkAccessRespectPreflightResultsBrowserTest,
+ PrivateNetworkAccessRespectPreflightResultsBrowserTest,
RecordsFeatureForSharedWorkerScriptFetchErrorFromSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -1043,11 +1048,11 @@ IN_PROC_BROWSER_TEST_P(
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a shared worker script from a secure context,
+// private network request to load a shared worker script from a secure context,
// does not send a preflight request because the request is same-origin and the
// origin is potentially trustworthy, and succeeds in loading the script.
IN_PROC_BROWSER_TEST_P(
- LocalNetworkAccessRespectPreflightResultsBrowserTest,
+ PrivateNetworkAccessRespectPreflightResultsBrowserTest,
RecordsFeatureForSharedWorkerScriptFetchSuccessFromSecure) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -1067,10 +1072,10 @@ IN_PROC_BROWSER_TEST_P(
}
// This test verifies that a UseCounter is recorded when a document makes a
-// local network request to load a service worker script from treat-as-public
-// to loopback.
+// private network request to load a service worker script from treat-as-public
+// to local.
IN_PROC_BROWSER_TEST_F(
- LocalNetworkAccessWithFeatureEnabledBrowserTest,
+ PrivateNetworkAccessWithFeatureEnabledBrowserTest,
RecordsFeatureForServiceWorkerScriptFetchFromTreatAsPublicToLocal) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -1092,10 +1097,9 @@ IN_PROC_BROWSER_TEST_F(
}
// This test verifies that a UseCounter is not recorded when a document makes a
-// local network request to load a service worker script from loopback to
-// loopback.
+// private network request to load a service worker script from local to local.
IN_PROC_BROWSER_TEST_F(
- LocalNetworkAccessWithFeatureEnabledBrowserTest,
+ PrivateNetworkAccessWithFeatureEnabledBrowserTest,
ShouldNotRecordFeatureForServiceWorkerScriptFetchFromLocalToLocal) {
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -1114,9 +1118,10 @@ IN_PROC_BROWSER_TEST_F(
}
INSTANTIATE_TEST_SUITE_P(,
- LocalNetworkAccessRespectPreflightResultsBrowserTest,
+ PrivateNetworkAccessRespectPreflightResultsBrowserTest,
testing::ValuesIn(kIsWarningOnlyTestData));
+// Test the experimental use counter for accesses to the 0.0.0.0 IP address
// (and the corresponding `[::]` IPv6 address).
//
// In the Internet Protocol Version 4, the address 0.0.0.0 is a non-routable
@@ -1126,7 +1131,7 @@ INSTANTIATE_TEST_SUITE_P(,
// all IP addresses on the local machine.
//
// In this case, 0.0.0.0 can be used to access localhost on MacOS and Linux
-// and bypass Local Network Access checks, so that we would like to forbid
+// and bypass Private Network Access checks, so that we would like to forbid
// fetches to 0.0.0.0. See more: https://crbug.com/1300021
#if BUILDFLAG(IS_WIN)
#define MAYBE_FetchNullIpAddressForNavigation \
@@ -1134,7 +1139,7 @@ INSTANTIATE_TEST_SUITE_P(,
#else
#define MAYBE_FetchNullIpAddressForNavigation FetchNullIpAddressForNavigation
#endif
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
MAYBE_FetchNullIpAddressForNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -1155,7 +1160,7 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
#else
#define MAYBE_FetchNullIpAddressFromDocument FetchNullIpAddressFromDocument
#endif
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
MAYBE_FetchNullIpAddressFromDocument) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -1185,7 +1190,7 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
#else
#define MAYBE_FetchNullIpAddressFromWorker FetchNullIpAddressFromWorker
#endif
-IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_P(PrivateNetworkAccessWithFeatureEnabledWorkerBrowserTest,
MAYBE_FetchNullIpAddressFromWorker) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
@@ -1211,9 +1216,10 @@ IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
}));
}
-INSTANTIATE_TEST_SUITE_P(,
- LocalNetworkAccessWithFeatureEnabledWorkerBrowserTest,
- testing::ValuesIn(kIsWarningOnlyTestData));
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ PrivateNetworkAccessWithFeatureEnabledWorkerBrowserTest,
+ testing::ValuesIn(kIsWarningOnlyTestData));
// ====================
// SPECIAL SCHEME TESTS
@@ -1224,10 +1230,10 @@ INSTANTIATE_TEST_SUITE_P(,
// an IP address space must be made up for them.
// This test verifies that the chrome-untrusted:// scheme is considered local
-// for the purpose of Local Network Access computations.
+// for the purpose of Private Network Access computations.
// TODO(crbug.com/1244544): The NTP no longer loads a chrome-untrusted://
// iframe in all cases. Find another way to test the chrome-untrusted:// scheme.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
DISABLED_SpecialSchemeChromeUntrusted) {
// The only way to have a page with a loaded chrome-untrusted:// url without
// relying on platform specific or components features, is to use the
@@ -1244,9 +1250,9 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
GURL fetch_url = LocalNonSecureWithCrossOriginCors(*server);
- // TODO(crbug.com/591068): The chrome-untrusted:// page should be kLoopback,
- // and not require a Local Network Access CORS preflight. However we have
- // not yet implemented the CORS preflight mechanism, and fixing the underlying
+ // TODO(crbug.com/591068): The chrome-untrusted:// page should be kLocal, and
+ // not require a Private Network Access CORS preflight. However we have not
+ // yet implemented the CORS preflight mechanism, and fixing the underlying
// issue will not change the test result. Once CORS preflight is implemented,
// review this test and delete this comment.
// Note: CSP is blocking javascript eval, unless we run it in an isolated
@@ -1257,8 +1263,8 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
}
// This test verifies that the devtools:// scheme is considered local for the
-// purpose of Local Network Access.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+// purpose of Private Network Access.
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
SpecialSchemeDevtools) {
EXPECT_TRUE(content::NavigateToURL(
web_contents(), GURL("devtools://devtools/bundled/devtools_app.html")));
@@ -1269,8 +1275,8 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
GURL fetch_url = LocalNonSecureWithCrossOriginCors(*server);
- // TODO(crbug.com/591068): The devtools:// page should be kLoopback, and not
- // require a Local Network Access CORS preflight. However we have not yet
+ // TODO(crbug.com/591068): The devtools:// page should be kLocal, and not
+ // require a Private Network Access CORS preflight. However we have not yet
// implemented the CORS preflight mechanism, and fixing the underlying issue
// will not change the test result. Once CORS preflight is implemented, review
// this test and delete this comment.
@@ -1278,8 +1284,8 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
}
// This test verifies that the chrome-search:// scheme is considered local for
-// the purpose of Local Network Access.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+// the purpose of Private Network Access.
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
SpecialSchemeChromeSearch) {
EXPECT_TRUE(content::NavigateToURL(
web_contents(), GURL("chrome-search://most-visited/title.html")));
@@ -1290,20 +1296,21 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
GURL fetch_url = LocalNonSecureWithCrossOriginCors(*server);
- // TODO(crbug.com/591068): The chrome-search:// page should be kLoopback, and
- // not require a Local Network Access CORS preflight. However we have not
- // yet implemented the CORS preflight mechanism, and fixing the underlying
- // issue will not change the test result. Once CORS preflight is implemented,
- // review this test and delete this comment. Note: CSP is blocking javascript
- // eval, unless we run it in an isolated world.
+ // TODO(crbug.com/591068): The chrome-search:// page should be kLocal, and not
+ // require a Private Network Access CORS preflight. However we have not yet
+ // implemented the CORS preflight mechanism, and fixing the underlying issue
+ // will not change the test result. Once CORS preflight is implemented, review
+ // this test and delete this comment.
+ // Note: CSP is blocking javascript eval, unless we run it in an isolated
+ // world.
EXPECT_EQ(true, content::EvalJs(web_contents(), FetchScript(fetch_url),
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
content::ISOLATED_WORLD_ID_CONTENT_END));
}
// This test verifies that the chrome-extension:// scheme is considered local
-// for the purpose of Local Network Access.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+// for the purpose of Private Network Access.
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
SpecialSchemeChromeExtension) {
base::ScopedAllowBlockingForTesting allow_blocking;
extensions::ScopedInstallVerifierBypassForTest install_verifier_bypass;
@@ -1348,9 +1355,9 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();
GURL fetch_url = LocalNonSecureWithCrossOriginCors(*server);
- // TODO(crbug.com/591068): The chrome-extension:// page should be kLoopback,
- // and not require a Local Network Access CORS preflight. However we have
- // not yet implemented the CORS preflight mechanism, and fixing the underlying
+ // TODO(crbug.com/591068): The chrome-extension:// page should be kLocal, and
+ // not require a Private Network Access CORS preflight. However we have not
+ // yet implemented the CORS preflight mechanism, and fixing the underlying
// issue will not change the test result. Once CORS preflight is implemented,
// review this test and delete this comment.
// Note: CSP is blocking javascript eval, unless we run it in an isolated
@@ -1361,8 +1368,8 @@ IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
}
// This test verifies that the chrome-distiller:// scheme is considered public
-// for the purpose of Local Network Access.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWithFeatureEnabledBrowserTest,
+// for the purpose of Private Network Access.
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
SpecialSchemeChromeDistiller) {
// Load the base page to be distilled. Note that HTTPS has to be used
// otherwise the page won't be distillable.
@@ -1446,31 +1453,31 @@ class NetErrorInterceptor final {
const content::URLLoaderInterceptor interceptor_;
};
-class LocalNetworkAccessAutoReloadBrowserTest
- : public LocalNetworkAccessBrowserTestBase {
+class PrivateNetworkAccessAutoReloadBrowserTest
+ : public PrivateNetworkAccessBrowserTestBase {
public:
- LocalNetworkAccessAutoReloadBrowserTest()
- : LocalNetworkAccessBrowserTestBase(
+ PrivateNetworkAccessAutoReloadBrowserTest()
+ : PrivateNetworkAccessBrowserTestBase(
{
features::kBlockInsecurePrivateNetworkRequests,
- features::kBlockInsecurePrivateNetworkRequestsForNavigations,
features::kBlockInsecurePrivateNetworkRequestsDeprecationTrial,
+ features::kPrivateNetworkAccessForIframes,
},
{}) {}
void SetUpOnMainThread() override {
- LocalNetworkAccessBrowserTestBase::SetUpOnMainThread();
+ PrivateNetworkAccessBrowserTestBase::SetUpOnMainThread();
error_page::NetErrorAutoReloader::CreateForWebContents(web_contents());
}
};
-// This test verifies that when a document in the `loopback` address space fails
-// to load due to a transient network error, it is auto-reloaded a short while
-// later and that fetch is not blocked as a local network request.
+// This test verifies that when a document in the `local` address space fails to
+// load due to a transient network error, it is auto-reloaded a short while
+// later and that fetch is not blocked as a private network request.
//
// TODO(crbug.com/1326341): Test is flaky.
-IN_PROC_BROWSER_TEST_F(LocalNetworkAccessAutoReloadBrowserTest,
+IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessAutoReloadBrowserTest,
DISABLED_AutoReloadWorks) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url = embedded_test_server()->GetURL("/defaultresponse");
diff --git a/chromium/chrome/browser/net/profile_network_context_service.cc b/chromium/chrome/browser/net/profile_network_context_service.cc
index 2795ff21d23..fd92bb9a1f1 100644
--- a/chromium/chrome/browser/net/profile_network_context_service.cc
+++ b/chromium/chrome/browser/net/profile_network_context_service.cc
@@ -29,6 +29,7 @@
#include "chrome/browser/domain_reliability/service_factory.h"
#include "chrome/browser/first_party_sets/first_party_sets_policy_service.h"
#include "chrome/browser/first_party_sets/first_party_sets_policy_service_factory.h"
+#include "chrome/browser/ip_protection/ip_protection_config_provider.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
#include "chrome/browser/profiles/profile.h"
@@ -43,11 +44,13 @@
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
+#include "components/content_settings/core/common/pref_names.h"
#include "components/embedder_support/pref_names.h"
#include "components/embedder_support/switches.h"
#include "components/language/core/browser/language_prefs.h"
#include "components/language/core/browser/pref_names.h"
#include "components/metrics/metrics_pref_names.h"
+#include "components/permissions/features.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
@@ -75,10 +78,6 @@
#include "services/network/public/mojom/network_service.mojom.h"
#include "third_party/blink/public/common/features.h"
-#if BUILDFLAG(IS_ANDROID)
-#include "base/android/remove_stale_data.h"
-#endif
-
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/certificate_provider/certificate_provider.h"
#include "chrome/browser/certificate_provider/certificate_provider_service.h"
@@ -211,9 +210,9 @@ void UpdateAntiAbuseSettings(Profile* profile) {
}
void UpdateCookieSettings(Profile* profile) {
- ContentSettingsForOneType settings;
- HostContentSettingsMapFactory::GetForProfile(profile)->GetSettingsForOneType(
- ContentSettingsType::COOKIES, &settings);
+ ContentSettingsForOneType settings =
+ HostContentSettingsMapFactory::GetForProfile(profile)
+ ->GetSettingsForOneType(ContentSettingsType::COOKIES);
profile->ForEachLoadedStoragePartition(base::BindRepeating(
[](ContentSettingsForOneType settings,
content::StoragePartition* storage_partition) {
@@ -224,9 +223,9 @@ void UpdateCookieSettings(Profile* profile) {
}
void UpdateLegacyCookieSettings(Profile* profile) {
- ContentSettingsForOneType settings;
- HostContentSettingsMapFactory::GetForProfile(profile)->GetSettingsForOneType(
- ContentSettingsType::LEGACY_COOKIE_ACCESS, &settings);
+ ContentSettingsForOneType settings =
+ HostContentSettingsMapFactory::GetForProfile(profile)
+ ->GetSettingsForOneType(ContentSettingsType::LEGACY_COOKIE_ACCESS);
profile->ForEachLoadedStoragePartition(base::BindRepeating(
[](ContentSettingsForOneType settings,
content::StoragePartition* storage_partition) {
@@ -236,11 +235,54 @@ void UpdateLegacyCookieSettings(Profile* profile) {
settings));
}
+void Update3pcdSettings(Profile* profile) {
+ ContentSettingsForOneType settings =
+ HostContentSettingsMapFactory::GetForProfile(profile)
+ ->GetSettingsForOneType(ContentSettingsType::TPCD_SUPPORT);
+ profile->ForEachLoadedStoragePartition(base::BindRepeating(
+ [](ContentSettingsForOneType settings,
+ content::StoragePartition* storage_partition) {
+ storage_partition->GetCookieManagerForBrowserProcess()
+ ->SetContentSettingsFor3pcd(settings);
+ },
+ settings));
+}
+
+void Update3pcdMetadataGrantsSettings(Profile* profile) {
+ ContentSettingsForOneType settings =
+ HostContentSettingsMapFactory::GetForProfile(profile)
+ ->GetSettingsForOneType(ContentSettingsType::TPCD_METADATA_GRANTS);
+ profile->ForEachLoadedStoragePartition(base::BindRepeating(
+ [](ContentSettingsForOneType settings,
+ content::StoragePartition* storage_partition) {
+ storage_partition->GetCookieManagerForBrowserProcess()
+ ->SetContentSettingsFor3pcdMetadataGrants(settings);
+ },
+ settings));
+}
+
+// `kPermissionStorageAccessAPI` enables feature: Storage Access API with
+// Prompts (https://chromestatus.com/feature/5085655327047680). StorageAccessAPI
+// is considered enabled when either feature is enabled (by different field
+// trial studies).
+bool StorageAccessAPIEnabled() {
+ return base::FeatureList::IsEnabled(blink::features::kStorageAccessAPI) ||
+ base::FeatureList::IsEnabled(
+ permissions::features::kPermissionStorageAccessAPI);
+}
+
+// TODO(crbug.com/1385156): Separate the two flags entirely.
+bool TopLevelStorageAccessAPIEnabled() {
+ return base::FeatureList::IsEnabled(blink::features::kStorageAccessAPI) &&
+ base::FeatureList::IsEnabled(
+ blink::features::kStorageAccessAPIForOriginExtension);
+}
+
void UpdateStorageAccessSettings(Profile* profile) {
- if (base::FeatureList::IsEnabled(blink::features::kStorageAccessAPI)) {
- ContentSettingsForOneType settings;
- HostContentSettingsMapFactory::GetForProfile(profile)
- ->GetSettingsForOneType(ContentSettingsType::STORAGE_ACCESS, &settings);
+ if (StorageAccessAPIEnabled()) {
+ ContentSettingsForOneType settings =
+ HostContentSettingsMapFactory::GetForProfile(profile)
+ ->GetSettingsForOneType(ContentSettingsType::STORAGE_ACCESS);
profile->ForEachLoadedStoragePartition(base::BindRepeating(
[](ContentSettingsForOneType settings,
@@ -253,18 +295,14 @@ void UpdateStorageAccessSettings(Profile* profile) {
}
void UpdateAllStorageAccessSettings(Profile* profile) {
- // TODO(crbug.com/1385156): Switch to an independent feature flag.
- if (base::FeatureList::IsEnabled(blink::features::kStorageAccessAPI) &&
- base::FeatureList::IsEnabled(
- blink::features::kStorageAccessAPIForOriginExtension)) {
- ContentSettingsForOneType top_level_settings;
- HostContentSettingsMapFactory::GetForProfile(profile)
- ->GetSettingsForOneType(ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS,
- &top_level_settings);
- ContentSettingsForOneType storage_access_settings;
- HostContentSettingsMapFactory::GetForProfile(profile)
- ->GetSettingsForOneType(ContentSettingsType::STORAGE_ACCESS,
- &storage_access_settings);
+ if (TopLevelStorageAccessAPIEnabled()) {
+ ContentSettingsForOneType top_level_settings =
+ HostContentSettingsMapFactory::GetForProfile(profile)
+ ->GetSettingsForOneType(
+ ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS);
+ ContentSettingsForOneType storage_access_settings =
+ HostContentSettingsMapFactory::GetForProfile(profile)
+ ->GetSettingsForOneType(ContentSettingsType::STORAGE_ACCESS);
profile->ForEachLoadedStoragePartition(base::BindRepeating(
[](ContentSettingsForOneType storage_access_settings,
@@ -338,6 +376,11 @@ ProfileNetworkContextService::ProfileNetworkContextService(Profile* profile)
base::BindRepeating(&ProfileNetworkContextService::
UpdateCorsNonWildcardRequestHeadersSupport,
base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kBlockTruncatedCookies,
+ base::BindRepeating(
+ &ProfileNetworkContextService::OnTruncatedCookieBlockingChanged,
+ base::Unretained(this)));
}
ProfileNetworkContextService::~ProfileNetworkContextService() = default;
@@ -441,6 +484,22 @@ void ProfileNetworkContextService::OnThirdPartyCookieBlockingChanged(
block_third_party_cookies));
}
+void ProfileNetworkContextService::OnTruncatedCookieBlockingChanged() {
+ const bool block_truncated_cookies =
+ profile_->GetPrefs()->GetBoolean(prefs::kBlockTruncatedCookies);
+
+ profile_->ForEachLoadedStoragePartition(base::BindRepeating(
+ [](bool block_truncated_cookies,
+ content::StoragePartition* storage_partition) {
+ // Update the main CookieManager's CookieSettings object to block
+ // truncated cookies, and since this is shared with all of the
+ // RestrictedCookieManager instances, those will get the change as well.
+ storage_partition->GetCookieManagerForBrowserProcess()
+ ->BlockTruncatedCookies(block_truncated_cookies);
+ },
+ block_truncated_cookies));
+}
+
void ProfileNetworkContextService::OnFirstPartySetsEnabledChanged(
bool enabled) {
// Update all FPS Access Delegates on the FPS service to be `enabled`.
@@ -566,9 +625,9 @@ ProfileNetworkContextService::CreateCookieManagerParams(
cookie_settings.ShouldBlockThirdPartyCookies();
// This allows cookies to be sent on https requests from chrome:// pages,
// ignoring SameSite attribute rules. For example, this is needed for browser
- // UI to interact with SameSite cookies on accounts.google.com, which are used
- // for logging into Cloud Print from chrome://print, for displaying a list
- // of available accounts on the NTP (chrome://new-tab-page), etc.
+ // UI to interact with SameSite cookies on accounts.google.com, which is used
+ // for displaying a list of available accounts on the NTP
+ // (chrome://new-tab-page), etc.
out->secure_origin_cookies_allowed_schemes.push_back(
content::kChromeUIScheme);
#if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -579,44 +638,57 @@ ProfileNetworkContextService::CreateCookieManagerParams(
extensions::kExtensionScheme);
#endif
- ContentSettingsForOneType settings;
HostContentSettingsMap* host_content_settings_map =
HostContentSettingsMapFactory::GetForProfile(profile);
- host_content_settings_map->GetSettingsForOneType(ContentSettingsType::COOKIES,
- &settings);
- out->settings = std::move(settings);
-
- ContentSettingsForOneType settings_for_legacy_cookie_access;
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::LEGACY_COOKIE_ACCESS,
- &settings_for_legacy_cookie_access);
+ out->settings = host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::COOKIES);
+
out->settings_for_legacy_cookie_access =
- std::move(settings_for_legacy_cookie_access);
+ host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::LEGACY_COOKIE_ACCESS);
+
+ out->settings_for_3pcd = host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::TPCD_SUPPORT);
- ContentSettingsForOneType settings_for_storage_access;
- if (base::FeatureList::IsEnabled(blink::features::kStorageAccessAPI)) {
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::STORAGE_ACCESS, &settings_for_storage_access);
+ out->settings_for_3pcd_metadata_grants =
+ host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::TPCD_METADATA_GRANTS);
+
+ if (StorageAccessAPIEnabled()) {
+ out->settings_for_storage_access =
+ host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::STORAGE_ACCESS);
}
- out->settings_for_storage_access = std::move(settings_for_storage_access);
- ContentSettingsForOneType settings_for_top_level_storage_access;
// TODO(crbug.com/1385156): Separate the two flags entirely.
- if (base::FeatureList::IsEnabled(blink::features::kStorageAccessAPI) &&
- base::FeatureList::IsEnabled(
- blink::features::kStorageAccessAPIForOriginExtension)) {
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS,
- &settings_for_top_level_storage_access);
+ if (TopLevelStorageAccessAPIEnabled()) {
+ out->settings_for_top_level_storage_access =
+ host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS);
}
- out->settings_for_top_level_storage_access =
- std::move(settings_for_top_level_storage_access);
out->cookie_access_delegate_type =
network::mojom::CookieAccessDelegateType::USE_CONTENT_SETTINGS;
+
+ out->block_truncated_cookies =
+ profile->GetPrefs()->GetBoolean(prefs::kBlockTruncatedCookies);
+
return out;
}
+void ProfileNetworkContextService::FlushCachedClientCertIfNeeded(
+ const net::HostPortPair& host,
+ const scoped_refptr<net::X509Certificate>& certificate) {
+ profile_->ForEachLoadedStoragePartition(base::BindRepeating(
+ [](const net::HostPortPair& host,
+ const scoped_refptr<net::X509Certificate>& certificate,
+ content::StoragePartition* storage_partition) {
+ storage_partition->GetNetworkContext()->FlushCachedClientCertIfNeeded(
+ host, certificate);
+ },
+ host, certificate));
+}
+
void ProfileNetworkContextService::FlushProxyConfigMonitorForTesting() {
proxy_config_monitor_.FlushForTesting();
}
@@ -734,6 +806,23 @@ bool GetHttpCacheBackendResetParam(PrefService* local_state) {
current_field_trial_status +=
(field_trial ? field_trial->group_name() : "None");
+ // For the ongoing HTTP Cache keying experiments, if a flag indicates that the
+ // user is in an experiment group, modify `current_field_trial_status` to
+ // ensure that the cache gets cleared. If the user is not a part of the
+ // experiment, don't make any changes so as not to invalidate the existing
+ // cache.
+ if (base::FeatureList::IsEnabled(
+ net::features::kEnableCrossSiteFlagNetworkIsolationKey)) {
+ current_field_trial_status += " CrossSiteFlagNIK";
+ } else if (base::FeatureList::IsEnabled(
+ net::features::
+ kEnableFrameSiteSharedOpaqueNetworkIsolationKey)) {
+ current_field_trial_status += " FrameSiteSharedOpaqueNIK";
+ } else if (base::FeatureList::IsEnabled(
+ net::features::kHttpCacheKeyingExperimentControlGroup)) {
+ current_field_trial_status += " 2023ExperimentControlGroup";
+ }
+
std::string previous_field_trial_status =
local_state->GetString(kHttpCacheFinchExperimentGroups);
local_state->SetString(kHttpCacheFinchExperimentGroups,
@@ -774,6 +863,8 @@ void ProfileNetworkContextService::ConfigureNetworkContextParamsInternal(
g_browser_process->system_network_context_manager()
->ConfigureDefaultNetworkContextParams(network_context_params);
+ network_context_params->enable_zstd =
+ base::FeatureList::IsEnabled(net::features::kZstdContentEncoding);
network_context_params->accept_language = ComputeAcceptLanguage();
network_context_params->enable_referrers = enable_referrers_.GetValue();
@@ -813,8 +904,6 @@ void ProfileNetworkContextService::ConfigureNetworkContextParamsInternal(
local_state->GetFilePath(prefs::kDiskCacheDir);
if (!disk_cache_dir.empty())
base_cache_path = disk_cache_dir.Append(base_cache_path.BaseName());
- network_context_params->http_cache_directory =
- base_cache_path.Append(chrome::kCacheDirname);
const int disk_cache_size = local_state->GetInteger(prefs::kDiskCacheSize);
network_context_params->http_cache_max_size = disk_cache_size;
network_context_params->shared_dictionary_cache_max_size = disk_cache_size;
@@ -822,22 +911,14 @@ void ProfileNetworkContextService::ConfigureNetworkContextParamsInternal(
network_context_params->file_paths =
::network::mojom::NetworkContextFilePaths::New();
+ network_context_params->file_paths->http_cache_directory =
+ base_cache_path.Append(chrome::kCacheDirname);
network_context_params->file_paths->data_directory =
path.Append(chrome::kNetworkDataDirname);
network_context_params->file_paths->unsandboxed_data_path = path;
network_context_params->file_paths->trigger_migration =
base::FeatureList::IsEnabled(features::kTriggerNetworkDataMigration);
-#if BUILDFLAG(IS_ANDROID)
- // On Android the `data_directory` was used by some wrong builds instead of
- // `unsandboxed_data_path`. Cleaning it up. See crbug.com/1331809.
- // The `trigger_migration` has always been false and will remain to be such
- // on Android, hence not checking for it.
- DCHECK(!network_context_params->file_paths->trigger_migration);
- base::android::RemoveStaleDataDirectory(
- network_context_params->file_paths->data_directory.path());
-#endif // BUILDFLAG(IS_ANDROID)
-
// Currently this just contains HttpServerProperties, but that will likely
// change.
network_context_params->file_paths->http_server_properties_file_name =
@@ -1033,6 +1114,14 @@ void ProfileNetworkContextService::ConfigureNetworkContextParamsInternal(
network_context_params->acam_preflight_spec_conformant =
profile_->GetPrefs()->GetBoolean(
prefs::kAccessControlAllowMethodsInCORSPreflightSpecConformant);
+
+ IpProtectionConfigProvider* ip_protection_auth_token_getter =
+ IpProtectionConfigProvider::Get(profile_);
+ if (ip_protection_auth_token_getter) {
+ ip_protection_auth_token_getter->AddReceiver(
+ network_context_params->ip_protection_config_getter
+ .InitWithNewPipeAndPassReceiver());
+ }
}
base::FilePath ProfileNetworkContextService::GetPartitionPath(
@@ -1057,6 +1146,12 @@ void ProfileNetworkContextService::OnContentSettingChanged(
case ContentSettingsType::LEGACY_COOKIE_ACCESS:
UpdateLegacyCookieSettings(profile_);
break;
+ case ContentSettingsType::TPCD_SUPPORT:
+ Update3pcdSettings(profile_);
+ break;
+ case ContentSettingsType::TPCD_METADATA_GRANTS:
+ Update3pcdMetadataGrantsSettings(profile_);
+ break;
case ContentSettingsType::STORAGE_ACCESS:
UpdateStorageAccessSettings(profile_);
break;
@@ -1067,6 +1162,8 @@ void ProfileNetworkContextService::OnContentSettingChanged(
UpdateAntiAbuseSettings(profile_);
UpdateCookieSettings(profile_);
UpdateLegacyCookieSettings(profile_);
+ Update3pcdSettings(profile_);
+ Update3pcdMetadataGrantsSettings(profile_);
UpdateAllStorageAccessSettings(profile_);
break;
default:
diff --git a/chromium/chrome/browser/net/profile_network_context_service.h b/chromium/chrome/browser/net/profile_network_context_service.h
index 548e70ba265..bd850887bc0 100644
--- a/chromium/chrome/browser/net/profile_network_context_service.h
+++ b/chromium/chrome/browser/net/profile_network_context_service.h
@@ -90,6 +90,12 @@ class ProfileNetworkContextService
Profile* profile,
const content_settings::CookieSettings& cookie_settings);
+ // Flushes a cached client certificate preference for |host| if |certificate|
+ // doesn't match the cached certificate.
+ void FlushCachedClientCertIfNeeded(
+ const net::HostPortPair& host,
+ const scoped_refptr<net::X509Certificate>& certificate);
+
// Flushes all pending proxy configuration changes.
void FlushProxyConfigMonitorForTesting();
@@ -145,6 +151,8 @@ class ProfileNetworkContextService
void UpdateCorsNonWildcardRequestHeadersSupport();
+ void OnTruncatedCookieBlockingChanged();
+
// Creates parameters for the NetworkContext. Use |in_memory| instead of
// |profile_->IsOffTheRecord()| because sometimes normal profiles want off the
// record partitions (e.g. for webview tag).
diff --git a/chromium/chrome/browser/net/profile_network_context_service_browsertest.cc b/chromium/chrome/browser/net/profile_network_context_service_browsertest.cc
index 9500566fff9..86c8bbaa809 100644
--- a/chromium/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chromium/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -264,14 +264,13 @@ void CheckCacheResetStatus(base::HistogramTester* histograms, bool reset) {
class ProfileNetworkContextServiceCacheSameBrowsertest
: public ProfileNetworkContextServiceBrowsertest {
public:
- ProfileNetworkContextServiceCacheSameBrowsertest() = default;
- ~ProfileNetworkContextServiceCacheSameBrowsertest() override = default;
-
- void SetUp() override {
- scoped_feature_list_.InitAndDisableFeature(
- net::features::kSplitCacheByNetworkIsolationKey);
- ProfileNetworkContextServiceBrowsertest::SetUp();
+ ProfileNetworkContextServiceCacheSameBrowsertest() {
+ // Override features that are enabled via the fieldtrial testing config.
+ scoped_feature_list_.InitWithFeatures(
+ {}, {net::features::kSplitCacheByNetworkIsolationKey,
+ net::features::kEnableCrossSiteFlagNetworkIsolationKey});
}
+ ~ProfileNetworkContextServiceCacheSameBrowsertest() override = default;
base::HistogramTester histograms_;
@@ -408,6 +407,125 @@ IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceCacheCredentialsBrowserTest,
"None None None scoped_feature_list_trial_group");
}
+// This subclass adds tests for the 2023 HTTP Cache keying experiment flags.
+enum class HttpCache2023ExperimentTestCase {
+ kDoublePlusBitExperimentGroup,
+ kTripleKeyedSharedOpaqueExperimentGroup,
+ kControlGroup,
+};
+
+class ProfileNetworkContextServiceCacheKeySchemeExperimentBrowserTest
+ : public ProfileNetworkContextServiceBrowsertest,
+ public testing::WithParamInterface<HttpCache2023ExperimentTestCase> {
+ public:
+ ProfileNetworkContextServiceCacheKeySchemeExperimentBrowserTest() {
+ // Override any configured experiments for the
+ // SplitCacheByNetworkIsolationKey feature.
+ always_enabled_feature_list_.InitAndEnableFeatureWithParameters(
+ net::features::kSplitCacheByNetworkIsolationKey, {});
+
+ switch (GetParam()) {
+ case HttpCache2023ExperimentTestCase::kDoublePlusBitExperimentGroup:
+ test_feature_list_.InitWithFeatures(
+ {net::features::kEnableCrossSiteFlagNetworkIsolationKey},
+ {net::features::kEnableFrameSiteSharedOpaqueNetworkIsolationKey,
+ net::features::kHttpCacheKeyingExperimentControlGroup});
+ break;
+ case HttpCache2023ExperimentTestCase::
+ kTripleKeyedSharedOpaqueExperimentGroup:
+ test_feature_list_.InitWithFeatures(
+ {net::features::kEnableFrameSiteSharedOpaqueNetworkIsolationKey},
+ {net::features::kEnableCrossSiteFlagNetworkIsolationKey,
+ net::features::kHttpCacheKeyingExperimentControlGroup});
+ break;
+ case HttpCache2023ExperimentTestCase::kControlGroup:
+ test_feature_list_.InitWithFeatures(
+ {net::features::kHttpCacheKeyingExperimentControlGroup},
+ {net::features::kEnableCrossSiteFlagNetworkIsolationKey,
+ net::features::kEnableFrameSiteSharedOpaqueNetworkIsolationKey});
+ break;
+ }
+ }
+
+ const char* GetExperimentString() {
+ switch (GetParam()) {
+ case HttpCache2023ExperimentTestCase::kDoublePlusBitExperimentGroup:
+ return "CrossSiteFlagNIK";
+ case HttpCache2023ExperimentTestCase::
+ kTripleKeyedSharedOpaqueExperimentGroup:
+ return "FrameSiteSharedOpaqueNIK";
+ case HttpCache2023ExperimentTestCase::kControlGroup:
+ return "2023ExperimentControlGroup";
+ }
+ }
+ ~ProfileNetworkContextServiceCacheKeySchemeExperimentBrowserTest() override =
+ default;
+
+ base::HistogramTester histograms_;
+
+ private:
+ base::test::ScopedFeatureList always_enabled_feature_list_;
+ base::test::ScopedFeatureList test_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ ProfileNetworkContextServiceCacheKeySchemeExperimentBrowserTest,
+ testing::ValuesIn(
+ {HttpCache2023ExperimentTestCase::kDoublePlusBitExperimentGroup,
+ HttpCache2023ExperimentTestCase::
+ kTripleKeyedSharedOpaqueExperimentGroup,
+ HttpCache2023ExperimentTestCase::kControlGroup}),
+ [](const testing::TestParamInfo<HttpCache2023ExperimentTestCase>& info) {
+ switch (info.param) {
+ case (HttpCache2023ExperimentTestCase::kDoublePlusBitExperimentGroup):
+ return "DoublePlusBitExperimentGroup";
+ case (HttpCache2023ExperimentTestCase::
+ kTripleKeyedSharedOpaqueExperimentGroup):
+ return "TripleKeyedSharedOpaqueExperimentGroup";
+ case (HttpCache2023ExperimentTestCase::kControlGroup):
+ return "ControlGroup";
+ }
+ });
+
+IN_PROC_BROWSER_TEST_P(
+ ProfileNetworkContextServiceCacheKeySchemeExperimentBrowserTest,
+ PRE_TestCacheResetParameter) {
+ NavigateToCreateHttpCache();
+ CheckCacheResetStatus(&histograms_, false);
+
+ // At this point, we have already called the initialization.
+ // Verify that we have the correct values in the local_state.
+ PrefService* local_state = g_browser_process->local_state();
+ DCHECK_EQ(
+ local_state->GetString(
+ "profile_network_context_service.http_cache_finch_experiment_groups"),
+ base::StrCat({"scoped_feature_list_trial_group None None None ",
+ GetExperimentString()}));
+ // Set the local state for the next test.
+ local_state->SetString(
+ "profile_network_context_service.http_cache_finch_experiment_groups",
+ "None None None None");
+}
+
+// The second time we load we know the state, which was "None None None None"
+// for the previous test, so we should see a reset being in an experiment.
+IN_PROC_BROWSER_TEST_P(
+ ProfileNetworkContextServiceCacheKeySchemeExperimentBrowserTest,
+ TestCacheResetParameter) {
+ NavigateToCreateHttpCache();
+ CheckCacheResetStatus(&histograms_, true);
+
+ // At this point, we have already called the initialization once.
+ // Verify that we have the correct values in the local_state.
+ PrefService* local_state = g_browser_process->local_state();
+ DCHECK_EQ(
+ local_state->GetString(
+ "profile_network_context_service.http_cache_finch_experiment_groups"),
+ base::StrCat({"scoped_feature_list_trial_group None None None ",
+ GetExperimentString()}));
+}
+
class AmbientAuthenticationTestWithPolicy : public policy::PolicyTest {
public:
AmbientAuthenticationTestWithPolicy() {
diff --git a/chromium/chrome/browser/net/profile_network_context_service_factory.cc b/chromium/chrome/browser/net/profile_network_context_service_factory.cc
index 6ac83fcc626..14ac2ce8b90 100644
--- a/chromium/chrome/browser/net/profile_network_context_service_factory.cc
+++ b/chromium/chrome/browser/net/profile_network_context_service_factory.cc
@@ -61,9 +61,11 @@ ProfileNetworkContextServiceFactory::ProfileNetworkContextServiceFactory()
ProfileNetworkContextServiceFactory::~ProfileNetworkContextServiceFactory() =
default;
-KeyedService* ProfileNetworkContextServiceFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+ProfileNetworkContextServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const {
- return new ProfileNetworkContextService(Profile::FromBrowserContext(profile));
+ return std::make_unique<ProfileNetworkContextService>(
+ Profile::FromBrowserContext(profile));
}
bool ProfileNetworkContextServiceFactory::ServiceIsNULLWhileTesting() const {
diff --git a/chromium/chrome/browser/net/profile_network_context_service_factory.h b/chromium/chrome/browser/net/profile_network_context_service_factory.h
index 0ab8e151eef..6348cde5b0e 100644
--- a/chromium/chrome/browser/net/profile_network_context_service_factory.h
+++ b/chromium/chrome/browser/net/profile_network_context_service_factory.h
@@ -37,7 +37,7 @@ class ProfileNetworkContextServiceFactory : public ProfileKeyedServiceFactory {
~ProfileNetworkContextServiceFactory() override;
// BrowserContextKeyedServiceFactory implementation:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
bool ServiceIsNULLWhileTesting() const override;
};
diff --git a/chromium/chrome/browser/net/proxy_config_monitor.cc b/chromium/chrome/browser/net/proxy_config_monitor.cc
index 6dc5622f508..c9281f7abfb 100644
--- a/chromium/chrome/browser/net/proxy_config_monitor.cc
+++ b/chromium/chrome/browser/net/proxy_config_monitor.cc
@@ -15,7 +15,6 @@
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/network_context.mojom.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -86,9 +85,7 @@ void ProxyConfigMonitor::AddToNetworkContextParams(
proxy_config_client.InitWithNewPipeAndPassReceiver();
proxy_config_client_set_.Add(std::move(proxy_config_client));
- if (!base::FeatureList::IsEnabled(
- network::features::kLessChattyNetworkService) ||
- proxy_config_service_->UsesPolling()) {
+ if (proxy_config_service_->UsesPolling()) {
poller_receiver_set_.Add(this,
network_context_params->proxy_config_poller_client
.InitWithNewPipeAndPassReceiver());
diff --git a/chromium/chrome/browser/net/sandboxed_network_change_notifier_win_browsertest.cc b/chromium/chrome/browser/net/sandboxed_network_change_notifier_win_browsertest.cc
new file mode 100644
index 00000000000..173ea4d1416
--- /dev/null
+++ b/chromium/chrome/browser/net/sandboxed_network_change_notifier_win_browsertest.cc
@@ -0,0 +1,203 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/network/public/mojom/network_service.mojom.h"
+
+#include <windows.h> // Must be in front of other Windows header files.
+
+#include <initguid.h> // Must be in front of devpkey.h.
+
+// Must be in front of Windows includes because they define LogSeverity and this
+// breaks gmock.
+#include "testing/gmock/include/gmock/gmock.h"
+
+#include <cfgmgr32.h>
+#include <devpkey.h>
+#include <newdev.h>
+#include <ntddser.h>
+#include <setupapi.h>
+#include <shlobj.h>
+#include <stdint.h>
+
+#include "base/base_paths_win.h"
+#include "base/files/file_path.h"
+#include "base/functional/callback_helpers.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/win/scoped_devinfo.h"
+#include "base/win/win_util.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/common/content_features.h"
+#include "content/public/test/browser_test.h"
+#include "sandbox/policy/features.h"
+#include "services/network/public/mojom/network_change_manager.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace content {
+
+namespace {
+
+absl::optional<base::ScopedClosureRunner> InstallAdapter(
+ const base::FilePath& inf,
+ const std::wstring hwid) {
+ GUID guid;
+ wchar_t className[MAX_CLASS_NAME_LEN];
+
+ if (!::SetupDiGetINFClass(inf.value().c_str(), &guid, className,
+ MAX_CLASS_NAME_LEN, 0)) {
+ PLOG(ERROR) << "Unable to create SetupDiGetINFClass.";
+ return absl::nullopt;
+ }
+
+ base::win::ScopedDevInfo dev_info(
+ ::SetupDiCreateDeviceInfoList(&guid, nullptr));
+ if (!dev_info.is_valid()) {
+ PLOG(ERROR) << "Unable to call SetupDiCreateDeviceInfoList.";
+ return absl::nullopt;
+ }
+
+ SP_DEVINFO_DATA deviceInfoData = {};
+ deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ if (!::SetupDiCreateDeviceInfo(dev_info.get(), className, &guid, nullptr,
+ nullptr, DICD_GENERATE_ID, &deviceInfoData)) {
+ PLOG(ERROR) << "Unable to call SetupDiCreateDeviceInfo";
+ return absl::nullopt;
+ }
+
+ if (!::SetupDiSetDeviceRegistryProperty(
+ dev_info.get(), &deviceInfoData, SPDRP_HARDWAREID,
+ reinterpret_cast<const BYTE*>(hwid.c_str()),
+ (hwid.length() + 1) * sizeof(wchar_t))) {
+ PLOG(ERROR) << "Unable to call SetupDiSetDeviceRegistryProperty.";
+ return absl::nullopt;
+ }
+
+ if (!::SetupDiCallClassInstaller(DIF_REGISTERDEVICE, dev_info.get(),
+ &deviceInfoData)) {
+ PLOG(ERROR) << "Unable to call SetupDiCallClassInstaller.";
+ return absl::nullopt;
+ }
+
+ BOOL reboot_required = FALSE;
+ if (!::UpdateDriverForPlugAndPlayDevices(
+ nullptr, hwid.c_str(), inf.value().c_str(), 0, &reboot_required)) {
+ PLOG(ERROR) << "Unable to call UpdateDriverForPlugAndPlayDevices.";
+ return absl::nullopt;
+ }
+
+ return base::ScopedClosureRunner(base::BindOnce(
+ [](DEVINST devinst) { std::ignore = ::CM_Uninstall_DevNode(devinst, 0); },
+ deviceInfoData.DevInst));
+}
+
+class MockNetworkChangeManagerClient
+ : public network::mojom::NetworkChangeManagerClient {
+ public:
+ MockNetworkChangeManagerClient(
+ network::mojom::NetworkChangeManager* network_change_manager) {
+ mojo::PendingRemote<network::mojom::NetworkChangeManagerClient>
+ client_remote;
+ receiver_.Bind(client_remote.InitWithNewPipeAndPassReceiver());
+ network_change_manager->RequestNotifications(std::move(client_remote));
+ }
+
+ MockNetworkChangeManagerClient(const MockNetworkChangeManagerClient&) =
+ delete;
+ MockNetworkChangeManagerClient& operator=(
+ const MockNetworkChangeManagerClient&) = delete;
+
+ ~MockNetworkChangeManagerClient() override {}
+
+ // NetworkChangeManagerClient implementation:
+ MOCK_METHOD(void,
+ OnInitialConnectionType,
+ (network::mojom::ConnectionType type),
+ (override));
+ MOCK_METHOD(void,
+ OnNetworkChanged,
+ (network::mojom::ConnectionType type),
+ (override));
+
+ private:
+ mojo::Receiver<network::mojom::NetworkChangeManagerClient> receiver_{this};
+};
+
+} // namespace
+
+class SandboxedNetworkChangeNotifierBrowserTest
+ : public InProcessBrowserTest,
+ public ::testing::WithParamInterface</*sandboxed=*/bool> {
+ public:
+ SandboxedNetworkChangeNotifierBrowserTest() {
+ if (GetParam()) {
+ scoped_feature_list_.InitWithFeatures(
+ {sandbox::policy::features::kNetworkServiceSandbox},
+ {features::kNetworkServiceInProcess});
+ } else {
+ scoped_feature_list_.InitWithFeatures(
+ {}, {features::kNetworkServiceInProcess,
+ sandbox::policy::features::kNetworkServiceSandbox});
+ }
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Test that dynamically adds a new adapter to the host, and verifies that a
+// network change notification is sent from the network service to the browser
+// process.
+// The network service is able to see these network adapter changes, as it is
+// created with the LPAC "internetClient" capability. See
+// https://learn.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations
+IN_PROC_BROWSER_TEST_P(SandboxedNetworkChangeNotifierBrowserTest,
+ AddNetworkAdapter) {
+ if (!::IsUserAnAdmin()) {
+ GTEST_SKIP() << "This test requires running elevated.";
+ }
+ mojo::Remote<network::mojom::NetworkChangeManager> network_change_manager;
+ GetNetworkService()->GetNetworkChangeManager(
+ network_change_manager.BindNewPipeAndPassReceiver());
+ base::RunLoop run_loop;
+
+ ::testing::StrictMock<MockNetworkChangeManagerClient> mock(
+ network_change_manager.get());
+
+ ::testing::InSequence order;
+ // OnInitialConnectionType can be called with CONNECTION_UNKNOWN or
+ // CONNECTION_ETHERNET.
+ EXPECT_CALL(mock, OnInitialConnectionType(::testing::AnyOf(
+ network::mojom::ConnectionType::CONNECTION_UNKNOWN,
+ network::mojom::ConnectionType::CONNECTION_ETHERNET)));
+ // NetworkChangeManager sends two notifications, the first is always
+ // CONNECTION_NONE, followed by the actual ConnectionType. See
+ // `network_change_manager.mojom`.
+ EXPECT_CALL(
+ mock, OnNetworkChanged(network::mojom::ConnectionType::CONNECTION_NONE));
+ EXPECT_CALL(mock, OnNetworkChanged(
+ network::mojom::ConnectionType::CONNECTION_ETHERNET))
+ .WillOnce([&run_loop]() { run_loop.Quit(); });
+
+ // Install a new network card.
+ base::FilePath dir_windows;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_WINDOWS, &dir_windows));
+ auto inst = InstallAdapter(
+ dir_windows.AppendASCII("Inf").AppendASCII("netloop.inf"), L"*MSLOOP");
+ ASSERT_TRUE(inst);
+
+ run_loop.Run();
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+ SandboxedNetworkChangeNotifierBrowserTest,
+ ::testing::Bool(),
+ [](const auto& info) {
+ return info.param ? "Sandboxed" : "Unsandboxed";
+ });
+} // namespace content
diff --git a/chromium/chrome/browser/net/storage_test_utils.cc b/chromium/chrome/browser/net/storage_test_utils.cc
index 051e2e46b85..b5cf741adc2 100644
--- a/chromium/chrome/browser/net/storage_test_utils.cc
+++ b/chromium/chrome/browser/net/storage_test_utils.cc
@@ -44,7 +44,9 @@ std::string GetFrameContent(content::RenderFrameHost* frame) {
return content::EvalJs(frame, "document.body.textContent").ExtractString();
}
-void SetStorageForFrame(content::RenderFrameHost* frame, bool include_cookies) {
+void SetStorageForFrame(content::RenderFrameHost* frame,
+ bool include_cookies,
+ const base::Location& location) {
base::flat_map<std::string, bool> actual;
base::flat_map<std::string, bool> expected;
for (const auto& data_type : GetStorageTypesForFrame(include_cookies)) {
@@ -59,10 +61,12 @@ void SetStorageForFrame(content::RenderFrameHost* frame, bool include_cookies) {
expected[data_type] = true;
}
}
- EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected));
+ EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected))
+ << "(expected at " << location.ToString() << ")";
}
-void SetStorageForWorker(content::RenderFrameHost* frame) {
+void SetStorageForWorker(content::RenderFrameHost* frame,
+ const base::Location& location) {
base::flat_map<std::string, bool> actual;
base::flat_map<std::string, bool> expected;
for (const auto& data_type : kStorageTypesForWorker) {
@@ -70,15 +74,16 @@ void SetStorageForWorker(content::RenderFrameHost* frame) {
content::EvalJs(frame, "set" + data_type + "()").ExtractBool();
expected[data_type] = true;
}
- EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected));
+ EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected))
+ << "(expected at " << location.ToString() << ")";
}
void ExpectStorageForFrame(content::RenderFrameHost* frame,
- bool include_cookies,
- bool expected) {
+ bool expected,
+ const base::Location& location) {
base::flat_map<std::string, bool> actual;
base::flat_map<std::string, bool> expected_elts;
- for (const auto& data_type : GetStorageTypesForFrame(include_cookies)) {
+ for (const auto& data_type : GetStorageTypesForFrame(false)) {
actual[data_type] =
content::EvalJs(frame, "has" + data_type + "();").ExtractBool();
if (frame->GetLastCommittedOrigin() !=
@@ -90,10 +95,13 @@ void ExpectStorageForFrame(content::RenderFrameHost* frame,
expected_elts[data_type] = expected;
}
}
- EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected_elts));
+ EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected_elts))
+ << "(expected at " << location.ToString() << ")";
}
-void ExpectStorageForWorker(content::RenderFrameHost* frame, bool expected) {
+void ExpectStorageForWorker(content::RenderFrameHost* frame,
+ bool expected,
+ const base::Location& location) {
base::flat_map<std::string, bool> actual;
base::flat_map<std::string, bool> expected_elts;
for (const auto& data_type : kStorageTypesForWorker) {
@@ -101,10 +109,12 @@ void ExpectStorageForWorker(content::RenderFrameHost* frame, bool expected) {
content::EvalJs(frame, "has" + data_type + "();").ExtractBool();
expected_elts[data_type] = expected;
}
- EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected_elts));
+ EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected_elts))
+ << "(expected at " << location.ToString() << ")";
}
-void SetCrossTabInfoForFrame(content::RenderFrameHost* frame) {
+void SetCrossTabInfoForFrame(content::RenderFrameHost* frame,
+ const base::Location& location) {
base::flat_map<std::string, bool> actual;
base::flat_map<std::string, bool> expected;
for (const auto& data_type : kCrossTabCommunicationTypes) {
@@ -112,11 +122,13 @@ void SetCrossTabInfoForFrame(content::RenderFrameHost* frame) {
content::EvalJs(frame, "set" + data_type + "()").ExtractBool();
expected[data_type] = true;
}
- EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected));
+ EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected))
+ << "(expected at " << location.ToString() << ")";
}
void ExpectCrossTabInfoForFrame(content::RenderFrameHost* frame,
- bool expected) {
+ bool expected,
+ const base::Location& location) {
base::flat_map<std::string, bool> actual;
base::flat_map<std::string, bool> expected_elts;
for (const auto& data_type : kCrossTabCommunicationTypes) {
@@ -124,7 +136,8 @@ void ExpectCrossTabInfoForFrame(content::RenderFrameHost* frame,
content::EvalJs(frame, "has" + data_type + "();").ExtractBool();
expected_elts[data_type] = expected;
}
- EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected_elts));
+ EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected_elts))
+ << "(expected at " << location.ToString() << ")";
}
bool RequestAndCheckStorageAccessForFrame(content::RenderFrameHost* frame) {
diff --git a/chromium/chrome/browser/net/storage_test_utils.h b/chromium/chrome/browser/net/storage_test_utils.h
index 500dabb58a4..b1ce9481685 100644
--- a/chromium/chrome/browser/net/storage_test_utils.h
+++ b/chromium/chrome/browser/net/storage_test_utils.h
@@ -7,6 +7,8 @@
#include <string>
+#include "base/location.h"
+
class GURL;
namespace content {
@@ -20,17 +22,25 @@ std::string GetFrameContent(content::RenderFrameHost* frame);
// Helpers to set and check various types of storage on a given frame. Typically
// used on page like //chrome/test/data/browsing_data/site_data.html
-void SetStorageForFrame(content::RenderFrameHost* frame, bool include_cookies);
-void SetStorageForWorker(content::RenderFrameHost* frame);
+void SetStorageForFrame(content::RenderFrameHost* frame,
+ bool include_cookies,
+ const base::Location& location = FROM_HERE);
+void SetStorageForWorker(content::RenderFrameHost* frame,
+ const base::Location& location = FROM_HERE);
void ExpectStorageForFrame(content::RenderFrameHost* frame,
- bool include_cookies,
- bool expected);
-void ExpectStorageForWorker(content::RenderFrameHost* frame, bool expected);
+ bool expected,
+ const base::Location& location = FROM_HERE);
+void ExpectStorageForWorker(content::RenderFrameHost* frame,
+ bool expected,
+ const base::Location& location = FROM_HERE);
// Helpers to set and check various types of cross tab info for a given frame.
// Typically used on page like //chrome/test/data/browsing_data/site_data.html
-void SetCrossTabInfoForFrame(content::RenderFrameHost* frame);
-void ExpectCrossTabInfoForFrame(content::RenderFrameHost* frame, bool expected);
+void SetCrossTabInfoForFrame(content::RenderFrameHost* frame,
+ const base::Location& location = FROM_HERE);
+void ExpectCrossTabInfoForFrame(content::RenderFrameHost* frame,
+ bool expected,
+ const base::Location& location = FROM_HERE);
// Helper to request storage access for a frame using
// document.requestStorageAccess() and then get the value of
diff --git a/chromium/chrome/browser/net/system_network_context_manager.cc b/chromium/chrome/browser/net/system_network_context_manager.cc
index 35ad09af3e0..79914e85fd2 100644
--- a/chromium/chrome/browser/net/system_network_context_manager.cc
+++ b/chromium/chrome/browser/net/system_network_context_manager.cc
@@ -70,7 +70,6 @@
#include "net/cookies/cookie_util.h"
#include "net/net_buildflags.h"
#include "net/third_party/uri_template/uri_template.h"
-#include "sandbox/features.h"
#include "sandbox/policy/features.h"
#include "sandbox/policy/sandbox_type.h"
#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
@@ -124,12 +123,12 @@ enum class NetworkSandboxState {
kEnabledByPolicy = 3,
// Disabled because of a previous failed launch attempt.
kDisabledBecauseOfFailedLaunch = 4,
- kMaxValue = kDisabledBecauseOfFailedLaunch
+ // Disabled because the user (might) want kerberos, which is incompatible with
+ // the Linux/Cros sandbox.
+ kDisabledBecauseOfKerberos = 5,
+ kMaxValue = kDisabledBecauseOfKerberos
};
-// The temporary header name expected by the envoy proxy configuration.
-const char kIPAnonymizationProxyPassword[] = "password";
-
// The global instance of the SystemNetworkContextManager.
SystemNetworkContextManager* g_system_network_context_manager = nullptr;
@@ -137,6 +136,12 @@ SystemNetworkContextManager* g_system_network_context_manager = nullptr;
// received a failed launch for a sandboxed network service.
bool g_previously_failed_to_launch_sandboxed_service = false;
+#if BUILDFLAG(IS_CHROMEOS)
+// Whether kerberos library loading will work in the network service due to the
+// sandbox.
+bool g_network_service_will_allow_gssapi_library_load = false;
+#endif // BUILDFLAG(IS_CHROMEOS)
+
// Constructs HttpAuthStaticParams based on |local_state|.
network::mojom::HttpAuthStaticParamsPtr CreateHttpAuthStaticParams(
PrefService* local_state) {
@@ -201,28 +206,71 @@ network::mojom::HttpAuthDynamicParamsPtr CreateHttpAuthDynamicParams(
return auth_dynamic_params;
}
+void OnNewHttpAuthDynamicParams(
+ network::mojom::HttpAuthDynamicParamsPtr& params) {
+#if BUILDFLAG(IS_CHROMEOS)
+ // The kerberos library is incompatible with the network service sandbox, so
+ // if library loading is now enabled, the network service needs to be
+ // restarted. It will be restarted unsandboxed because is
+ // `g_network_service_will_allow_gssapi_library_load` will be set.
+ if (params->allow_gssapi_library_load &&
+ !g_network_service_will_allow_gssapi_library_load) {
+ g_network_service_will_allow_gssapi_library_load = true;
+ // The network service, if sandboxed, will still not allow gssapi library
+ // loading until it is shut down. On restart the network service will get
+ // the correct value.
+ params->allow_gssapi_library_load = false;
+ // Post a shutdown task because the current task probably holds a raw
+ // pointer to the remote.
+ base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE, base::BindOnce(&content::RestartNetworkService));
+ }
+#endif // BUILDFLAG(IS_CHROMEOS)
+}
+
void OnAuthPrefsChanged(PrefService* local_state,
const std::string& pref_name) {
- content::GetNetworkService()->ConfigureHttpAuthPrefs(
- CreateHttpAuthDynamicParams(local_state));
+ auto params = CreateHttpAuthDynamicParams(local_state);
+ OnNewHttpAuthDynamicParams(params);
+ content::GetNetworkService()->ConfigureHttpAuthPrefs(std::move(params));
}
NetworkSandboxState IsNetworkSandboxEnabledInternal() {
// If previously an attempt to launch the sandboxed process failed, then
// launch unsandboxed.
- if (g_previously_failed_to_launch_sandboxed_service)
+ if (g_previously_failed_to_launch_sandboxed_service) {
return NetworkSandboxState::kDisabledBecauseOfFailedLaunch;
+ }
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
+ auto* local_state = g_browser_process->local_state();
+#endif
+
+#if BUILDFLAG(IS_CHROMEOS)
+ // The network service sandbox and the kerberos library are incompatible.
+ // If kerberos is enabled by policy, disable the network service sandbox.
+ if (g_network_service_will_allow_gssapi_library_load ||
+ (local_state && local_state->HasPrefPath(prefs::kKerberosEnabled) &&
+ local_state->GetBoolean(prefs::kKerberosEnabled))) {
+ g_network_service_will_allow_gssapi_library_load = true;
+ return NetworkSandboxState::kDisabledBecauseOfKerberos;
+ }
+#endif // BUILDFLAG(IS_CHROMEOS)
+
#if BUILDFLAG(IS_WIN)
- if (!sandbox::features::IsAppContainerSandboxSupported())
+ if (!sandbox::policy::features::IsNetworkSandboxSupported()) {
return NetworkSandboxState::kDisabledByPlatform;
- auto* local_state = g_browser_process->local_state();
+ }
+#endif // BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
if (local_state &&
local_state->HasPrefPath(prefs::kNetworkServiceSandboxEnabled)) {
return local_state->GetBoolean(prefs::kNetworkServiceSandboxEnabled)
? NetworkSandboxState::kEnabledByPolicy
: NetworkSandboxState::kDisabledByPolicy;
}
-#endif // BUILDFLAG(IS_WIN)
+#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
+
// If no policy is specified, then delegate to global sandbox configuration.
return sandbox::policy::features::IsNetworkSandboxEnabled()
? NetworkSandboxState::kEnabledByPlatform
@@ -409,6 +457,28 @@ void SystemNetworkContextManager::DeleteInstance() {
g_system_network_context_manager = nullptr;
}
+#if BUILDFLAG(IS_LINUX)
+SystemNetworkContextManager::GssapiLibraryLoadObserver::
+ GssapiLibraryLoadObserver(SystemNetworkContextManager* owner)
+ : owner_(owner) {}
+
+SystemNetworkContextManager::GssapiLibraryLoadObserver::
+ ~GssapiLibraryLoadObserver() = default;
+
+void SystemNetworkContextManager::GssapiLibraryLoadObserver::Install(
+ network::mojom::NetworkService* network_service) {
+ gssapi_library_loader_observer_receiver_.reset();
+ network_service->SetGssapiLibraryLoadObserver(
+ gssapi_library_loader_observer_receiver_.BindNewPipeAndPassRemote());
+}
+
+void SystemNetworkContextManager::GssapiLibraryLoadObserver::
+ OnBeforeGssapiLibraryLoad() {
+ owner_->local_state_->SetBoolean(prefs::kReceivedHttpAuthNegotiateHeader,
+ true);
+}
+#endif // BUILDFLAG(IS_LINUX)
+
SystemNetworkContextManager::SystemNetworkContextManager(
PrefService* local_state)
: local_state_(local_state),
@@ -579,9 +649,13 @@ void SystemNetworkContextManager::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterListPref(prefs::kExplicitlyAllowedNetworkPorts);
-#if BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
registry->RegisterBooleanPref(prefs::kNetworkServiceSandboxEnabled, true);
-#endif // BUILDFLAG(IS_WIN)
+#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
+
+#if BUILDFLAG(IS_LINUX)
+ registry->RegisterBooleanPref(prefs::kReceivedHttpAuthNegotiateHeader, false);
+#endif // BUILDFLAG(IS_LINUX)
}
// static
@@ -624,8 +698,13 @@ void SystemNetworkContextManager::OnNetworkServiceCreated(
}
network_service->SetUpHttpAuth(CreateHttpAuthStaticParams(local_state_));
- network_service->ConfigureHttpAuthPrefs(
- CreateHttpAuthDynamicParams(local_state_));
+ auto http_auth_dynamic_params = CreateHttpAuthDynamicParams(local_state_);
+ OnNewHttpAuthDynamicParams(http_auth_dynamic_params);
+ network_service->ConfigureHttpAuthPrefs(std::move(http_auth_dynamic_params));
+
+#if BUILDFLAG(IS_LINUX)
+ gssapi_library_loader_observer_.Install(network_service);
+#endif // BUILDFLAG(IS_LINUX)
// Configure the Certificate Transparency logs.
if (IsCertificateTransparencyEnabled()) {
@@ -676,7 +755,7 @@ void SystemNetworkContextManager::OnNetworkServiceCreated(
network_service->SetMaxConnectionsPerProxy(max_connections_per_proxy);
network_service_network_context_.reset();
- network_service->CreateNetworkContext(
+ content::CreateNetworkContextInNetworkService(
network_service_network_context_.BindNewPipeAndPassReceiver(),
CreateNetworkContextParams());
@@ -726,6 +805,9 @@ void SystemNetworkContextManager::ConfigureDefaultNetworkContextParams(
network_context_params->enable_brotli = true;
+ network_context_params->enable_zstd =
+ base::FeatureList::IsEnabled(net::features::kZstdContentEncoding);
+
network_context_params->user_agent = embedder_support::GetUserAgent();
// Disable referrers by default. Any consumer that enables referrers should
@@ -735,11 +817,6 @@ void SystemNetworkContextManager::ConfigureDefaultNetworkContextParams(
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
- // TODO(crbug.com/1448657) Chrome no longer supports versions of QUIC which
- // send the quic_user_agent_id. We should remove quic_user_agent_id from
- // Chrome completely.
- network_context_params->quic_user_agent_id = "";
-
// TODO(eroman): Figure out why this doesn't work in single-process mode,
// or if it does work, now.
// Should be possible now that a private isolate is used.
@@ -777,48 +854,6 @@ void SystemNetworkContextManager::ConfigureDefaultNetworkContextParams(
if (IsCertificateTransparencyEnabled()) {
network_context_params->enforce_chrome_ct_policy = true;
}
-
- // If a custom proxy for IP protection is specified by either command line
- // switch or Finch experiment flag, set the proxy rules
- if (command_line.HasSwitch(network::switches::kIPAnonymizationProxyServer) ||
- base::FeatureList::IsEnabled(net::features::kEnableIpProtectionProxy)) {
- auto proxy_config = network::mojom::CustomProxyConfig::New();
- proxy_config->rules.type =
- net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME;
-
- // Command line input takes precedence over flag configuration
- std::string ip_protection_proxy_server =
- command_line.HasSwitch(network::switches::kIPAnonymizationProxyServer)
- ? command_line.GetSwitchValueASCII(
- network::switches::kIPAnonymizationProxyServer)
- : net::features::kIpPrivacyProxyServer.Get();
-
- proxy_config->rules.ParseFromString(ip_protection_proxy_server);
-
- // Get allowlist hosts, command line input takes precedence over flag
- // configuration
- std::string ip_protection_proxy_allow_list =
- command_line.HasSwitch(network::switches::kIPAnonymizationProxyServer)
- ? command_line.GetSwitchValueASCII(
- network::switches::kIPAnonymizationProxyAllowList)
- : net::features::kIpPrivacyProxyAllowlist.Get();
-
- proxy_config->rules.reverse_bypass = true;
- proxy_config->rules.bypass_rules.ParseFromString(
- ip_protection_proxy_allow_list);
-
- proxy_config->should_replace_direct = true;
- proxy_config->should_override_existing_config = false;
- proxy_config->allow_non_idempotent_methods = true;
- proxy_config->connect_tunnel_headers.SetHeader(
- kIPAnonymizationProxyPassword,
- command_line.GetSwitchValueASCII(
- network::switches::kIPAnonymizationProxyPassword));
-
- // Set initial custom proxy configuration
- network_context_params->initial_custom_proxy_config =
- std::move(proxy_config);
- }
}
network::mojom::NetworkContextParamsPtr
@@ -854,6 +889,8 @@ bool SystemNetworkContextManager::IsNetworkSandboxEnabled() {
"Chrome.SystemNetworkContextManager.NetworkSandboxState", state);
switch (state) {
+ case NetworkSandboxState::kDisabledBecauseOfKerberos:
+ return false;
case NetworkSandboxState::kDisabledByPlatform:
return false;
case NetworkSandboxState::kEnabledByPlatform:
@@ -900,23 +937,18 @@ void SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting(
bool SystemNetworkContextManager::IsCertificateTransparencyEnabled() {
if (certificate_transparency_enabled_for_testing_.has_value())
return certificate_transparency_enabled_for_testing_.value();
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && defined(OFFICIAL_BUILD)
-// TODO(carlosil): Figure out if we can/should remove the OFFICIAL_BUILD and
-// GOOGLE_CHROME_BRANDING checks now that enforcement does not rely on build
-// dates, and allow embedders to enforce.
-// Certificate Transparency is only enabled if:
-// - base::GetBuildTime() is deterministic to the source (OFFICIAL_BUILD)
-// - The build in reliably updatable (GOOGLE_CHROME_BRANDING)
-#if BUILDFLAG(IS_ANDROID)
- // On Android, enforcement is currently controlled via a feature flag.
+#if defined(OFFICIAL_BUILD)
+// TODO(carlosil): Figure out if we can/should remove the OFFICIAL_BUILD
+// check now that enforcement does not rely on build dates.
+// Certificate Transparency is enabled:
+// - by default for Chrome-branded builds
+// - on an opt-in basis for other builds and embedders, controlled with the
+// kCertificateTransparencyAskBeforeEnabling flag
return base::FeatureList::IsEnabled(
- features::kCertificateTransparencyAndroid);
-#else
- return true;
-#endif // BUILDFLAG(IS_ANDROID)
+ features::kCertificateTransparencyAskBeforeEnabling);
#else
return false;
-#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) && defined(OFFICIAL_BUILD)
+#endif // defined(OFFICIAL_BUILD)
}
#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
diff --git a/chromium/chrome/browser/net/system_network_context_manager.h b/chromium/chrome/browser/net/system_network_context_manager.h
index 2b1bffbdd34..226be4ff8b6 100644
--- a/chromium/chrome/browser/net/system_network_context_manager.h
+++ b/chromium/chrome/browser/net/system_network_context_manager.h
@@ -22,7 +22,7 @@
#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom-forward.h"
#include "services/network/public/mojom/host_resolver.mojom-forward.h"
#include "services/network/public/mojom/network_context.mojom.h"
-#include "services/network/public/mojom/network_service.mojom-forward.h"
+#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/ssl_config.mojom-forward.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -185,6 +185,28 @@ class SystemNetworkContextManager {
class URLLoaderFactoryForSystem;
class NetworkProcessLaunchWatcher;
+#if BUILDFLAG(IS_LINUX)
+ class GssapiLibraryLoadObserver
+ : public network::mojom::GssapiLibraryLoadObserver {
+ public:
+ explicit GssapiLibraryLoadObserver(SystemNetworkContextManager* owner);
+ GssapiLibraryLoadObserver(const GssapiLibraryLoadObserver&) = delete;
+ GssapiLibraryLoadObserver& operator=(const GssapiLibraryLoadObserver&) =
+ delete;
+ ~GssapiLibraryLoadObserver() override;
+
+ void Install(network::mojom::NetworkService* network_service);
+
+ // network::mojom::GssapiLibraryLoadObserver implementation:
+ void OnBeforeGssapiLibraryLoad() override;
+
+ private:
+ mojo::Receiver<network::mojom::GssapiLibraryLoadObserver>
+ gssapi_library_loader_observer_receiver_{this};
+ raw_ptr<SystemNetworkContextManager> owner_;
+ };
+#endif
+
// Constructor. |pref_service| must out live this object.
explicit SystemNetworkContextManager(PrefService* pref_service);
@@ -252,6 +274,10 @@ class SystemNetworkContextManager {
static StubResolverConfigReader* stub_resolver_config_reader_for_testing_;
static absl::optional<bool> certificate_transparency_enabled_for_testing_;
+
+#if BUILDFLAG(IS_LINUX)
+ GssapiLibraryLoadObserver gssapi_library_loader_observer_{this};
+#endif // BUILDFLAG(IS_LINUX)
};
#endif // CHROME_BROWSER_NET_SYSTEM_NETWORK_CONTEXT_MANAGER_H_
diff --git a/chromium/chrome/browser/net/system_network_context_manager_browsertest.cc b/chromium/chrome/browser/net/system_network_context_manager_browsertest.cc
index 7d487d27adb..3c948d66b6f 100644
--- a/chromium/chrome/browser/net/system_network_context_manager_browsertest.cc
+++ b/chromium/chrome/browser/net/system_network_context_manager_browsertest.cc
@@ -8,6 +8,7 @@
#include <vector>
#include "base/files/file_path.h"
+#include "base/functional/bind.h"
#include "base/strings/strcat.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
@@ -30,20 +31,22 @@
#include "components/version_info/version_info.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/network_service_util.h"
+#include "content/public/browser/service_process_host.h"
+#include "content/public/browser/service_process_info.h"
#include "content/public/common/content_features.h"
#include "content/public/common/user_agent.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/frame_test_utils.h"
+#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
+#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "net/base/features.h"
#include "net/cookies/canonical_cookie_test_helpers.h"
#include "net/dns/mock_host_resolver.h"
#include "net/net_buildflags.h"
-#include "net/proxy_resolution/proxy_info.h"
+#include "sandbox/policy/features.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/network_service_buildflags.h"
-#include "services/network/public/cpp/network_switches.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/network_service_test.mojom.h"
@@ -53,9 +56,13 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/features.h"
+#if BUILDFLAG(IS_CHROMEOS)
+#include "sandbox/policy/linux/sandbox_seccomp_bpf_linux.h"
+#endif // BUILDFLAG(IS_CHROMEOS)
+
using SystemNetworkContextManagerBrowsertest = InProcessBrowserTest;
-const char* kSamePartyCookieName = "SamePartyCookie";
+const char* kCookieName = "Cookie";
const char* kHostA = "a.test";
IN_PROC_BROWSER_TEST_F(SystemNetworkContextManagerBrowsertest,
@@ -214,259 +221,195 @@ IN_PROC_BROWSER_TEST_F(SystemNetworkContextManagerBrowsertest, AuthParams) {
dynamic_params->patterns_allowed_to_use_all_schemes);
}
-class SystemNetworkContextManagerWithCustomProxyConfigBrowserTest
- : public SystemNetworkContextManagerBrowsertest {
- protected:
- void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
- SystemNetworkContextManagerBrowsertest::SetUpDefaultCommandLine(
- command_line);
- command_line->AppendSwitchASCII(
- network::switches::kIPAnonymizationProxyServer, "testproxy:80");
- command_line->AppendSwitchASCII(
- network::switches::kIPAnonymizationProxyAllowList,
- "a.test, foo.a.test, foo.test, b.test:1234");
- command_line->AppendSwitchASCII(
- network::switches::kIPAnonymizationProxyPassword, "value");
+#if BUILDFLAG(IS_CHROMEOS)
+class SystemNetworkContextManagerNetworkServiceSandboxEnabledBrowsertest
+ : public SystemNetworkContextManagerBrowsertest,
+ public content::ServiceProcessHost::Observer {
+ public:
+ SystemNetworkContextManagerNetworkServiceSandboxEnabledBrowsertest() {
+ scoped_feature_list_.InitAndEnableFeature(
+ sandbox::policy::features::kNetworkServiceSandbox);
}
-};
-IN_PROC_BROWSER_TEST_F(
- SystemNetworkContextManagerWithCustomProxyConfigBrowserTest,
- InitialCustomProxyConfig) {
- network::mojom::NetworkContextParamsPtr network_context_params =
- g_browser_process->system_network_context_manager()
- ->CreateDefaultNetworkContextParams();
+ void SetUpOnMainThread() override {
+ // If the sandbox or the seccomp policy is disabled, these tests are
+ // meaningless.
+ if (!sandbox::policy::SandboxSeccompBPF::IsSeccompBPFDesired()) {
+ GTEST_SKIP();
+ }
- // Check that command line switches were correctly set in
- // `initial_custom_proxy_config`
- EXPECT_TRUE(network_context_params->initial_custom_proxy_config->rules
- .reverse_bypass);
- EXPECT_TRUE(network_context_params->initial_custom_proxy_config
- ->should_replace_direct);
- EXPECT_FALSE(network_context_params->initial_custom_proxy_config
- ->should_override_existing_config);
-
- EXPECT_EQ(network_context_params->initial_custom_proxy_config->rules
- .single_proxies.ToValue(),
- base::test::ParseJson(R"(["testproxy:80"])"));
- EXPECT_EQ(network_context_params->initial_custom_proxy_config->rules
- .bypass_rules.ToString(),
- "a.test;foo.a.test;foo.test;b.test:1234;");
-
- net::HttpRequestHeaders expected_header;
- expected_header.SetHeader("password", "value");
- EXPECT_EQ(network_context_params->initial_custom_proxy_config
- ->connect_tunnel_headers.ToString(),
- expected_header.ToString());
-
- // Check that rules are applied correctly
- net::ProxyInfo result;
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("http://example.test"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("http://foo.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("http://a.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://a.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://foo.a.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://bar.a.test"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://b.test:1234"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://b.test:5678"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
-}
+ SystemNetworkContextManagerBrowsertest::SetUpOnMainThread();
-class SystemNetworkContextManagerWithIpProtectionFlagsEnabled
- : public SystemNetworkContextManagerBrowsertest {
- public:
- SystemNetworkContextManagerWithIpProtectionFlagsEnabled() {
- // Enable the IP protection feature and set parameters for the proxy server
- // and proxy allowlist.
- base::FieldTrialParams params;
- params[net::features::kIpPrivacyProxyServer.name] =
- "testproxyenabledfromflag.test:80";
- params[net::features::kIpPrivacyProxyAllowlist.name] =
- "enabledfromflag.test";
-
- feature_list_.InitAndEnableFeatureWithParameters(
- net::features::kEnableIpProtectionProxy, params);
+ content::ServiceProcessHost::AddObserver(this);
+ auto running_processes =
+ content::ServiceProcessHost::GetRunningProcessInfo();
+ for (const auto& info : running_processes) {
+ if (info.IsService<network::mojom::NetworkService>()) {
+ network_process_ = info.GetProcess().Duplicate();
+ break;
+ }
+ }
+ }
+
+ void WaitForNextLaunch() {
+ launch_run_loop_.emplace();
+ launch_run_loop_->Run();
+ }
+
+ void WaitForNetworkServiceReady() {
+ mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
+ content::GetNetworkService()->BindTestInterfaceForTesting(
+ network_service_test.BindNewPipeAndPassReceiver());
+ mojo::ScopedAllowSyncCallForTesting allow_sync_call;
+ // Log() is sync so this thread will wait for this call to succeed.
+ network_service_test->Log(
+ "Logging in network service to ensure it's ready.");
+ }
+
+ void ExpectNetworkServiceSeccompSandboxed(bool sandboxed) {
+ // The network service may have been launched but has not yet sandboxed
+ // itself. So, wait for the Mojo endpoints to start accepting messages.
+ WaitForNetworkServiceReady();
+ EXPECT_EQ(sandboxed, GetNetworkServiceProcess().IsSeccompSandboxed());
+ }
+
+ base::Process GetNetworkServiceProcess() {
+ CHECK(content::IsOutOfProcessNetworkService());
+ return network_process_.Duplicate();
}
private:
- base::test::ScopedFeatureList feature_list_;
+ void OnServiceProcessLaunched(
+ const content::ServiceProcessInfo& info) override {
+ if (!info.IsService<network::mojom::NetworkService>()) {
+ return;
+ }
+ network_process_ = info.GetProcess().Duplicate();
+ if (launch_run_loop_) {
+ launch_run_loop_->Quit();
+ }
+ }
+
+ void OnServiceProcessTerminatedNormally(
+ const content::ServiceProcessInfo& info) override {}
+
+ void OnServiceProcessCrashed(
+ const content::ServiceProcessInfo& info) override {}
+
+ base::test::ScopedFeatureList scoped_feature_list_;
+ base::Process network_process_;
+ absl::optional<base::RunLoop> launch_run_loop_;
};
IN_PROC_BROWSER_TEST_F(
- SystemNetworkContextManagerWithIpProtectionFlagsEnabled,
- EnableIpProtectionProxyByFeature) {
- network::mojom::NetworkContextParamsPtr network_context_params =
- g_browser_process->system_network_context_manager()
- ->CreateDefaultNetworkContextParams();
+ SystemNetworkContextManagerNetworkServiceSandboxEnabledBrowsertest,
+ NetworkServiceRestartsUnsandboxedOnKerberosEnabled) {
+ PrefService* local_state = g_browser_process->local_state();
+
+ // Ensure kerberos starts disabled.
+ EXPECT_FALSE(local_state->GetBoolean(prefs::kKerberosEnabled));
+ // Ensure the network service starts sandboxed.
+ ExpectNetworkServiceSeccompSandboxed(/*sandboxed=*/true);
+
+ // Now enable kerberos.
+ local_state->SetBoolean(prefs::kKerberosEnabled, true);
+ EXPECT_TRUE(local_state->GetBoolean(prefs::kKerberosEnabled));
+ // The network service should automatically restart, and be unsandboxed.
+ WaitForNextLaunch();
+ ExpectNetworkServiceSeccompSandboxed(/*sandboxed=*/false);
+
+ // After killing the network service, it should still restart unsandboxed.
+ base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE,
+ base::BindOnce(base::IgnoreResult(&content::RestartNetworkService)));
+ WaitForNextLaunch();
+ ExpectNetworkServiceSeccompSandboxed(/*sandboxed=*/false);
+}
- // Check that feature configuration was correctly set in
- // `initial_custom_proxy_config`
- EXPECT_TRUE(network_context_params->initial_custom_proxy_config->rules
- .reverse_bypass);
- EXPECT_TRUE(network_context_params->initial_custom_proxy_config
- ->should_replace_direct);
- EXPECT_FALSE(network_context_params->initial_custom_proxy_config
- ->should_override_existing_config);
-
- EXPECT_EQ(network_context_params->initial_custom_proxy_config->rules
- .single_proxies.ToValue(),
- base::test::ParseJson(R"(["testproxyenabledfromflag.test:80"])"));
- EXPECT_EQ(network_context_params->initial_custom_proxy_config->rules
- .bypass_rules.ToString(),
- "enabledfromflag.test;");
-
- // Check that rules are applied correctly
- net::ProxyInfo result;
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("http://example.test"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("http://enabledfromflag.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxyenabledfromflag.test:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://enabledfromflag.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxyenabledfromflag.test:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("http://a.test"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://a.test"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://bar.a.test"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://b.test:1234"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
+IN_PROC_BROWSER_TEST_F(
+ SystemNetworkContextManagerNetworkServiceSandboxEnabledBrowsertest,
+ PRE_NetworkServiceStartsUnsandboxedWithKerberosEnabled) {
+ PrefService* local_state = g_browser_process->local_state();
+ // Enable kerberos.
+ local_state->SetBoolean(prefs::kKerberosEnabled, true);
+ EXPECT_TRUE(local_state->GetBoolean(prefs::kKerberosEnabled));
+}
+
+IN_PROC_BROWSER_TEST_F(
+ SystemNetworkContextManagerNetworkServiceSandboxEnabledBrowsertest,
+ NetworkServiceStartsUnsandboxedWithKerberosEnabled) {
+ // Ensure the network service starts sandboxed.
+ ExpectNetworkServiceSeccompSandboxed(/*sandboxed=*/false);
}
-class
- SystemNetworkContextManagerWithIpProtectionFlagsEnabledAndCommandLineSettingsEnabled
- : public SystemNetworkContextManagerWithCustomProxyConfigBrowserTest {
+#endif // BUILDFLAG(IS_CHROMEOS)
+
+#if BUILDFLAG(IS_LINUX)
+class SystemNetworkContextManagerHttpNegotiateHeader
+ : public SystemNetworkContextManagerBrowsertest {
public:
- SystemNetworkContextManagerWithIpProtectionFlagsEnabledAndCommandLineSettingsEnabled() {
- // Enable the IP protection feature flag using default params.
- feature_list_.InitAndEnableFeature(net::features::kEnableIpProtectionProxy);
+ static constexpr char kHttpsNegotiateAuthPath[] = "/http_negotiate_auth";
+
+ SystemNetworkContextManagerHttpNegotiateHeader()
+ : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+
+ void SetUpOnMainThread() override {
+ SystemNetworkContextManagerBrowsertest::SetUpOnMainThread();
+
+ https_server_.AddDefaultHandlers(
+ base::FilePath(FILE_PATH_LITERAL("content/test/data")));
+ https_server_.RegisterRequestHandler(
+ base::BindRepeating(&SystemNetworkContextManagerHttpNegotiateHeader::
+ SendBackHttpNegotiateHeader,
+ base::Unretained(this)));
+ ASSERT_TRUE(https_server_.Start());
}
- private:
- base::test::ScopedFeatureList feature_list_;
+ std::unique_ptr<net::test_server::HttpResponse> SendBackHttpNegotiateHeader(
+ const net::test_server::HttpRequest& request) {
+ if (request.relative_url != kHttpsNegotiateAuthPath) {
+ return nullptr;
+ }
+
+ auto http_response =
+ std::make_unique<net::test_server::BasicHttpResponse>();
+ http_response->set_code(net::HTTP_UNAUTHORIZED);
+ http_response->AddCustomHeader("WWW-Authenticate", "Negotiate");
+ return http_response;
+ }
+
+ content::WebContents* web_contents() {
+ return browser()->tab_strip_model()->GetActiveWebContents();
+ }
+
+ protected:
+ net::test_server::EmbeddedTestServer https_server_;
};
-IN_PROC_BROWSER_TEST_F(
- SystemNetworkContextManagerWithIpProtectionFlagsEnabledAndCommandLineSettingsEnabled,
- EnableIpProtectionProxyCommandLineOverridesFeature) {
- network::mojom::NetworkContextParamsPtr network_context_params =
- g_browser_process->system_network_context_manager()
- ->CreateDefaultNetworkContextParams();
+IN_PROC_BROWSER_TEST_F(SystemNetworkContextManagerHttpNegotiateHeader,
+ SetsPrefOnHttpNegotiateHeader) {
+ PrefService* local_state = g_browser_process->local_state();
+
+ // Ensure the pref starts false.
+ EXPECT_FALSE(
+ local_state->GetBoolean(prefs::kReceivedHttpAuthNegotiateHeader));
- // Check that command line switches were correctly set in
- // `initial_custom_proxy_config`even though feature flag parameters were set.
- EXPECT_TRUE(network_context_params->initial_custom_proxy_config->rules
- .reverse_bypass);
- EXPECT_TRUE(network_context_params->initial_custom_proxy_config
- ->should_replace_direct);
- EXPECT_FALSE(network_context_params->initial_custom_proxy_config
- ->should_override_existing_config);
-
- EXPECT_EQ(network_context_params->initial_custom_proxy_config->rules
- .single_proxies.ToValue(),
- base::test::ParseJson(R"(["testproxy:80"])"));
- EXPECT_EQ(network_context_params->initial_custom_proxy_config->rules
- .bypass_rules.ToString(),
- "a.test;foo.a.test;foo.test;b.test:1234;");
-
- net::HttpRequestHeaders expected_header;
- expected_header.SetHeader("password", "value");
- EXPECT_EQ(network_context_params->initial_custom_proxy_config
- ->connect_tunnel_headers.ToString(),
- expected_header.ToString());
-
- // Check that rules are applied correctly
- net::ProxyInfo result;
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("http://example.test"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("http://foo.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("http://a.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://a.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://foo.a.test"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://bar.a.test"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://b.test:1234"), &result);
- EXPECT_FALSE(result.did_bypass_proxy());
- EXPECT_EQ(result.ToPacString(), "PROXY testproxy:80");
-
- network_context_params->initial_custom_proxy_config->rules.Apply(
- GURL("https://b.test:5678"), &result);
- EXPECT_TRUE(result.did_bypass_proxy());
- EXPECT_EQ(result.proxy_server(), net::ProxyServer::Direct());
+ PrefChangeRegistrar pref_change_registrar;
+ pref_change_registrar.Init(local_state);
+
+ base::RunLoop wait_for_set_pref_loop;
+ pref_change_registrar.Add(prefs::kReceivedHttpAuthNegotiateHeader,
+ wait_for_set_pref_loop.QuitClosure());
+
+ // Navigate to a URL that requests negotiate authentication.
+ EXPECT_FALSE(NavigateToURL(web_contents(),
+ https_server_.GetURL(kHttpsNegotiateAuthPath)));
+ wait_for_set_pref_loop.Run();
+
+ // Ensure the pref is now true.
+ EXPECT_TRUE(local_state->GetBoolean(prefs::kReceivedHttpAuthNegotiateHeader));
}
+#endif // BUILDFLAG(IS_LINUX)
class SystemNetworkContextManagerWithFirstPartySetComponentBrowserTest
: public SystemNetworkContextManagerBrowsertest {
@@ -485,8 +428,10 @@ class SystemNetworkContextManagerWithFirstPartySetComponentBrowserTest
void SetUpInProcessBrowserTestFixture() override {
SystemNetworkContextManagerBrowsertest::SetUpInProcessBrowserTestFixture();
+ // Since we set kWaitForFirstPartySetsInit, all cookie-carrying network
+ // requests are blocked until FPS is initialized.
feature_list_.InitWithFeatures(
- {features::kFirstPartySets, net::features::kSamePartyAttributeEnabled},
+ {features::kFirstPartySets, net::features::kWaitForFirstPartySetsInit},
{});
CHECK(component_dir_.CreateUniqueTempDir());
base::ScopedAllowBlockingForTesting allow_blocking;
@@ -546,12 +491,10 @@ IN_PROC_BROWSER_TEST_F(
const GURL host_root = https_server()->GetURL(kHostA, "/");
ASSERT_TRUE(content::SetCookie(
browser()->profile(), host_root,
- base::StrCat(
- {kSamePartyCookieName,
- "=1; samesite=lax; secure; sameparty; max-age=2147483647"})));
+ base::StrCat({kCookieName, "=1; secure; max-age=2147483647"})));
ASSERT_THAT(content::GetCookies(browser()->profile(), host_root),
- net::CookieStringIs(testing::UnorderedElementsAre(
- testing::Key(kSamePartyCookieName))));
+ net::CookieStringIs(
+ testing::UnorderedElementsAre(testing::Key(kCookieName))));
}
IN_PROC_BROWSER_TEST_F(
@@ -563,22 +506,22 @@ IN_PROC_BROWSER_TEST_F(
const GURL host_root = https_server()->GetURL(kHostA, "/");
ASSERT_THAT(content::GetCookies(browser()->profile(), host_root),
- net::CookieStringIs(testing::UnorderedElementsAre(
- testing::Key(kSamePartyCookieName))));
+ net::CookieStringIs(
+ testing::UnorderedElementsAre(testing::Key(kCookieName))));
- EXPECT_THAT(content::ArrangeFramesAndGetContentFromLeaf(
- web_contents(), https_server(), "b.test(%s)", {0},
- EchoCookiesUrl(kHostA)),
- net::CookieStringIs(testing::UnorderedElementsAre(
- testing::Key(kSamePartyCookieName))));
+ ASSERT_TRUE(content::NavigateToURL(web_contents(), EchoCookiesUrl(kHostA)));
+ EXPECT_THAT(content::EvalJs(web_contents(), "document.body.textContent")
+ .ExtractString(),
+ net::CookieStringIs(
+ testing::UnorderedElementsAre(testing::Key(kCookieName))));
SimulateNetworkServiceCrash();
- EXPECT_THAT(content::ArrangeFramesAndGetContentFromLeaf(
- web_contents(), https_server(), "b.test(%s)", {0},
- EchoCookiesUrl(kHostA)),
- net::CookieStringIs(testing::UnorderedElementsAre(
- testing::Key(kSamePartyCookieName))));
+ ASSERT_TRUE(content::NavigateToURL(web_contents(), EchoCookiesUrl(kHostA)));
+ EXPECT_THAT(content::EvalJs(web_contents(), "document.body.textContent")
+ .ExtractString(),
+ net::CookieStringIs(
+ testing::UnorderedElementsAre(testing::Key(kCookieName))));
}
class SystemNetworkContextManagerReferrersFeatureBrowsertest
@@ -589,7 +532,7 @@ class SystemNetworkContextManagerReferrersFeatureBrowsertest
scoped_feature_list_.InitWithFeatureState(features::kNoReferrers,
GetParam());
}
- ~SystemNetworkContextManagerReferrersFeatureBrowsertest() override {}
+ ~SystemNetworkContextManagerReferrersFeatureBrowsertest() override = default;
void SetUpOnMainThread() override {}
@@ -615,7 +558,7 @@ class SystemNetworkContextManagerFreezeQUICUaBrowsertest
: public SystemNetworkContextManagerBrowsertest {
public:
SystemNetworkContextManagerFreezeQUICUaBrowsertest() = default;
- ~SystemNetworkContextManagerFreezeQUICUaBrowsertest() override {}
+ ~SystemNetworkContextManagerFreezeQUICUaBrowsertest() override = default;
void SetUpOnMainThread() override {}
@@ -623,17 +566,6 @@ class SystemNetworkContextManagerFreezeQUICUaBrowsertest
base::test::ScopedFeatureList scoped_feature_list_;
};
-IN_PROC_BROWSER_TEST_F(SystemNetworkContextManagerFreezeQUICUaBrowsertest,
- QUICUaConfig) {
- network::mojom::NetworkContextParamsPtr network_context_params =
- g_browser_process->system_network_context_manager()
- ->CreateDefaultNetworkContextParams();
-
- std::string quic_ua = network_context_params->quic_user_agent_id;
-
- EXPECT_EQ("", quic_ua);
-}
-
class SystemNetworkContextManagerWPADQuickCheckBrowsertest
: public SystemNetworkContextManagerBrowsertest,
public testing::WithParamInterface<bool> {
diff --git a/chromium/chrome/browser/net/websocket_browsertest.cc b/chromium/chrome/browser/net/websocket_browsertest.cc
index f1d1f4f4cbf..907cb22cf6d 100644
--- a/chromium/chrome/browser/net/websocket_browsertest.cc
+++ b/chromium/chrome/browser/net/websocket_browsertest.cc
@@ -18,7 +18,6 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
-#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
@@ -51,7 +50,6 @@
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/test/test_data_directory.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "services/network/public/cpp/network_switches.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/websocket.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -755,53 +753,4 @@ IN_PROC_BROWSER_TEST_F(WebSocketBrowserTestWithAllowFileAccessFromFiles,
EXPECT_EQ("FILE", WaitAndGetTitle());
}
-// A test fixture that enables First-Party Sets.
-class FirstPartySetsWebSocketBrowserTest
- : public WebSocketBrowserHTTPSConnectToTest {
- public:
- FirstPartySetsWebSocketBrowserTest()
- : WebSocketBrowserHTTPSConnectToTest(SSLOptions::CERT_TEST_NAMES) {}
-
- void SetUpInProcessBrowserTestFixture() override {
- feature_list_.InitAndEnableFeature(
- net::features::kSamePartyAttributeEnabled);
- }
-
- void SetUpCommandLine(base::CommandLine* command_line) override {
- WebSocketBrowserTest::SetUpCommandLine(command_line);
- command_line->AppendSwitchASCII(
- network::switches::kUseFirstPartySet,
- R"({"primary": "https://a.test",)"
- R"("associatedSites": ["https://b.test","https://c.test"]})");
- }
-
- private:
- base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(FirstPartySetsWebSocketBrowserTest,
- SendsSamePartyCookies) {
- ASSERT_TRUE(wss_server_.Start());
-
- ASSERT_TRUE(content::SetCookie(browser()->profile(),
- server().GetURL("a.test", "/"),
- "same-party-cookie=1; SameParty; Secure"));
- ASSERT_TRUE(content::SetCookie(browser()->profile(),
- server().GetURL("a.test", "/"),
- "same-site-cookie=1; SameSite=Lax; Secure"));
-
- content::DOMMessageQueue message_queue(
- browser()->tab_strip_model()->GetActiveWebContents());
- ConnectTo("b.test", wss_server_.GetURL("a.test", "echo-request-headers"));
-
- std::string message;
- EXPECT_TRUE(message_queue.WaitForMessage(&message));
- // Only the SameParty cookie should have been sent, since it was a cross-site
- // but same-party connection.
- EXPECT_THAT(message, testing::HasSubstr("same-party-cookie=1"));
- EXPECT_THAT(message, testing::Not(testing::HasSubstr("same-site-cookie=1")));
-
- EXPECT_EQ("PASS", WaitAndGetTitle());
-}
-
} // namespace
diff --git a/chromium/chrome/browser/new_tab_page/modules/history_clusters/BUILD.gn b/chromium/chrome/browser/new_tab_page/modules/history_clusters/BUILD.gn
index 3aee6a671e0..f74d4324dfb 100644
--- a/chromium/chrome/browser/new_tab_page/modules/history_clusters/BUILD.gn
+++ b/chromium/chrome/browser/new_tab_page/modules/history_clusters/BUILD.gn
@@ -5,11 +5,15 @@
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojo_bindings") {
- sources = [ "history_clusters.mojom" ]
+ sources = [
+ "history_clusters.mojom",
+ "history_clusters_layout_type.mojom",
+ ]
webui_module_path = "/"
use_typescript_sources = true
public_deps = [
"//chrome/browser/new_tab_page/modules/history_clusters/cart:mojo_bindings",
+ "//chrome/browser/new_tab_page/modules/history_clusters/discount:mojo_bindings",
"//components/history_clusters/public/mojom:mojo_bindings",
"//mojo/public/mojom/base",
"//url/mojom:url_mojom_gurl",
diff --git a/chromium/chrome/browser/new_tab_page/modules/history_clusters/discount/BUILD.gn b/chromium/chrome/browser/new_tab_page/modules/history_clusters/discount/BUILD.gn
new file mode 100644
index 00000000000..fa2e6a2bcb2
--- /dev/null
+++ b/chromium/chrome/browser/new_tab_page/modules/history_clusters/discount/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojo_bindings") {
+ sources = [ "discount.mojom" ]
+ public_deps = [ "//url/mojom:url_mojom_gurl" ]
+ webui_module_path = "/"
+ use_typescript_sources = true
+}
diff --git a/chromium/chrome/browser/new_tab_page/modules/history_clusters/discount/discount.mojom b/chromium/chrome/browser/new_tab_page/modules/history_clusters/discount/discount.mojom
new file mode 100644
index 00000000000..ffb86147e14
--- /dev/null
+++ b/chromium/chrome/browser/new_tab_page/modules/history_clusters/discount/discount.mojom
@@ -0,0 +1,16 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ntp.history_clusters.discount.mojom;
+
+import "url/mojom/url.mojom";
+
+// Information needed to show discount for one visit in Resume browsing module.
+struct Discount {
+ // Text indicating the discount value (e.g. 10$ off).
+ string value_in_text;
+ // Original visit URL plus UTM annotations to indicate that
+ // this visit has an associated discount.
+ url.mojom.Url annotated_visit_url;
+}; \ No newline at end of file
diff --git a/chromium/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom b/chromium/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom
index afac760f636..d201794d790 100644
--- a/chromium/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom
+++ b/chromium/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom
@@ -5,19 +5,11 @@
module ntp.history_clusters.mojom;
import "chrome/browser/new_tab_page/modules/history_clusters/cart/cart.mojom";
+import "chrome/browser/new_tab_page/modules/history_clusters/discount/discount.mojom";
+import "chrome/browser/new_tab_page/modules/history_clusters/history_clusters_layout_type.mojom";
import "components/history_clusters/public/mojom/history_cluster_types.mojom";
import "url/mojom/url.mojom";
-// Available module UI layouts. This enum must match the numbering for
-// NTPHistoryClustersModuleDisplayLayout in enums.xml. These values are
-// persisted to logs. Entries should not be renumbered, removed or reused.
-enum LayoutType {
- kNone,
- kLayout1, // 2 image visits
- kLayout2, // 1 image visit & 2 non-image visits
- kLayout3, // 2 image visits & 2 non-image visits
-};
-
// Browser-side handler for requests from WebUI page.
interface PageHandler {
// Get a series of relevant history clusters.
@@ -27,6 +19,10 @@ interface PageHandler {
GetCartForCluster(history_clusters.mojom.Cluster cluster)
=> (ntp.history_clusters.cart.mojom.Cart? cart);
+ // Get the discounts for the given `cluster`.
+ GetDiscountsForCluster(history_clusters.mojom.Cluster cluster)
+ => (map<url.mojom.Url, array<ntp.history_clusters.discount.mojom.Discount>> discounts);
+
// Open or make visible the Journeys UI on the Side Panel.
ShowJourneysSidePanel(string query);
diff --git a/chromium/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_layout_type.mojom b/chromium/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_layout_type.mojom
new file mode 100644
index 00000000000..78c5c2c2554
--- /dev/null
+++ b/chromium/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_layout_type.mojom
@@ -0,0 +1,17 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ntp.history_clusters.mojom;
+
+// Available module UI layouts. This enum must match the numbering for
+// NTPHistoryClustersModuleDisplayLayout in enums.xml. These values are
+// persisted to logs. Entries should not be renumbered, removed or reused.
+enum LayoutType {
+ kNone,
+ kLayout1, // 2 image visits (V1)
+ kLayout2, // 1 image visit & 2 non-image visits (V1)
+ kLayout3, // 2 image visits & 2 non-image visits (V1)
+ kTextOnly, // No visits with images (V2)
+ kImages, // One or more visits with images (V2)
+};
diff --git a/chromium/chrome/browser/new_tab_page/modules/v2/history_clusters/BUILD.gn b/chromium/chrome/browser/new_tab_page/modules/v2/history_clusters/BUILD.gn
new file mode 100644
index 00000000000..dce2bf9f776
--- /dev/null
+++ b/chromium/chrome/browser/new_tab_page/modules/v2/history_clusters/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojo_bindings") {
+ sources = [ "history_clusters_v2.mojom" ]
+ webui_module_path = "/"
+ use_typescript_sources = true
+ public_deps = [
+ "//chrome/browser/new_tab_page/modules/history_clusters:mojo_bindings",
+ "//chrome/browser/new_tab_page/modules/history_clusters/cart:mojo_bindings",
+ "//chrome/browser/new_tab_page/modules/history_clusters/discount:mojo_bindings",
+ "//components/history_clusters/public/mojom:mojo_bindings",
+ "//mojo/public/mojom/base",
+ "//url/mojom:url_mojom_gurl",
+ ]
+}
diff --git a/chromium/chrome/browser/new_tab_page/modules/v2/history_clusters/history_clusters_v2.mojom b/chromium/chrome/browser/new_tab_page/modules/v2/history_clusters/history_clusters_v2.mojom
new file mode 100644
index 00000000000..4fecd33bc90
--- /dev/null
+++ b/chromium/chrome/browser/new_tab_page/modules/v2/history_clusters/history_clusters_v2.mojom
@@ -0,0 +1,41 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ntp.history_clusters_v2.mojom;
+
+import "chrome/browser/new_tab_page/modules/history_clusters/cart/cart.mojom";
+import "chrome/browser/new_tab_page/modules/history_clusters/discount/discount.mojom";
+import "chrome/browser/new_tab_page/modules/history_clusters/history_clusters_layout_type.mojom";
+import "components/history_clusters/public/mojom/history_cluster_types.mojom";
+import "url/mojom/url.mojom";
+
+// Browser-side handler for requests from WebUI page.
+interface PageHandler {
+ // Get a series of relevant history clusters.
+ GetClusters() => (array<history_clusters.mojom.Cluster> clusters);
+
+ // Get the most relevant cart for given `cluster`.
+ GetCartForCluster(history_clusters.mojom.Cluster cluster)
+ => (ntp.history_clusters.cart.mojom.Cart? cart);
+
+ // Get the discounts for the given `cluster`.
+ GetDiscountsForCluster(history_clusters.mojom.Cluster cluster)
+ => (map<url.mojom.Url, array<ntp.history_clusters.discount.mojom.Discount>> discounts);
+
+ // Open or make visible the Journeys UI on the Side Panel.
+ ShowJourneysSidePanel(string query);
+
+ // Records that the cluster with `cluster_id` was clicked.
+ RecordClick(int64 cluster_id);
+
+ // Records the layout type for the cluster with `cluster_id`.
+ RecordLayoutTypeShown(ntp.history_clusters.mojom.LayoutType layout_type,
+ int64 cluster_id);
+
+ // Dismiss or undo a dismiss of a cluster by marking its associated visits
+ // with a given interaction state.
+ UpdateClusterVisitsInteractionState(
+ array<history_clusters.mojom.URLVisit> visits,
+ history_clusters.mojom.InteractionState state);
+};
diff --git a/chromium/chrome/browser/notifications/BUILD.gn b/chromium/chrome/browser/notifications/BUILD.gn
index 9f8f270d738..249433c797f 100644
--- a/chromium/chrome/browser/notifications/BUILD.gn
+++ b/chromium/chrome/browser/notifications/BUILD.gn
@@ -126,7 +126,9 @@ if (is_android) {
":java_resources",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:base_java_test_support_uncommon",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
"//chrome/browser/ui/android/signin:java_resources",
diff --git a/chromium/chrome/browser/notifications/scheduler/public/BUILD.gn b/chromium/chrome/browser/notifications/scheduler/public/BUILD.gn
index c228380a6bc..7a05e978a02 100644
--- a/chromium/chrome/browser/notifications/scheduler/public/BUILD.gn
+++ b/chromium/chrome/browser/notifications/scheduler/public/BUILD.gn
@@ -39,8 +39,8 @@ source_set("public") {
"user_action_handler.h",
]
+ public_deps = [ "//base" ]
deps = [
- "//base",
"//components/keyed_service/core",
"//skia",
]
diff --git a/chromium/chrome/browser/optimization_guide/android/BUILD.gn b/chromium/chrome/browser/optimization_guide/android/BUILD.gn
index de9de7a9896..74f1fcdf6fb 100644
--- a/chromium/chrome/browser/optimization_guide/android/BUILD.gn
+++ b/chromium/chrome/browser/optimization_guide/android/BUILD.gn
@@ -49,6 +49,7 @@ android_library("native_java_unittests") {
deps = [
":java",
+ "//base:base_java_test_support",
"//base:jni_java",
"//chrome/browser/flags:java",
"//chrome/browser/profiles/android:java",
@@ -78,6 +79,7 @@ android_library("unit_device_javatests") {
":java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
"//chrome/browser/profiles/android:java",
@@ -101,6 +103,7 @@ android_library("javatests") {
":java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_test_util_java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
diff --git a/chromium/chrome/browser/paint_preview/android/BUILD.gn b/chromium/chrome/browser/paint_preview/android/BUILD.gn
index 80604dce2d5..24b51d1de76 100644
--- a/chromium/chrome/browser/paint_preview/android/BUILD.gn
+++ b/chromium/chrome/browser/paint_preview/android/BUILD.gn
@@ -74,6 +74,7 @@ android_library("javatests") {
"//chrome/browser/flags:java",
"//chrome/browser/tab:java",
"//chrome/browser/tabmodel:java",
+ "//chrome/browser/ui/android/appmenu:java",
"//chrome/browser/ui/android/appmenu/test:test_support_java",
"//chrome/browser/ui/messages/android:java",
"//chrome/test/android:chrome_java_integration_test_support",
@@ -87,6 +88,7 @@ android_library("javatests") {
"//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/androidx:androidx_test_uiautomator_uiautomator_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
diff --git a/chromium/chrome/browser/partnercustomizations/BUILD.gn b/chromium/chrome/browser/partnercustomizations/BUILD.gn
index b1c0ef34897..f15057d757c 100644
--- a/chromium/chrome/browser/partnercustomizations/BUILD.gn
+++ b/chromium/chrome/browser/partnercustomizations/BUILD.gn
@@ -120,6 +120,7 @@ robolectric_library("junit") {
":delegate_public_impl_java",
":helper_java",
":java",
+ ":junit_test_utils_java",
":test_support_java",
":uma_java",
"//base:base_java",
@@ -134,7 +135,16 @@ robolectric_library("junit") {
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//url:gurl_java",
- "//url:gurl_junit_shadows",
"//url:gurl_junit_test_support",
]
}
+
+android_library("junit_test_utils_java") {
+ testonly = true
+ sources = [ "java/src/org/chromium/chrome/browser/partnercustomizations/PartnerCustomizationsTestUtils.java" ]
+ deps = [
+ ":helper_java",
+ "//components/embedder_support/android:util_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ ]
+}
diff --git a/chromium/chrome/browser/password_check/android/BUILD.gn b/chromium/chrome/browser/password_check/android/BUILD.gn
index 96f4c1c4226..033c9d61cf8 100644
--- a/chromium/chrome/browser/password_check/android/BUILD.gn
+++ b/chromium/chrome/browser/password_check/android/BUILD.gn
@@ -129,6 +129,7 @@ android_library("test_java") {
"internal:internal_ui_factory_java",
"internal:java_resources",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_app_java_resources",
"//chrome/android:chrome_java",
"//chrome/android:chrome_test_java",
@@ -142,6 +143,7 @@ android_library("test_java") {
"//chrome/browser/settings:java",
"//chrome/browser/settings:test_support_java",
"//chrome/test/android:chrome_java_integration_test_support",
+ "//components/browser_ui/settings/android:java",
"//components/browser_ui/widget/android:java",
"//components/embedder_support/android:util_java",
"//components/password_manager/core/browser:password_manager_java_enums",
diff --git a/chromium/chrome/browser/password_check/android/internal/BUILD.gn b/chromium/chrome/browser/password_check/android/internal/BUILD.gn
index 4339ad24d73..777bff51e46 100644
--- a/chromium/chrome/browser/password_check/android/internal/BUILD.gn
+++ b/chromium/chrome/browser/password_check/android/internal/BUILD.gn
@@ -27,6 +27,7 @@ android_library_factory("public_factory_java") {
android_library("internal_factory_java") {
deps = [
":internal_java",
+ "//base:base_java",
"//chrome/browser/flags:java",
"//chrome/browser/settings:java",
"//components/browser_ui/settings/android:java",
@@ -58,6 +59,7 @@ android_library("internal_ui_factory_java") {
"//chrome/browser/settings:java",
"//components/browser_ui/settings/android:java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_preference_preference_java",
_public_target,
_public_ui_target,
]
diff --git a/chromium/chrome/browser/password_edit_dialog/android/BUILD.gn b/chromium/chrome/browser/password_edit_dialog/android/BUILD.gn
index 4dcbd8feda4..01b5652ed4f 100644
--- a/chromium/chrome/browser/password_edit_dialog/android/BUILD.gn
+++ b/chromium/chrome/browser/password_edit_dialog/android/BUILD.gn
@@ -82,6 +82,7 @@ robolectric_library("junit") {
"//chrome/browser/flags:java",
"//chrome/test/android:chrome_java_unit_test_support",
"//third_party/android_deps:espresso_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
@@ -103,6 +104,7 @@ android_library("unit_device_javatests") {
deps = [
":java",
":java_resources",
+ "//base:base_java",
"//base:base_java_test_support",
"//chrome/browser/flags:java",
"//chrome/browser/ui/android/night_mode:java",
diff --git a/chromium/chrome/browser/password_entry_edit/android/internal/BUILD.gn b/chromium/chrome/browser/password_entry_edit/android/internal/BUILD.gn
index c8faf10ce7a..86c213634af 100644
--- a/chromium/chrome/browser/password_entry_edit/android/internal/BUILD.gn
+++ b/chromium/chrome/browser/password_entry_edit/android/internal/BUILD.gn
@@ -38,6 +38,7 @@ android_library("java") {
"//chrome/browser/ui/android/strings:ui_strings_grd",
"//components/browser_ui/settings/android:java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
"//ui/android:ui_no_recycler_view_java",
]
@@ -59,6 +60,7 @@ robolectric_library("junit") {
"//chrome/browser/ui/android/strings:ui_strings_grd",
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_test_core_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
@@ -74,6 +76,8 @@ android_library("javatests") {
deps = [
":java",
"//base:base_java_test_support",
+ "//chrome/android:chrome_java",
+ "//chrome/browser/feedback/android:java",
"//chrome/browser/flags:java",
"//chrome/browser/password_entry_edit/android:java",
"//chrome/browser/password_entry_edit/android:java_resources",
diff --git a/chromium/chrome/browser/password_manager/android/BUILD.gn b/chromium/chrome/browser/password_manager/android/BUILD.gn
index dc1ec8c93f4..5bb88045a57 100644
--- a/chromium/chrome/browser/password_manager/android/BUILD.gn
+++ b/chromium/chrome/browser/password_manager/android/BUILD.gn
@@ -81,9 +81,11 @@ source_set("backend") {
deps = [
":jni_headers",
"//components/autofill/core/browser:browser",
+ "//components/password_manager/core/browser:affiliation",
"//components/password_manager/core/browser:browser",
"//components/password_manager/core/browser:password_form",
"//components/password_manager/core/browser:unified_password_manager_proto",
+ "//components/password_manager/core/browser/features:password_features",
"//components/password_manager/core/common:common",
"//components/password_manager/core/common:features",
"//components/prefs:prefs",
@@ -147,6 +149,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridge.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLifecycleHelper.java",
+ "java/src/org/chromium/chrome/browser/password_manager/PasswordMetricsUtil.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterDispatcherBridge.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterMetricsRecorder.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterReceiverBridge.java",
@@ -225,6 +228,7 @@ robolectric_binary("password_manager_junit_tests") {
"//base:base_java_test_support",
"//base:base_java_test_support_uncommon",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
"//chrome/browser/flags:java",
"//chrome/browser/loading_modal/android:java",
@@ -243,6 +247,7 @@ robolectric_binary("password_manager_junit_tests") {
"//components/sync/android:sync_java",
"//components/sync/protocol:protocol_java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/android_deps:espresso_java",
"//third_party/android_deps:protobuf_lite_runtime_java",
"//third_party/androidx:androidx_annotation_annotation_java",
@@ -371,6 +376,7 @@ android_library("test_support_java") {
"//components/password_manager/core/browser:unified_password_manager_proto_java",
"//components/sync/protocol:protocol_java",
"//content/public/android:content_main_dex_java",
+ "//third_party/android_deps:protobuf_lite_runtime_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
}
@@ -419,6 +425,14 @@ android_library("public_impl_java") {
resources_package = "org.chromium.chrome.browser.password_manager"
}
+source_set("password_generation_utils") {
+ deps = [ "//components/autofill/core/common" ]
+ sources = [
+ "password_generation_element_data.cc",
+ "password_generation_element_data.h",
+ ]
+}
+
source_set("unit_tests") {
testonly = true
sources = [
@@ -452,6 +466,7 @@ source_set("unit_tests") {
deps = [
":backend",
":backend_public",
+ ":password_generation_utils",
"//base/test:test_support",
"//chrome/app:generated_resources",
"//chrome/browser",
@@ -466,8 +481,10 @@ source_set("unit_tests") {
"//components/device_reauth:test_support",
"//components/messages/android:test_support",
"//components/password_manager/content/browser",
+ "//components/password_manager/content/browser:test_support",
"//components/password_manager/core/browser",
"//components/password_manager/core/browser:test_support",
+ "//components/password_manager/core/browser/features:password_features",
"//components/password_manager/core/common",
"//components/safe_browsing/core/browser/password_protection:test_support",
"//components/security_state/core",
diff --git a/chromium/chrome/browser/password_manager/android/pwd_migration/BUILD.gn b/chromium/chrome/browser/password_manager/android/pwd_migration/BUILD.gn
index 1edeb1150cd..cee75152e62 100644
--- a/chromium/chrome/browser/password_manager/android/pwd_migration/BUILD.gn
+++ b/chromium/chrome/browser/password_manager/android/pwd_migration/BUILD.gn
@@ -21,11 +21,13 @@ android_library("java") {
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/settings/android:java",
"//components/browser_ui/widget/android:java",
+ "//components/password_manager/core/browser:password_manager_java_enums",
"//components/prefs/android:java",
"//components/signin/public/android:java",
"//components/sync/android:sync_java",
"//components/user_prefs/android:java",
"//components/version_info/android:version_constants_java",
+ "//content/public/android:content_java",
"//third_party/android_deps/utils:java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
@@ -37,6 +39,7 @@ android_library("java") {
sources = [
"java/src/org/chromium/chrome/browser/pwd_migration/ExportDeletionDialogFragment.java",
"java/src/org/chromium/chrome/browser/pwd_migration/ExportFlowInterface.java",
+ "java/src/org/chromium/chrome/browser/pwd_migration/NonCancelableProgressBar.java",
"java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningCoordinator.java",
"java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningIntroFragment.java",
"java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediator.java",
@@ -46,7 +49,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningUtil.java",
"java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningView.java",
"java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningViewBinder.java",
- "java/src/org/chromium/chrome/browser/pwd_migration/ProgressBarDeletionDialog.java",
+ "java/src/org/chromium/chrome/browser/pwd_migration/ScrollablePasswordMigrationWarningContent.java",
]
resources_package = "org.chromium.chrome.browser.pwd_migration"
@@ -62,6 +65,8 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
+ "//chrome/browser/password_manager/android:java",
"//chrome/browser/preferences:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/signin/services/android:java",
@@ -70,11 +75,13 @@ robolectric_library("junit") {
"//chrome/test/android:chrome_java_test_support_common",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/bottomsheet/android/test:java",
+ "//components/password_manager/core/browser:password_manager_java_enums",
"//components/prefs/android:java",
"//components/signin/public/android:java",
"//components/signin/public/android:signin_java_test_support",
"//components/sync/android:sync_java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/espresso:espresso_core_java",
"//third_party/hamcrest:hamcrest_library_java",
@@ -102,12 +109,14 @@ android_library("javatests") {
"//base:base_java_test_support",
"//chrome/android:chrome_java",
"//chrome/browser/flags:java",
+ "//chrome/browser/password_manager/android:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/signin/services/android:java",
"//chrome/browser/ui/android/night_mode:night_mode_java_test_support",
"//chrome/test/android:chrome_java_integration_test_support",
"//chrome/test/android:chrome_java_test_support_common",
"//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/browser_ui/bottomsheet/android/test:java",
"//components/browser_ui/widget/android:java",
"//components/signin/public/android:java",
@@ -116,6 +125,7 @@ android_library("javatests") {
"//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
diff --git a/chromium/chrome/browser/payments/android/BUILD.gn b/chromium/chrome/browser/payments/android/BUILD.gn
index 25c76b619bf..0ce3acadb49 100644
--- a/chromium/chrome/browser/payments/android/BUILD.gn
+++ b/chromium/chrome/browser/payments/android/BUILD.gn
@@ -8,13 +8,10 @@ import("//build/config/android/rules.gni")
# TODO(crbug.com/1066269): Extract //chrome/browser/payments/android source files
# from //chrome/browser/BUILD.gn into an independent build target here.
-generate_jni("jni_headers") {
- sources = [ "../../../android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java" ]
-}
-
# TODO(crbug.com/1164979): remove the dependency on chrome_java.
robolectric_library("junit_test_support") {
deps = [
+ "//base:base_java",
"//chrome/android:chrome_java",
"//chrome/browser/android/lifecycle:java",
"//chrome/browser/profiles/android:java",
@@ -47,9 +44,11 @@ robolectric_library("junit") {
":junit_test_support",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
"//chrome/browser/autofill/android:java",
"//chrome/browser/profiles/android:java",
+ "//components/autofill/android:main_autofill_java",
"//components/payments/content/android:java",
"//components/payments/content/android:junit_test_support",
"//components/payments/content/android:service_java",
diff --git a/chromium/chrome/browser/persisted_state_db/BUILD.gn b/chromium/chrome/browser/persisted_state_db/BUILD.gn
index eba59762cc4..08c594d363e 100644
--- a/chromium/chrome/browser/persisted_state_db/BUILD.gn
+++ b/chromium/chrome/browser/persisted_state_db/BUILD.gn
@@ -38,6 +38,7 @@ source_set("persisted_state_db") {
deps += [
"//components/commerce/core:cart_db_content_proto",
"//components/commerce/core:coupon_db_content_proto",
+ "//components/commerce/core:discounts_db_content_proto",
]
}
}
diff --git a/chromium/chrome/browser/plus_addresses/BUILD.gn b/chromium/chrome/browser/plus_addresses/BUILD.gn
new file mode 100644
index 00000000000..4b438559d98
--- /dev/null
+++ b/chromium/chrome/browser/plus_addresses/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("browser_tests") {
+ testonly = true
+ sources = [ "plus_address_browsertest.cc" ]
+
+ defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//chrome/browser",
+ "//chrome/test:test_support",
+ "//components/plus_addresses",
+ "//content/test:test_support",
+ "//testing/gtest",
+ ]
+
+ if (is_android) {
+ deps += [ "//chrome/test:test_support_ui_android" ]
+ } else {
+ deps += [ "//chrome/test:test_support_ui" ]
+ }
+}
diff --git a/chromium/chrome/browser/policy/BUILD.gn b/chromium/chrome/browser/policy/BUILD.gn
index a4250ce60f4..6b198efc5ed 100644
--- a/chromium/chrome/browser/policy/BUILD.gn
+++ b/chromium/chrome/browser/policy/BUILD.gn
@@ -28,7 +28,6 @@ source_set("path_parser") {
]
if (is_mac) {
- configs += [ "//build/config/compiler:enable_arc" ]
deps += [ "//build:branding_buildflags" ]
} else if (is_win) {
deps += [ "//chrome/install_static:install_static_util" ]
@@ -139,6 +138,13 @@ source_set("test_support") {
]
}
+ if (enable_dice_support || is_chromeos_lacros) {
+ sources += [
+ "cloud/user_policy_signin_service_test_util.cc",
+ "cloud/user_policy_signin_service_test_util.h",
+ ]
+ }
+
if (!is_android) {
sources += [
"extension_policy_test_base.cc",
@@ -181,16 +187,21 @@ source_set("policy_specific_browser_tests") {
sources = [
"policy_network_browsertest.cc",
"site_isolation_policy_browsertest.cc",
+ "test/back_forward_cache_policy_browsertest.cc",
"test/certificate_transparency_policy_browsertest.cc",
+ "test/force_permission_policy_unload_default_enabled_policy_browsertest.cc",
"test/hsts_policy_browsertest.cc",
"test/network_prediction_policy_browsertest.cc",
"test/offset_parent_new_spec_behavior_policy_browsertest.cc",
+ "test/policy_statistics_collector_browsertest.cc",
+ "test/proxy_policies_browsertest.cc",
"test/safe_browsing_policy_browsertest.cc",
"test/send_mouse_events_disabled_form_controls_policy_browsertest.cc",
"test/shared_clipboard_enabled_browsertest.cc",
"test/ssl_error_overriding_allowed_policy_browsertest.cc",
"test/url_keyed_anonymized_data_collection_enabled_browsertest.cc",
"test/web_rtc_udp_port_range_policy_browsertest.cc",
+ "test/web_sql_access_policy_browsertest.cc",
]
if (is_chromeos) {
sources += [ "test/policy_certs_browsertest.cc" ]
@@ -205,6 +216,8 @@ source_set("policy_specific_browser_tests") {
"//chrome/test:test_support",
"//components/policy/core/common",
"//components/prefs",
+ "//components/proxy_config",
+ "//components/safe_browsing/core/common",
"//components/security_interstitials/content:security_interstitial_page",
"//components/unified_consent",
"//content/test:test_support",
@@ -230,15 +243,16 @@ source_set("policy_specific_browser_tests") {
if (!is_android) {
sources += [
"test/autoplay_policy_browsertest.cc",
+ "test/block_truncated_cookies_policy_browsertest.cc",
"test/bookmark_bar_enabled_browsertest.cc",
"test/component_updater_policy_browsertest.cc",
"test/developer_tools_policy_browsertest.cc",
"test/download_directory_browsertest.cc",
- "test/hide_webstore_icon_policy_browsertest.cc",
"test/lens_desktop_ntp_search_enabled_policy_browsertest.cc",
"test/local_fonts_policy_browsertest.cc",
"test/media_stream_policy_browsertest.cc",
"test/quic_allowed_browsertest.cc",
+ "test/shared_dictionary_policy_browsertest.cc",
"test/signed_exchange_browsertest.cc",
"test/task_manager_end_process_enabled_browsertest.cc",
"test/web_rtc_event_log_collection_allowed_policy_browsertest.cc",
@@ -348,6 +362,7 @@ source_set("browser_tests") {
"test/assistant_policy_browsertest.cc",
"test/audio_output_allowed_browsertest.cc",
"test/note_taking_on_lock_screen_policy_browsertest.cc",
+ "test/power_sounds_policy_browsertest.cc",
"test/screenshot_policy_browsertest.cc",
"test/session_length_limit_policy_browsertest.cc",
"test/suggested_content_policy_browsertest.cc",
@@ -362,6 +377,7 @@ source_set("browser_tests") {
"//ash/components/arc/session:session",
"//chrome/browser/apps/app_service",
"//chrome/browser/ash",
+ "//chrome/browser/ash:test_support",
"//chrome/browser/ash/system_web_apps/test_support",
"//chromeos/ash/components/audio",
"//chromeos/ash/components/cryptohome",
@@ -381,18 +397,20 @@ source_set("browser_tests") {
"cloud/chrome_browser_cloud_management_browsertest_mac_util.h",
"cloud/chrome_browser_cloud_management_browsertest_mac_util.mm",
]
- configs += [ "//build/config/compiler:enable_arc" ]
}
if (is_win) {
sources += [
"test/audio_process_high_priority_enabled_browsertest.cc",
"test/locale_policy_browsertest.cc",
- "test/network_service_sandbox_enabled_browsertest.cc",
"test/renderer_app_container_enabled_win_browsertest.cc",
]
}
+ if (is_win || is_linux) {
+ sources += [ "test/network_service_sandbox_enabled_browsertest.cc" ]
+ }
+
if (!is_android) {
sources += [
"cloud/component_cloud_policy_browsertest.cc",
diff --git a/chromium/chrome/browser/policy/android/BUILD.gn b/chromium/chrome/browser/policy/android/BUILD.gn
index b9fb3700a4f..7737223b5d3 100644
--- a/chromium/chrome/browser/policy/android/BUILD.gn
+++ b/chromium/chrome/browser/policy/android/BUILD.gn
@@ -27,6 +27,7 @@ android_library("java") {
]
deps = [
+ "//base:base_java",
"//base:jni_java",
"//build/android:build_java",
"//chrome/browser/profiles/android:java",
diff --git a/chromium/chrome/browser/policy/messaging_layer/proto/BUILD.gn b/chromium/chrome/browser/policy/messaging_layer/proto/BUILD.gn
index 452ffac8fe5..8bcbe6c6264 100644
--- a/chromium/chrome/browser/policy/messaging_layer/proto/BUILD.gn
+++ b/chromium/chrome/browser/policy/messaging_layer/proto/BUILD.gn
@@ -5,13 +5,11 @@
import("//build/config/features.gni")
import("//third_party/protobuf/proto_library.gni")
-proto_library("session_affiliated_user") {
- sources = [ "synced/session_affiliated_user.proto" ]
-}
-
proto_library("crd_event_proto") {
+ proto_in_dir = "//"
sources = [ "synced/crd_event.proto" ]
- deps = [ ":session_affiliated_user" ]
+ import_dirs = [ "//components/reporting/proto/synced" ]
+ deps = [ "//components/reporting/proto:session_affiliated_user_proto" ]
}
proto_library("log_upload_event_proto") {
diff --git a/chromium/chrome/browser/preferences/BUILD.gn b/chromium/chrome/browser/preferences/BUILD.gn
index e0257b2da7c..d15a889535f 100644
--- a/chromium/chrome/browser/preferences/BUILD.gn
+++ b/chromium/chrome/browser/preferences/BUILD.gn
@@ -34,22 +34,22 @@ android_library("java") {
# TODO(chouinard,estade): Consider breaking this target up into a different target for each pref file.
java_cpp_strings("java_pref_names_srcjar") {
sources = [
- "//chrome/common/pref_names.cc",
+ "//chrome/common/pref_names.h",
"//components/autofill/core/common/autofill_prefs.cc",
"//components/commerce/core/pref_names.cc",
"//components/dom_distiller/core/pref_names.cc",
- "//components/embedder_support/pref_names.cc",
+ "//components/embedder_support/pref_names.h",
"//components/feed/core/common/pref_names.cc",
"//components/feed/core/shared_prefs/pref_names.cc",
"//components/history/core/common/pref_names.cc",
- "//components/language/core/browser/pref_names.cc",
+ "//components/language/core/browser/pref_names.h",
"//components/password_manager/core/common/password_manager_pref_names.cc",
"//components/payments/core/payment_prefs.cc",
"//components/policy/core/common/policy_pref_names.cc",
- "//components/privacy_sandbox/privacy_sandbox_prefs.cc",
+ "//components/privacy_sandbox/privacy_sandbox_prefs.h",
"//components/safe_browsing/core/common/safe_browsing_prefs.cc",
"//components/signin/public/base/signin_pref_names.cc",
- "//components/supervised_user/core/common/pref_names.cc",
+ "//components/supervised_user/core/common/pref_names.h",
"//components/translate/core/browser/translate_pref_names.cc",
]
diff --git a/chromium/chrome/browser/prefetch/android/BUILD.gn b/chromium/chrome/browser/prefetch/android/BUILD.gn
index 333b0b6fc29..4fa498afab1 100644
--- a/chromium/chrome/browser/prefetch/android/BUILD.gn
+++ b/chromium/chrome/browser/prefetch/android/BUILD.gn
@@ -69,8 +69,10 @@ android_library("javatests") {
"//components/user_prefs/android:java",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
]
diff --git a/chromium/chrome/browser/prefs/browser_prefs.cc b/chromium/chrome/browser/prefs/browser_prefs.cc
index af2d036f0b3..327282bd867 100644
--- a/chromium/chrome/browser/prefs/browser_prefs.cc
+++ b/chromium/chrome/browser/prefs/browser_prefs.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/prefs/browser_prefs.h"
#include <string>
+#include <string_view>
#include "ash/constants/ash_constants.h"
#include "base/time/time.h"
@@ -17,8 +18,11 @@
#include "chrome/browser/accessibility/accessibility_labels_service.h"
#include "chrome/browser/accessibility/accessibility_ui.h"
#include "chrome/browser/accessibility/invert_bubble_prefs.h"
+#include "chrome/browser/ash/notifications/update_notification_showing_controller.h"
#include "chrome/browser/browser_process_impl.h"
#include "chrome/browser/chrome_content_browser_client.h"
+#include "chrome/browser/chromeos/enterprise/cloud_storage/policy_utils.h"
+#include "chrome/browser/chromeos/upload_office_to_cloud/upload_office_to_cloud.h"
#include "chrome/browser/component_updater/component_updater_prefs.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/download/download_prefs.h"
@@ -31,7 +35,6 @@
#include "chrome/browser/gpu/gpu_mode_manager.h"
#include "chrome/browser/lifetime/browser_shutdown.h"
#include "chrome/browser/login_detection/login_detection_prefs.h"
-#include "chrome/browser/media/media_device_id_salt.h"
#include "chrome/browser/media/media_engagement_service.h"
#include "chrome/browser/media/media_storage_id_salt.h"
#include "chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h"
@@ -99,6 +102,7 @@
#include "components/certificate_transparency/pref_names.h"
#include "components/commerce/core/pref_names.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/pref_names.h"
#include "components/custom_handlers/protocol_handler_registry.h"
#include "components/dom_distiller/core/distilled_page_prefs.h"
#include "components/dom_distiller/core/dom_distiller_features.h"
@@ -117,6 +121,7 @@
#include "components/language/core/browser/language_prefs.h"
#include "components/lens/buildflags.h"
#include "components/lookalikes/core/lookalike_url_util.h"
+#include "components/media_device_salt/media_device_id_salt.h"
#include "components/metrics/demographics/user_demographics.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/network_time/network_time_tracker.h"
@@ -129,9 +134,10 @@
#include "components/password_manager/core/browser/password_manager.h"
#include "components/payments/core/payment_prefs.h"
#include "components/performance_manager/public/user_tuning/prefs.h"
-#include "components/permissions/permission_actions_history.h"
+#include "components/permissions/pref_names.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/browser/url_blocklist_manager.h"
+#include "components/policy/core/common/local_test_policy_provider.h"
#include "components/policy/core/common/management/management_service.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/policy/core/common/policy_statistics_collector.h"
@@ -157,8 +163,8 @@
#include "components/site_engagement/content/site_engagement_service.h"
#include "components/subresource_filter/content/browser/ruleset_service.h"
#include "components/supervised_user/core/common/buildflags.h"
-#include "components/sync/base/sync_prefs.h"
#include "components/sync/service/glue/sync_transport_data_prefs.h"
+#include "components/sync/service/sync_prefs.h"
#include "components/sync_device_info/device_info_prefs.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "components/sync_sessions/session_sync_prefs.h"
@@ -195,7 +201,6 @@
#include "extensions/browser/permissions_manager.h"
#include "extensions/browser/pref_names.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/ash/attestation/tpm_challenge_key.h"
#include "chrome/browser/ash/crosapi/browser_data_migrator.h"
#include "chrome/browser/ash/device_name/device_name_store.h"
#include "chrome/browser/ash/extensions/extensions_permissions_tracker.h"
@@ -204,6 +209,7 @@
#include "chrome/browser/ash/net/system_proxy_manager.h"
#include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_manager_impl.h"
#include "chrome/browser/ash/policy/networking/euicc_status_uploader.h"
+#include "chrome/browser/ash/policy/remote_commands/crd_admin_session_controller.h"
#include "chrome/browser/ash/settings/hardware_data_usage_controller.h"
#include "chrome/browser/ash/settings/stats_reporting_controller.h"
#include "chrome/browser/component_updater/metadata_table_chromeos.h"
@@ -223,7 +229,7 @@
#endif
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-#include "chrome/browser/supervised_user/child_accounts/child_account_service.h"
+#include "components/supervised_user/core/browser/child_account_service.h"
#include "components/supervised_user/core/browser/supervised_user_service.h"
#endif
@@ -232,6 +238,7 @@
#include "components/feed/core/shared_prefs/pref_names.h"
#if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.h"
#include "chrome/browser/android/bookmarks/partner_bookmarks_shim.h"
#include "chrome/browser/android/ntp/recent_tabs_page_prefs.h"
#include "chrome/browser/android/oom_intervention/oom_intervention_decider.h"
@@ -270,7 +277,9 @@
#include "chrome/browser/serial/serial_policy_allowed_ports.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/ui/commerce/price_tracking/shopping_list_ui_tab_helper.h"
+#include "chrome/browser/ui/safety_hub/safety_hub_prefs.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
+#include "chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h"
#include "chrome/browser/ui/webui/history/foreign_session_handler.h"
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h"
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
@@ -296,6 +305,8 @@
#include "chrome/browser/chromeos/extensions/echo_private/echo_private_api.h"
#include "chrome/browser/chromeos/extensions/login_screen/login/login_api_prefs.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.h"
+#include "chrome/browser/chromeos/quickoffice/quickoffice_prefs.h"
+#include "chrome/browser/chromeos/reporting/metric_reporting_prefs.h"
#include "chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h"
#include "chrome/browser/memory/oom_kills_monitor.h"
#include "chrome/browser/policy/networking/policy_cert_service.h"
@@ -341,7 +352,6 @@
#include "chrome/browser/ash/child_accounts/time_limits/app_activity_registry.h"
#include "chrome/browser/ash/child_accounts/time_limits/app_time_controller.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
-#include "chrome/browser/ash/crosapi/network_settings_service_ash.h"
#include "chrome/browser/ash/crostini/crostini_pref_names.h"
#include "chrome/browser/ash/cryptauth/client_app_metadata_provider_service.h"
#include "chrome/browser/ash/cryptauth/cryptauth_device_id_provider_impl.h"
@@ -368,12 +378,14 @@
#include "chrome/browser/ash/login/security_token_session_controller.h"
#include "chrome/browser/ash/login/session/user_session_manager.h"
#include "chrome/browser/ash/login/signin/signin_error_notifier.h"
+#include "chrome/browser/ash/login/signin/token_handle_fetcher.h"
#include "chrome/browser/ash/login/startup_utils.h"
#include "chrome/browser/ash/login/users/avatar/user_image_manager.h"
#include "chrome/browser/ash/login/users/avatar/user_image_prefs.h"
#include "chrome/browser/ash/login/users/avatar/user_image_sync_observer.h"
#include "chrome/browser/ash/login/users/chrome_user_manager_impl.h"
#include "chrome/browser/ash/login/users/multi_profile_user_controller.h"
+#include "chrome/browser/ash/net/ash_proxy_monitor.h"
#include "chrome/browser/ash/net/network_throttling_observer.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_pref_names.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -402,7 +414,7 @@
#include "chrome/browser/ash/settings/device_settings_cache.h"
#include "chrome/browser/ash/system/automatic_reboot_manager.h"
#include "chrome/browser/ash/system/input_device_settings.h"
-#include "chrome/browser/ash/web_applications/help_app/help_app_notification_controller.h"
+#include "chrome/browser/ash/system_web_apps/apps/help_app/help_app_notification_controller.h"
#include "chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h"
#include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
#include "chrome/browser/media/protected_media_identifier_permission_context.h"
@@ -517,49 +529,13 @@ namespace {
// Please keep the list of deprecated prefs in chronological order. i.e. Add to
// the bottom of the list, not here at the top.
-// Deprecated 06/2022.
-const char kBackgroundTracingLastUpload[] = "background_tracing.last_upload";
-const char kStabilityGpuCrashCount[] =
- "user_experience_metrics.stability.gpu_crash_count";
-const char kStabilityRendererCrashCount[] =
- "user_experience_metrics.stability.renderer_crash_count";
-const char kStabilityExtensionRendererCrashCount[] =
- "user_experience_metrics.stability.extension_renderer_crash_count";
-const char kPrivacySandboxPreferencesReconciled[] =
- "privacy_sandbox.preferences_reconciled";
+// Deprecated 06/2022
+// TODO(crbug.com/1476489): Remove when unit test code is updated.
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
const char kTokenServiceDiceCompatible[] = "token_service.dice_compatible";
#endif
-#if !BUILDFLAG(IS_ANDROID)
-const char kStabilityPageLoadCount[] =
- "user_experience_metrics.stability.page_load_count";
-#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-const char kImprovedShortcutsNotificationShownCount[] =
- "ash.improved_shortcuts_notification_shown_count";
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-#if BUILDFLAG(IS_ANDROID)
-const char kDownloadLaterPromptStatus[] =
- "download.download_later_prompt_status";
-#endif // BUILDFLAG(IS_ANDROID)
-
-// Deprecated 07/2022
-const char kPrivacySandboxFlocEnabled[] = "privacy_sandbox.floc_enabled";
-const char kPrivacySandboxFlocDataAccessibleSince[] =
- "privacy_sandbox.floc_data_accessible_since";
-const char kStabilityCrashCount[] =
- "user_experience_metrics.stability.crash_count";
-const char kPrivacySandboxApisEnabledV2Init[] =
- "privacy_sandbox.apis_enabled_v2_init";
#if BUILDFLAG(ENABLE_EXTENSIONS)
-// Deprecated 06/2022.
-const char kU2fSecurityKeyApiEnabled[] =
- "extensions.u2f_security_key_api_enabled";
-
-// Deprecated 07/2022.
-const char kExtensionToolbar[] = "extensions.toolbar";
-
// Deprecated 10/2022.
const char kLoadCryptoTokenExtension[] =
"extensions.load_cryptotoken_extension";
@@ -569,30 +545,6 @@ const char kLoadCryptoTokenExtension[] =
const char kOriginTrialPrefKey[] = "origin_trials.persistent_trials";
#if BUILDFLAG(IS_CHROMEOS_ASH)
-// Deprecated 07/2022.
-// The name of a boolean pref that determines whether we can show the folder
-// selection user nudge for the screen capture tool. When this pref is false, it
-// means that we showed the nudge at some point and the user interacted with the
-// capture mode session UI in such a way that the nudge no longer needs to be
-// displayed again.
-constexpr char kCanShowFolderSelectionNudge[] =
- "ash.capture_mode.can_show_folder_selection_nudge";
-const char kSettingsShowOSBanner[] = "settings.cros.show_os_banner";
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-// Deprecated 08/2022.
-constexpr char kSecurityTokenSessionNotificationDisplayed[] =
- "security_token_session_notification_displayed";
-#endif
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-// Deprecated 08/2022.
-const char kProfileAvatarTutorialShown[] =
- "profile.avatar_bubble_tutorial_shown";
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
// Deprecated 09/2022.
constexpr char kClipboardHistoryNewFeatureBadgeCount[] =
"ash.clipboard.multipaste_nudges.new_feature_shown_count";
@@ -895,6 +847,12 @@ const char kShouldShowSidePanelBookmarkTab[] =
"should_show_side_panel_bookmark_tab";
#endif // !BUILDFLAG(IS_ANDROID)
+// Deprecated 06/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+const char kGaiaLastOnlineSignInTime[] = "gaia.last_online_sign_in_time";
+const char kSAMLLastGAIASignInTime[] = "saml.last_gaia_sign_in_time";
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
// Deprecated 07/2023
#if !BUILDFLAG(IS_ANDROID)
const char kLegacyHoverCardImagesEnabled[] = "browser.hovercard_images_enabled";
@@ -904,21 +862,58 @@ const char kLegacyHoverCardImagesEnabled[] = "browser.hovercard_images_enabled";
const char kVideoPreviewsType[] = "ntp_snippets.video_previews_type";
#endif // BUILDFLAG(ENABLE_FEED_V2)
-// Register local state used only for migration (clearing or moving to a new
-// key).
-void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
- // Deprecated 06/2022.
- registry->RegisterInt64Pref(kBackgroundTracingLastUpload, 0);
- registry->RegisterIntegerPref(kStabilityGpuCrashCount, 0);
- registry->RegisterIntegerPref(kStabilityRendererCrashCount, 0);
- registry->RegisterIntegerPref(kStabilityExtensionRendererCrashCount, 0);
-#if !BUILDFLAG(IS_ANDROID)
- registry->RegisterIntegerPref(kStabilityPageLoadCount, 0);
+// Deprecated 06/2023.
+#if BUILDFLAG(IS_ANDROID)
+const char kPrefExplicitLanguageAskShown[] =
+ "translate_explicit_language_ask_shown";
+#endif // BUILDFLAG(IS_ANDROID)
+
+// Deprecated 07/2023.
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+const char kUnifiedConsentMigrationState[] = "unified_consent.migration_state";
+#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
+
+// Deprecated 07/2023.
+const char kPasswordsGroupingInfoRequested[] =
+ "password_manager.passwords_grouping_info_requested";
+
+// Deprecated 07/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+const char kPowerMetricsIdleScreenOffCount[] =
+ "power.metrics.idle_screen_off_count";
+const char kPowerMetricsIdleSuspendCount[] = "power.metrics.idle_suspend_count";
+const char kPowerMetricsLidClosedSuspendCount[] =
+ "power.metrics.lid_closed_suspend_count";
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+// Deprecated 07/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+const char kHatsPrivacyHubBaselineIsSelected[] =
+ "hats_privacy_hub_baseline_is_selected";
+const char kHatsPrivacyHubBaselineCycleEndTs[] =
+ "hats_privacy_hub_baseline_end_timestamp";
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+// Deprecated 07/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+const char kClearUserDataDir1Pref[] = "lacros.clear_user_data_dir_1";
#endif
- // Deprecated 07/2022.
- registry->RegisterIntegerPref(kStabilityCrashCount, 0);
+// Deprecated 07/2023.
+const char kShutdownNumProcesses[] = "shutdown.num_processes";
+const char kShutdownNumProcessesSlow[] = "shutdown.num_processes_slow";
+const char kShutdownType[] = "shutdown.type";
+
+// Deprecated 08/2023.
+const char kDriveFsBulkPinningMaxQueueSize[] =
+ "drivefs.bulk_pinning.max_queue_size";
+// Deprecated 09/2023.
+const char kPrivacySandboxM1Unrestricted[] = "privacy_sandbox.m1.unrestricted";
+
+// Register local state used only for migration (clearing or moving to a new
+// key).
+void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Deprecated 09/2022
registry->RegisterDictionaryPref(kUsersLastInputMethod);
@@ -1034,80 +1029,24 @@ void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
#if !BUILDFLAG(IS_ANDROID)
registry->RegisterBooleanPref(kLegacyHoverCardImagesEnabled, false);
#endif // !BUILDFLAG(IS_ANDROID)
+
+ // Deprecated 07/2023.
+ registry->RegisterIntegerPref(kShutdownNumProcesses, 0);
+ registry->RegisterIntegerPref(kShutdownNumProcessesSlow, 0);
+ registry->RegisterIntegerPref(kShutdownType, 0);
}
// Register prefs used only for migration (clearing or moving to a new key).
void RegisterProfilePrefsForMigration(
user_prefs::PrefRegistrySyncable* registry) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- registry->RegisterBooleanPref(kCanShowFolderSelectionNudge,
- /*default_value=*/true);
-
- registry->RegisterIntegerPref(kImprovedShortcutsNotificationShownCount, 0);
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
chrome_browser_net::secure_dns::RegisterProbesSettingBackupPref(registry);
-#if !BUILDFLAG(IS_ANDROID)
- // Removed in M91.
- registry->RegisterBooleanPref(prefs::kMediaFeedsBackgroundFetching, false);
- registry->RegisterBooleanPref(prefs::kMediaFeedsSafeSearchEnabled, false);
- registry->RegisterBooleanPref(prefs::kMediaFeedsAutoSelectEnabled, false);
-
-#endif
-
-#if !BUILDFLAG(IS_ANDROID)
- registry->RegisterListPref(
- prefs::kManagedProfileSerialAllowAllPortsForUrlsDeprecated);
- registry->RegisterListPref(
- prefs::kManagedProfileSerialAllowUsbDevicesForUrlsDeprecated);
-#endif
-
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ // Deprecated 06/2022
+ // TODO(crbug.com/1476489): Remove when unit test code is updated.
registry->RegisterBooleanPref(kTokenServiceDiceCompatible, false);
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
- registry->RegisterBooleanPref(kPrivacySandboxPreferencesReconciled, false);
-
-#if BUILDFLAG(IS_ANDROID)
- registry->RegisterIntegerPref(kDownloadLaterPromptStatus, 0);
-#endif // BUILDFLAG(IS_ANDROID)
-
- // Deprecated 06/2022
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- registry->RegisterBooleanPref(kU2fSecurityKeyApiEnabled, false);
-#endif
-
- // Deprecated 07/2022
- registry->RegisterBooleanPref(kPrivacySandboxFlocEnabled, true);
- registry->RegisterBooleanPref(kPrivacySandboxFlocDataAccessibleSince, false);
- registry->RegisterBooleanPref(kPrivacySandboxApisEnabledV2Init, false);
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- // Deprecated 07/2022
- registry->RegisterListPref(kExtensionToolbar);
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- registry->RegisterBooleanPref(kSettingsShowOSBanner, false);
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- // Deprecated 08/2022
- registry->RegisterBooleanPref(kSecurityTokenSessionNotificationDisplayed,
- false);
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
- // Deprecated 08/2022.
- registry->RegisterIntegerPref(kProfileAvatarTutorialShown, 0);
-#endif
-
-#if BUILDFLAG(IS_LINUX)
- // Deprecated 08/2022.
- registry->RegisterBooleanPref(prefs::kUsesSystemThemeDeprecated, false);
-#endif
-
// Deprecated 09/2022
registry->RegisterBooleanPref(kPrivacySandboxFirstPartySetsDataAccessAllowed,
true);
@@ -1296,19 +1235,70 @@ void RegisterProfilePrefsForMigration(
#if BUILDFLAG(ENABLE_FEED_V2)
registry->RegisterIntegerPref(kVideoPreviewsType, 1);
#endif // BUILDFLAG(ENABLE_FEED_V2)
+
+// Deprecated 06/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ registry->RegisterTimePref(kGaiaLastOnlineSignInTime, base::Time());
+ registry->RegisterTimePref(kSAMLLastGAIASignInTime, base::Time());
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+ // Deprecated 06/2023.
+#if BUILDFLAG(IS_ANDROID)
+ registry->RegisterBooleanPref(kPrefExplicitLanguageAskShown, false);
+#endif // BUILDFLAG(IS_ANDROID)
+
+// Deprecated 07/2023.
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+ registry->RegisterIntegerPref(kUnifiedConsentMigrationState, 0);
+#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
+
+ // Deprecated 07/2023
+ registry->RegisterBooleanPref(kPasswordsGroupingInfoRequested, false);
+
+// Deprecated 07/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ registry->RegisterIntegerPref(kPowerMetricsIdleScreenOffCount, 0);
+ registry->RegisterIntegerPref(kPowerMetricsIdleSuspendCount, 0);
+ registry->RegisterIntegerPref(kPowerMetricsLidClosedSuspendCount, 0);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+// Deprecated 07/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ registry->RegisterIntegerPref(kHatsPrivacyHubBaselineIsSelected, false);
+ registry->RegisterIntegerPref(kHatsPrivacyHubBaselineCycleEndTs, 0);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+ // Deprecated 08/2023.
+ registry->RegisterIntegerPref(kDriveFsBulkPinningMaxQueueSize, 0);
+
+ // Deprecated 09/2023.
+ registry->RegisterBooleanPref(kPrivacySandboxM1Unrestricted, false);
}
+
} // namespace
+std::string GetCountry() {
+ if (!g_browser_process || !g_browser_process->variations_service()) {
+ // This should only happen in tests. Ideally this would be guarded by
+ // CHECK_IS_TEST, but that is not set on Android, so no specific guard.
+ return std::string();
+ }
+ return std::string(
+ g_browser_process->variations_service()->GetStoredPermanentCountry());
+}
+
void RegisterLocalState(PrefRegistrySimple* registry) {
// Call outs to individual subsystems that register Local State (browser-wide)
// prefs en masse. See RegisterProfilePrefs for per-profile prefs. Please
// keep this list alphabetized.
+#if BUILDFLAG(IS_ANDROID)
+ accessibility::AccessibilityPrefsController::RegisterLocalStatePrefs(
+ registry);
+#endif
browser_shutdown::RegisterPrefs(registry);
BrowserProcessImpl::RegisterPrefs(registry);
ChromeContentBrowserClient::RegisterLocalStatePrefs(registry);
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
chrome_labs_prefs::RegisterLocalStatePrefs(registry);
-#endif
ChromeMetricsServiceClient::RegisterPrefs(registry);
chrome::enterprise_util::RegisterLocalStatePrefs(registry);
component_updater::RegisterPrefs(registry);
@@ -1330,6 +1320,7 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
optimization_guide::prefs::RegisterLocalStatePrefs(registry);
password_manager::PasswordManager::RegisterLocalPrefs(registry);
policy::BrowserPolicyConnector::RegisterPrefs(registry);
+ policy::LocalTestPolicyProvider::RegisterLocalStatePrefs(registry);
policy::ManagementService::RegisterLocalStatePrefs(registry);
policy::PolicyStatisticsCollector::RegisterPrefs(registry);
PrefProxyConfigTrackerImpl::RegisterPrefs(registry);
@@ -1367,6 +1358,7 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
#if BUILDFLAG(IS_ANDROID)
registry->RegisterBooleanPref(policy::policy_prefs::kBackForwardCacheEnabled,
true);
+ registry->RegisterBooleanPref(policy::policy_prefs::kReadAloudEnabled, true);
#endif // BUILDFLAG(IS_ANDROID)
// Below this point is for platform-specific and compile-time conditional
@@ -1470,6 +1462,7 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
// TODO(b/265923216): Replace with EnrollmentStateFetcher::RegisterPrefs.
policy::AutoEnrollmentClientImpl::RegisterPrefs(registry);
policy::BrowserPolicyConnectorAsh::RegisterPrefs(registry);
+ policy::CrdAdminSessionController::RegisterLocalStatePrefs(registry);
policy::DeviceCloudPolicyManagerAsh::RegisterPrefs(registry);
policy::DeviceStatusCollector::RegisterPrefs(registry);
policy::DeviceWallpaperImageExternalDataHandler::RegisterPrefs(registry);
@@ -1577,7 +1570,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
login_detection::prefs::RegisterProfilePrefs(registry);
lookalikes::RegisterProfilePrefs(registry);
MediaCaptureDevicesDispatcher::RegisterProfilePrefs(registry);
- MediaDeviceIDSalt::RegisterProfilePrefs(registry);
+ media_device_salt::MediaDeviceIDSalt::RegisterProfilePrefs(registry);
MediaEngagementService::RegisterProfilePrefs(registry);
MediaStorageIdSalt::RegisterProfilePrefs(registry);
metrics::RegisterDemographicsProfilePrefs(registry);
@@ -1588,7 +1581,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
password_manager::PasswordManager::RegisterProfilePrefs(registry);
payments::RegisterProfilePrefs(registry);
performance_manager::user_tuning::prefs::RegisterProfilePrefs(registry);
- permissions::PermissionActionsHistory::RegisterProfilePrefs(registry);
+ permissions::RegisterProfilePrefs(registry);
PermissionBubbleMediaAccessHandler::RegisterProfilePrefs(registry);
PlatformNotificationServiceImpl::RegisterProfilePrefs(registry);
policy::URLBlocklistManager::RegisterProfilePrefs(registry);
@@ -1673,7 +1666,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
#endif
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
- ChildAccountService::RegisterProfilePrefs(registry);
+ supervised_user::ChildAccountService::RegisterProfilePrefs(registry);
supervised_user::SupervisedUserService::RegisterProfilePrefs(registry);
#endif
@@ -1727,11 +1720,13 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
policy::DeveloperToolsPolicyHandler::RegisterProfilePrefs(registry);
PromoService::RegisterProfilePrefs(registry);
RegisterReadAnythingProfilePrefs(registry);
+ RegisterSafetyHubProfilePrefs(registry);
settings::SettingsUI::RegisterProfilePrefs(registry);
send_tab_to_self::RegisterProfilePrefs(registry);
signin::RegisterProfilePrefs(registry);
StartupBrowserCreator::RegisterProfilePrefs(registry);
tab_search_prefs::RegisterProfilePrefs(registry);
+ ThemeColorPickerHandler::RegisterProfilePrefs(registry);
RecipesService::RegisterProfilePrefs(registry);
UnifiedAutoplayConfig::RegisterProfilePrefs(registry);
CartService::RegisterProfilePrefs(registry);
@@ -1747,7 +1742,10 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
extensions::login_api::RegisterProfilePrefs(registry);
extensions::platform_keys::RegisterProfilePrefs(registry);
certificate_manager::CertificatesHandler::RegisterProfilePrefs(registry);
+ chromeos::cloud_storage::RegisterProfilePrefs(registry);
+ chromeos::cloud_upload::RegisterProfilePrefs(registry);
policy::PolicyCertService::RegisterProfilePrefs(registry);
+ quickoffice::RegisterProfilePrefs(registry);
registry->RegisterBooleanPref(prefs::kDeskAPIThirdPartyAccessEnabled, false);
registry->RegisterListPref(prefs::kDeskAPIThirdPartyAllowlist);
registry->RegisterBooleanPref(prefs::kInsightsExtensionEnabled, false);
@@ -1756,6 +1754,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
registry->RegisterListPref(
chromeos::prefs::kKeepFullscreenWithoutNotificationUrlAllowList,
PrefRegistry::PUBLIC);
+ ::reporting::RegisterProfilePrefs(registry);
#if BUILDFLAG(USE_CUPS)
extensions::PrintingAPIHandler::RegisterProfilePrefs(registry);
#endif // BUILDFLAG(USE_CUPS)
@@ -1774,6 +1773,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
ash::ApkWebAppService::RegisterProfilePrefs(registry);
ash::app_time::AppActivityRegistry::RegisterProfilePrefs(registry);
ash::app_time::AppTimeController::RegisterProfilePrefs(registry);
+ ash::AshProxyMonitor::RegisterProfilePrefs(registry);
ash::assistant::prefs::RegisterProfilePrefs(registry);
ash::auth::AuthFactorConfig::RegisterPrefs(registry);
ash::bluetooth::DebugLogsManager::RegisterPrefs(registry);
@@ -1786,7 +1786,6 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
ash::FamilyUserChromeActivityMetrics::RegisterProfilePrefs(registry);
ash::FamilyUserMetricsService::RegisterProfilePrefs(registry);
ash::FamilyUserSessionMetrics::RegisterProfilePrefs(registry);
- crosapi::NetworkSettingsServiceAsh::RegisterProfilePrefs(registry);
ash::InlineLoginHandlerImpl::RegisterProfilePrefs(registry);
ash::first_run::RegisterProfilePrefs(registry);
ash::file_system_provider::RegisterProfilePrefs(registry);
@@ -1814,11 +1813,11 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
ash::ServicesCustomizationDocument::RegisterProfilePrefs(registry);
ash::settings::OSSettingsUI::RegisterProfilePrefs(registry);
ash::StartupUtils::RegisterOobeProfilePrefs(registry);
+ ash::UpdateNotificationShowingController::RegisterProfilePrefs(registry);
ash::user_image::prefs::RegisterProfilePrefs(registry);
ash::UserImageSyncObserver::RegisterProfilePrefs(registry);
ChromeMetricsServiceClient::RegisterProfilePrefs(registry);
crostini::prefs::RegisterProfilePrefs(registry);
- ash::attestation::TpmChallengeKey::RegisterProfilePrefs(registry);
flags_ui::PrefServiceFlagsStorage::RegisterProfilePrefs(registry);
guest_os::prefs::RegisterProfilePrefs(registry);
lock_screen_apps::StateController::RegisterProfilePrefs(registry);
@@ -1896,6 +1895,9 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
registry->RegisterBooleanPref(
webauthn::pref_names::kRemoteProxiedRequestsAllowed, false);
+ registry->RegisterStringPref(
+ webauthn::pref_names::kLastUsedPairingFromSyncPublicKey, "");
+
side_panel_prefs::RegisterProfilePrefs(registry);
#endif
@@ -1925,10 +1927,14 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
registry->RegisterTimePref(prefs::kDIPSTimerLastUpdate, base::Time());
#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
- registry->RegisterBooleanPref(
- prefs::kAccessibilityPdfOcrAlwaysActive, false,
- user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+ registry->RegisterBooleanPref(prefs::kAccessibilityPdfOcrAlwaysActive, false);
#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ registry->RegisterBooleanPref(kClearUserDataDir1Pref, false);
+#endif
+
+ registry->RegisterBooleanPref(prefs::kBlockTruncatedCookies, true);
}
void RegisterUserProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
@@ -1943,7 +1949,8 @@ void RegisterUserProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
::android::RegisterUserProfilePrefs(registry);
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
- ash::RegisterUserProfilePrefs(registry);
+ ash::RegisterUserProfilePrefs(registry, locale);
+ ash::TokenHandleFetcher::RegisterPrefs(registry);
#endif
}
@@ -1952,9 +1959,10 @@ void RegisterScreenshotPrefs(PrefRegistrySimple* registry) {
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
-void RegisterSigninProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
+void RegisterSigninProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
+ std::string_view country) {
RegisterProfilePrefs(registry, g_browser_process->GetApplicationLocale());
- ash::RegisterSigninProfilePrefs(registry);
+ ash::RegisterSigninProfilePrefs(registry, country);
}
#endif
@@ -1972,18 +1980,6 @@ void MigrateObsoleteLocalStatePrefs(PrefService* local_state) {
// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS
// Please don't delete the preceding line. It is used by PRESUBMIT.py.
- // Added 06/2022.
- local_state->ClearPref(kBackgroundTracingLastUpload);
- local_state->ClearPref(kStabilityGpuCrashCount);
- local_state->ClearPref(kStabilityRendererCrashCount);
- local_state->ClearPref(kStabilityExtensionRendererCrashCount);
-#if !BUILDFLAG(IS_ANDROID)
- local_state->ClearPref(kStabilityPageLoadCount);
-#endif
-
- // Added 07/2002.
- local_state->ClearPref(kStabilityCrashCount);
-
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Added 09/2022
local_state->ClearPref(kUsersLastInputMethod);
@@ -2100,6 +2096,11 @@ void MigrateObsoleteLocalStatePrefs(PrefService* local_state) {
local_state->ClearPref(kLegacyHoverCardImagesEnabled);
#endif
+ // Added 07/2023.
+ local_state->ClearPref(kShutdownNumProcesses);
+ local_state->ClearPref(kShutdownNumProcessesSlow);
+ local_state->ClearPref(kShutdownType);
+
// Please don't delete the following line. It is used by PRESUBMIT.py.
// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS
@@ -2137,67 +2138,10 @@ void MigrateObsoleteProfilePrefs(Profile* profile) {
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Added 06/2022.
+ // TODO(crbug.com/1476489): Remove when unit test code is updated.
profile_prefs->ClearPref(kTokenServiceDiceCompatible);
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
-#if BUILDFLAG(IS_ANDROID)
- // Added 06/2022.
- profile_prefs->ClearPref(kDownloadLaterPromptStatus);
-#endif // BUILDFLAG(IS_ANDROID)
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- // Added 06/2022.
- profile_prefs->ClearPref(kImprovedShortcutsNotificationShownCount);
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
- // Added 06/2022.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- profile_prefs->ClearPref(kU2fSecurityKeyApiEnabled);
-#endif
- profile_prefs->ClearPref(prefs::kCloudPrintSubmitEnabled);
-
- // Added 06/2022.
- profile_prefs->ClearPref(kPrivacySandboxPreferencesReconciled);
-
- // Added 07/2022
- profile_prefs->ClearPref(kPrivacySandboxFlocEnabled);
- profile_prefs->ClearPref(kPrivacySandboxFlocDataAccessibleSince);
- profile_prefs->ClearPref(kPrivacySandboxApisEnabledV2Init);
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- // Added 07/2022.
- profile_prefs->ClearPref(kExtensionToolbar);
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- // Added 07/2022.
- profile_prefs->ClearPref(kCanShowFolderSelectionNudge);
- profile_prefs->ClearPref(kSettingsShowOSBanner);
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- // Added 08/2022.
- profile_prefs->ClearPref(kSecurityTokenSessionNotificationDisplayed);
-#endif
-
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
- // Added 08/2022.
- profile_prefs->ClearPref(kProfileAvatarTutorialShown);
-#endif
-
-#if BUILDFLAG(IS_LINUX)
- // Added 08/2022.
- if (profile_prefs->HasPrefPath(prefs::kUsesSystemThemeDeprecated)) {
- auto migrated_theme =
- profile_prefs->GetBoolean(prefs::kUsesSystemThemeDeprecated)
- ? ui::SystemTheme::kGtk
- : ui::SystemTheme::kDefault;
- profile_prefs->SetInteger(prefs::kSystemTheme,
- static_cast<int>(migrated_theme));
- }
- profile_prefs->ClearPref(prefs::kUsesSystemThemeDeprecated);
-#endif
-
// Added 09/2022.
profile_prefs->ClearPref(kPrivacySandboxFirstPartySetsDataAccessAllowed);
@@ -2421,6 +2365,57 @@ void MigrateObsoleteProfilePrefs(Profile* profile) {
profile_prefs->ClearPref(kVideoPreviewsType);
#endif // BUILDFLAG(ENABLE_FEED_V2)
+ // Added 06/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ profile_prefs->ClearPref(kGaiaLastOnlineSignInTime);
+ profile_prefs->ClearPref(kSAMLLastGAIASignInTime);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+ // Added 06/2023.
+#if BUILDFLAG(IS_ANDROID)
+ profile_prefs->ClearPref(kPrefExplicitLanguageAskShown);
+#endif // BUILDFLAG(IS_ANDROID)
+
+// Added 07/2023.
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+ profile_prefs->ClearPref(kUnifiedConsentMigrationState);
+#endif
+
+ // Added 07/2023.
+ profile_prefs->ClearPref(kPasswordsGroupingInfoRequested);
+
+ // Added 07/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ profile_prefs->ClearPref(kPowerMetricsIdleScreenOffCount);
+ profile_prefs->ClearPref(kPowerMetricsIdleSuspendCount);
+ profile_prefs->ClearPref(kPowerMetricsLidClosedSuspendCount);
+#endif
+ syncer::SyncPrefs::MigrateAutofillWalletImportEnabledPref(profile_prefs);
+
+// Added 07/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ profile_prefs->ClearPref(kHatsPrivacyHubBaselineIsSelected);
+ profile_prefs->ClearPref(kHatsPrivacyHubBaselineCycleEndTs);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+// Added 07/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ profile_prefs->ClearPref(kClearUserDataDir1Pref);
+#endif
+
+ // Added 08/2023.
+ invalidation::InvalidatorRegistrarWithMemory::ClearDeprecatedPrefs(
+ profile_prefs);
+ invalidation::PerUserTopicSubscriptionManager::ClearDeprecatedPrefs(
+ profile_prefs);
+ invalidation::FCMInvalidationService::ClearDeprecatedPrefs(profile_prefs);
+
+ // Added 08/2023.
+ profile_prefs->ClearPref(kDriveFsBulkPinningMaxQueueSize);
+
+ // Added 09/2023
+ profile_prefs->ClearPref(kPrivacySandboxM1Unrestricted);
+
// Please don't delete the following line. It is used by PRESUBMIT.py.
// END_MIGRATE_OBSOLETE_PROFILE_PREFS
diff --git a/chromium/chrome/browser/prefs/browser_prefs.h b/chromium/chrome/browser/prefs/browser_prefs.h
index 24fca42861c..6e148d76732 100644
--- a/chromium/chrome/browser/prefs/browser_prefs.h
+++ b/chromium/chrome/browser/prefs/browser_prefs.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_PREFS_BROWSER_PREFS_H_
#include <string>
+#include <string_view>
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
@@ -18,6 +19,10 @@ namespace user_prefs {
class PrefRegistrySyncable;
}
+// Gets the current country from the browser process. May be empty if the
+// browser process cannot supply the country.
+std::string GetCountry();
+
// Register all prefs that will be used via the local state PrefService.
void RegisterLocalState(PrefRegistrySimple* registry);
@@ -34,8 +39,13 @@ void RegisterUserProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Register all prefs that will be used via a PrefService attached to the
-// sign-in profile using the locale of |g_browser_process|.
-void RegisterSigninProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+// sign-in profile using the locale of |g_browser_process|. |country| should be
+// the permanent country code stored for this client in lowercase ISO 3166-1
+// alpha-2. It can be used to pick country specific default values. May be
+// empty May be empty in which case country specific preferences will be unable
+// to be set.
+void RegisterSigninProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
+ std::string_view country);
#endif
// Migrate/cleanup deprecated prefs in |local_state|. Over time, long deprecated
diff --git a/chromium/chrome/browser/prefs/chrome_pref_service_factory.cc b/chromium/chrome/browser/prefs/chrome_pref_service_factory.cc
index 229154d85a9..2cdd468f6e7 100644
--- a/chromium/chrome/browser/prefs/chrome_pref_service_factory.cc
+++ b/chromium/chrome/browser/prefs/chrome_pref_service_factory.cc
@@ -31,7 +31,7 @@
#include "chrome/browser/prefs/profile_pref_store_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/glue/sync_start_util.h"
-#include "chrome/browser/ui/profile_error_dialog.h"
+#include "chrome/browser/ui/profiles/profile_error_dialog.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
@@ -222,13 +222,7 @@ enum SettingsEnforcementGroup {
SettingsEnforcementGroup GetSettingsEnforcementGroup() {
#if BUILDFLAG(IS_WIN)
if (!g_disable_domain_check_for_testing) {
- static bool first_call = true;
static const bool is_domain_joined = base::IsEnterpriseDevice();
- if (first_call) {
- UMA_HISTOGRAM_BOOLEAN("Settings.TrackedPreferencesNoEnforcementOnDomain",
- is_domain_joined);
- first_call = false;
- }
if (is_domain_joined)
return GROUP_NO_ENFORCEMENT;
}
@@ -428,9 +422,7 @@ std::unique_ptr<sync_preferences::PrefServiceSyncable> CreateProfilePrefs(
std::move(extension_prefs),
std::move(standalone_browser_prefs), async, connector);
- if (base::FeatureList::IsEnabled(syncer::kEnablePreferencesAccountStorage) &&
- base::FeatureList::IsEnabled(
- syncer::kSyncEnablePersistentStorageForAccountPreferences)) {
+ if (base::FeatureList::IsEnabled(syncer::kEnablePreferencesAccountStorage)) {
// Note: Only mobile platforms are targeted as part of the experiment,
// which do not require preference protection. Hence pref filters and
// ProfilePrefStoreManager::CreateProfilePrefStore() can be avoided.
diff --git a/chromium/chrome/browser/prefs/chrome_pref_service_unittest.cc b/chromium/chrome/browser/prefs/chrome_pref_service_unittest.cc
index 904400855f7..bb736b8941c 100644
--- a/chromium/chrome/browser/prefs/chrome_pref_service_unittest.cc
+++ b/chromium/chrome/browser/prefs/chrome_pref_service_unittest.cc
@@ -6,12 +6,12 @@
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/prefs/chrome_command_line_pref_store.h"
-#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/common/content_switches.h"
#include "content/public/test/test_renderer_host.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
@@ -20,11 +20,11 @@ using content::RenderViewHostTester;
TEST(ChromePrefServiceTest, UpdateCommandLinePrefStore) {
TestingPrefServiceSimple prefs;
- prefs.registry()->RegisterBooleanPref(prefs::kCloudPrintProxyEnabled, false);
+ prefs.registry()->RegisterBooleanPref(prefs::kDisable3DAPIs, false);
// Check to make sure the value is as expected.
const PrefService::Preference* pref =
- prefs.FindPreference(prefs::kCloudPrintProxyEnabled);
+ prefs.FindPreference(prefs::kDisable3DAPIs);
ASSERT_TRUE(pref);
const base::Value* value = pref->GetValue();
ASSERT_TRUE(value);
@@ -34,11 +34,11 @@ TEST(ChromePrefServiceTest, UpdateCommandLinePrefStore) {
// Change the command line.
base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM);
- cmd_line.AppendSwitch(switches::kEnableCloudPrintProxy);
+ cmd_line.AppendSwitch(switches::kDisable3DAPIs);
// Call UpdateCommandLinePrefStore and check to see if the value has changed.
prefs.UpdateCommandLinePrefStore(new ChromeCommandLinePrefStore(&cmd_line));
- pref = prefs.FindPreference(prefs::kCloudPrintProxyEnabled);
+ pref = prefs.FindPreference(prefs::kDisable3DAPIs);
ASSERT_TRUE(pref);
value = pref->GetValue();
ASSERT_TRUE(value);
diff --git a/chromium/chrome/browser/prefs/pref_metrics_service.cc b/chromium/chrome/browser/prefs/pref_metrics_service.cc
index 0630522df00..bd94925a491 100644
--- a/chromium/chrome/browser/prefs/pref_metrics_service.cc
+++ b/chromium/chrome/browser/prefs/pref_metrics_service.cc
@@ -123,10 +123,11 @@ PrefMetricsService::Factory::Factory()
PrefMetricsService::Factory::~Factory() {
}
-KeyedService* PrefMetricsService::Factory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+PrefMetricsService::Factory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
- return new PrefMetricsService(profile);
+ return std::make_unique<PrefMetricsService>(profile);
}
bool PrefMetricsService::Factory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/chromium/chrome/browser/prefs/pref_metrics_service.h b/chromium/chrome/browser/prefs/pref_metrics_service.h
index 3f1d316d83d..1834f173d44 100644
--- a/chromium/chrome/browser/prefs/pref_metrics_service.h
+++ b/chromium/chrome/browser/prefs/pref_metrics_service.h
@@ -41,7 +41,7 @@ class PrefMetricsService : public KeyedService {
~Factory() override;
// BrowserContextKeyedServiceFactory implementation
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
bool ServiceIsCreatedWithBrowserContext() const override;
};
diff --git a/chromium/chrome/browser/prefs/pref_service_browsertest.cc b/chromium/chrome/browser/prefs/pref_service_browsertest.cc
index 51e74788756..af2e178217f 100644
--- a/chromium/chrome/browser/prefs/pref_service_browsertest.cc
+++ b/chromium/chrome/browser/prefs/pref_service_browsertest.cc
@@ -30,8 +30,14 @@
typedef InProcessBrowserTest PreservedWindowPlacement;
using ::testing::Optional;
+namespace {
+
+const gfx::Rect window_frame = gfx::Rect(20, 40, 600, 600);
+
+} // namespace
+
IN_PROC_BROWSER_TEST_F(PreservedWindowPlacement, PRE_Test) {
- browser()->window()->SetBounds(gfx::Rect(20, 30, 600, 600));
+ browser()->window()->SetBounds(window_frame);
}
// Fails on Chrome OS as the browser thinks it is restarting after a crash, see
@@ -43,7 +49,7 @@ IN_PROC_BROWSER_TEST_F(PreservedWindowPlacement, PRE_Test) {
#endif
IN_PROC_BROWSER_TEST_F(PreservedWindowPlacement, MAYBE_Test) {
gfx::Rect bounds = browser()->window()->GetBounds();
- gfx::Rect expected_bounds(gfx::Rect(20, 30, 600, 600));
+ gfx::Rect expected_bounds(window_frame);
ASSERT_EQ(expected_bounds.ToString(), bounds.ToString());
}
diff --git a/chromium/chrome/browser/prefs/pref_service_incognito_allowlist.cc b/chromium/chrome/browser/prefs/pref_service_incognito_allowlist.cc
index 0be2b6f1658..69de284c8c3 100644
--- a/chromium/chrome/browser/prefs/pref_service_incognito_allowlist.cc
+++ b/chromium/chrome/browser/prefs/pref_service_incognito_allowlist.cc
@@ -64,9 +64,9 @@ const char* const kPersistentPrefNames[] = {
ash::prefs::kAccessibilityChromeVoxVirtualBrailleColumns,
ash::prefs::kAccessibilityChromeVoxVirtualBrailleRows,
ash::prefs::kAccessibilityChromeVoxVoiceName,
- ash::prefs::kAccessibilityColorFiltering,
+ ash::prefs::kAccessibilityColorCorrectionEnabled,
ash::prefs::kAccessibilityColorVisionCorrectionAmount,
- ash::prefs::kAccessibilityColorVisionDeficiencyType,
+ ash::prefs::kAccessibilityColorVisionCorrectionType,
ash::prefs::kAccessibilityHighContrastEnabled,
ash::prefs::kAccessibilityScreenMagnifierCenterFocus,
ash::prefs::kAccessibilityScreenMagnifierEnabled,
diff --git a/chromium/chrome/browser/printing/browser_printing_context_factory_for_test.cc b/chromium/chrome/browser/printing/browser_printing_context_factory_for_test.cc
index acf58598444..50a7f8c5fad 100644
--- a/chromium/chrome/browser/printing/browser_printing_context_factory_for_test.cc
+++ b/chromium/chrome/browser/printing/browser_printing_context_factory_for_test.cc
@@ -71,7 +71,7 @@ BrowserPrintingContextFactoryForTest::CreatePrintingContext(
if (fail_on_use_default_settings_) {
context->SetUseDefaultSettingsFails();
}
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
+#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
if (cancel_on_ask_user_for_settings_) {
context->SetAskUserForSettingsCanceled();
}
@@ -79,11 +79,9 @@ BrowserPrintingContextFactoryForTest::CreatePrintingContext(
context->SetUserSettings(*test::MakeUserModifiedPrintSettings(printer_name_));
- context->SetOnNewDocumentCallback(
- base::BindRepeating(&BrowserPrintingContextFactoryForTest::OnNewDocument,
- base::Unretained(this)));
+ context->SetOnNewDocumentCallback(on_new_document_callback_);
- return std::move(context);
+ return context;
}
void BrowserPrintingContextFactoryForTest::SetPrinterNameForSubsequentContexts(
@@ -139,10 +137,9 @@ void BrowserPrintingContextFactoryForTest::
}
#endif
-void BrowserPrintingContextFactoryForTest::OnNewDocument(
- const PrintSettings& settings) {
- ++new_document_called_count_;
- document_print_settings_ = settings;
+void BrowserPrintingContextFactoryForTest::SetOnNewDocumentCallback(
+ TestPrintingContext::OnNewDocumentCallback callback) {
+ on_new_document_callback_ = std::move(callback);
}
} // namespace printing
diff --git a/chromium/chrome/browser/printing/browser_printing_context_factory_for_test.h b/chromium/chrome/browser/printing/browser_printing_context_factory_for_test.h
index a8665372c08..c5d10d73aaa 100644
--- a/chromium/chrome/browser/printing/browser_printing_context_factory_for_test.h
+++ b/chromium/chrome/browser/printing/browser_printing_context_factory_for_test.h
@@ -10,10 +10,9 @@
#include "build/build_config.h"
#include "printing/buildflags/buildflags.h"
-#include "printing/print_settings.h"
#include "printing/printing_context.h"
#include "printing/printing_context_factory_for_test.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "printing/test_printing_context.h"
namespace printing {
@@ -41,13 +40,8 @@ class BrowserPrintingContextFactoryForTest
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
void SetCancelErrorOnAskUserForSettings();
#endif
- void OnNewDocument(const PrintSettings& settings);
-
- int new_document_called_count() { return new_document_called_count_; }
-
- const absl::optional<PrintSettings>& document_print_settings() const {
- return document_print_settings_;
- }
+ void SetOnNewDocumentCallback(
+ TestPrintingContext::OnNewDocumentCallback callback);
private:
std::string printer_name_;
@@ -64,8 +58,7 @@ class BrowserPrintingContextFactoryForTest
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
bool cancel_on_ask_user_for_settings_ = false;
#endif
- int new_document_called_count_ = 0;
- absl::optional<PrintSettings> document_print_settings_;
+ TestPrintingContext::OnNewDocumentCallback on_new_document_callback_;
};
} // namespace printing
diff --git a/chromium/chrome/browser/printing/pdf_to_emf_converter.cc b/chromium/chrome/browser/printing/pdf_to_emf_converter.cc
index 3bcfbaefdee..cc71a71799b 100644
--- a/chromium/chrome/browser/printing/pdf_to_emf_converter.cc
+++ b/chromium/chrome/browser/printing/pdf_to_emf_converter.cc
@@ -9,6 +9,7 @@
#include <memory>
#include <string>
+#include <string_view>
#include <utility>
#include <vector>
@@ -33,6 +34,7 @@
#include "printing/emf_win.h"
#include "printing/pdf_render_settings.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
using content::BrowserThread;
@@ -44,12 +46,12 @@ namespace {
// comment records.
class PostScriptMetaFile : public Emf {
public:
- PostScriptMetaFile() {}
+ PostScriptMetaFile() = default;
PostScriptMetaFile(const PostScriptMetaFile&) = delete;
PostScriptMetaFile& operator=(const PostScriptMetaFile&) = delete;
- ~PostScriptMetaFile() override {}
+ ~PostScriptMetaFile() override = default;
private:
// Emf:
@@ -71,6 +73,7 @@ class PdfConverterImpl : public PdfConverter {
PdfConverterImpl(scoped_refptr<base::RefCountedMemory> data,
const PdfRenderSettings& conversion_settings,
const absl::optional<bool>& use_skia,
+ const GURL& url,
StartCallback start_callback);
PdfConverterImpl(const PdfConverterImpl&) = delete;
@@ -131,7 +134,7 @@ class PdfConverterImpl : public PdfConverter {
void OnPageDone(base::ReadOnlySharedMemoryRegion emf_region,
float scale_factor);
- void OnFailed(const std::string& error_message);
+ void OnFailed(std::string_view error_message);
void RecordConversionMetrics();
@@ -139,6 +142,8 @@ class PdfConverterImpl : public PdfConverter {
absl::optional<bool> use_skia_;
+ const GURL url_;
+
// Document loaded callback.
PdfConverter::StartCallback start_callback_;
@@ -202,9 +207,11 @@ bool PostScriptMetaFile::SafePlayback(HDC hdc) const {
PdfConverterImpl::PdfConverterImpl(scoped_refptr<base::RefCountedMemory> data,
const PdfRenderSettings& settings,
const absl::optional<bool>& use_skia,
+ const GURL& url,
StartCallback start_callback)
: settings_(settings),
use_skia_(use_skia),
+ url_(url),
start_callback_(std::move(start_callback)) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(start_callback_);
@@ -220,14 +227,14 @@ PdfConverterImpl::~PdfConverterImpl() {
void PdfConverterImpl::Initialize(scoped_refptr<base::RefCountedMemory> data) {
if (simulate_failure_initializing_conversion_) {
- OnFailed(std::string("Failed to create PDF data mapping."));
+ OnFailed("Failed to create PDF data mapping.");
return;
}
base::MappedReadOnlyRegion memory =
base::ReadOnlySharedMemoryRegion::Create(data->size());
if (!memory.IsValid()) {
- OnFailed(std::string("Failed to create PDF data mapping."));
+ OnFailed("Failed to create PDF data mapping.");
return;
}
@@ -238,7 +245,7 @@ void PdfConverterImpl::Initialize(scoped_refptr<base::RefCountedMemory> data) {
pdf_to_emf_converter_factory_.BindNewPipeAndPassReceiver());
pdf_to_emf_converter_factory_.set_disconnect_handler(base::BindOnce(
&PdfConverterImpl::OnFailed, weak_ptr_factory_.GetWeakPtr(),
- std::string("Connection to PdfToEmfConverterFactory error.")));
+ "Connection to PdfToEmfConverterFactory error."));
pdf_to_emf_converter_factory_->CreateConverter(
std::move(memory.region), settings_,
@@ -254,7 +261,8 @@ void PdfConverterImpl::OnPageCount(
pdf_to_emf_converter_.Bind(std::move(converter));
pdf_to_emf_converter_.set_disconnect_handler(base::BindOnce(
&PdfConverterImpl::OnFailed, weak_ptr_factory_.GetWeakPtr(),
- std::string("Connection to PdfToEmfConverter error.")));
+ "Connection to PdfToEmfConverter error."));
+ pdf_to_emf_converter_->SetWebContentsURL(url_);
if (use_skia_) {
pdf_to_emf_converter_->SetUseSkiaRendererPolicy(*use_skia_);
}
@@ -272,7 +280,7 @@ void PdfConverterImpl::GetPage(
get_page_callbacks_.push(GetPageCallbackData(page_index, get_page_callback));
if (!pdf_to_emf_converter_)
- return OnFailed(std::string("No PdfToEmfConverter."));
+ return OnFailed("No PdfToEmfConverter.");
pdf_to_emf_converter_->ConvertPage(
page_index, base::BindOnce(&PdfConverterImpl::OnPageDone,
@@ -284,7 +292,7 @@ void PdfConverterImpl::OnPageDone(base::ReadOnlySharedMemoryRegion emf_region,
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (get_page_callbacks_.empty())
- return OnFailed(std::string("No get_page callbacks."));
+ return OnFailed("No get_page callbacks.");
GetPageCallbackData& data = get_page_callbacks_.front();
std::unique_ptr<MetafilePlayer> metafile;
@@ -315,11 +323,11 @@ void PdfConverterImpl::Stop() {
pdf_to_emf_converter_.reset();
}
-void PdfConverterImpl::OnFailed(const std::string& error_message) {
+void PdfConverterImpl::OnFailed(std::string_view error_message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
LOG(ERROR) << "Failed to convert PDF: " << error_message;
base::WeakPtr<PdfConverterImpl> weak_this = weak_ptr_factory_.GetWeakPtr();
- if (!start_callback_.is_null()) {
+ if (start_callback_) {
std::move(start_callback_).Run(/*page_count=*/0);
if (!weak_this)
return; // Protect against the `start_callback_` deleting `this`.
@@ -372,9 +380,6 @@ void PdfConverterImpl::RecordConversionMetrics() {
"Printing.ConversionSize.PostScript3WithType42Fonts",
average_page_size_in_kb);
return;
- default:
- NOTREACHED();
- return;
}
}
@@ -387,9 +392,10 @@ std::unique_ptr<PdfConverter> PdfConverter::StartPdfConverter(
scoped_refptr<base::RefCountedMemory> data,
const PdfRenderSettings& conversion_settings,
const absl::optional<bool>& use_skia,
+ const GURL& url,
StartCallback start_callback) {
return std::make_unique<PdfConverterImpl>(data, conversion_settings, use_skia,
- std::move(start_callback));
+ url, std::move(start_callback));
}
ScopedSimulateFailureCreatingTempFileForTests::
diff --git a/chromium/chrome/browser/printing/pdf_to_emf_converter.h b/chromium/chrome/browser/printing/pdf_to_emf_converter.h
index 836a2ed38aa..1ffa1c38bd2 100644
--- a/chromium/chrome/browser/printing/pdf_to_emf_converter.h
+++ b/chromium/chrome/browser/printing/pdf_to_emf_converter.h
@@ -11,6 +11,8 @@
#include "base/memory/ref_counted_memory.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+class GURL;
+
namespace printing {
class MetafilePlayer;
@@ -31,6 +33,7 @@ class PdfConverter {
scoped_refptr<base::RefCountedMemory> data,
const PdfRenderSettings& conversion_settings,
const absl::optional<bool>& use_skia,
+ const GURL& url,
StartCallback start_callback);
// Requests conversion of the page. `page_index` is 0-base page index for the
diff --git a/chromium/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc b/chromium/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc
index 5bce4b49b98..45cb02b1657 100644
--- a/chromium/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc
+++ b/chromium/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc
@@ -158,7 +158,7 @@ class PdfToEmfConverterBrowserTest
base::RunLoop run_loop;
uint32_t page_count = kInvalidPageCount;
pdf_converter_ = PdfConverter::StartPdfConverter(
- test_input_, pdf_settings, /*use_skia=*/GetParam(),
+ test_input_, pdf_settings, /*use_skia=*/GetParam(), GURL(),
base::BindOnce(&StartCallbackImpl, run_loop.QuitClosure(),
&page_count));
run_loop.Run();
@@ -249,7 +249,7 @@ IN_PROC_BROWSER_TEST_P(PdfToEmfConverterBrowserTest, FailureNoTempFile) {
uint32_t page_count = kInvalidPageCount;
std::unique_ptr<PdfConverter> pdf_converter = PdfConverter::StartPdfConverter(
base::MakeRefCounted<base::RefCountedStaticMemory>(), PdfRenderSettings(),
- /*use_skia=*/GetParam(),
+ /*use_skia=*/GetParam(), GURL(),
base::BindOnce(&StartCallbackImpl, run_loop.QuitClosure(), &page_count));
run_loop.Run();
EXPECT_EQ(0u, page_count);
@@ -262,7 +262,7 @@ IN_PROC_BROWSER_TEST_P(PdfToEmfConverterBrowserTest, FailureBadPdf) {
base::RunLoop run_loop;
uint32_t page_count = kInvalidPageCount;
std::unique_ptr<PdfConverter> pdf_converter = PdfConverter::StartPdfConverter(
- bad_pdf_data, PdfRenderSettings(), /*use_skia=*/GetParam(),
+ bad_pdf_data, PdfRenderSettings(), /*use_skia=*/GetParam(), GURL(),
base::BindOnce(&StartCallbackImpl, run_loop.QuitClosure(), &page_count));
run_loop.Run();
EXPECT_EQ(0u, page_count);
diff --git a/chromium/chrome/browser/printing/print_backend_browsertest.cc b/chromium/chrome/browser/printing/print_backend_browsertest.cc
index cb9b2943ebd..4f077d300fa 100644
--- a/chromium/chrome/browser/printing/print_backend_browsertest.cc
+++ b/chromium/chrome/browser/printing/print_backend_browsertest.cc
@@ -24,6 +24,7 @@
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
+#include "chrome/browser/printing/print_backend_service_manager.h"
#include "chrome/browser/printing/print_backend_service_test_impl.h"
#include "chrome/browser/printing/print_test_utils.h"
#include "chrome/common/chrome_paths.h"
@@ -141,6 +142,7 @@ class PrintBackendBrowserTest : public InProcessBrowserTest {
InProcessBrowserTest::TearDown();
PrintingContext::SetPrintingContextFactoryForTest(/*factory=*/nullptr);
PrintBackend::SetPrintBackendForTesting(/*print_backend=*/nullptr);
+ PrintBackendServiceManager::ResetForTesting();
}
// Load the test backend with a default printer driver.
@@ -590,22 +592,22 @@ IN_PROC_BROWSER_TEST_F(PrintBackendBrowserTest, GetPaperPrintableArea) {
}
}
ASSERT_TRUE(non_default_paper.has_value());
- EXPECT_EQ(non_default_paper->printable_area_um,
- gfx::Rect(non_default_paper->size_um));
+ EXPECT_EQ(non_default_paper->printable_area_um(),
+ gfx::Rect(non_default_paper->size_um()));
// Request the printable area for this paper size, which should no longer
// match the physical size but have real printable area values.
gfx::Rect printable_area_um;
PrintSettings::RequestedMedia media(
- /*.size_microns =*/non_default_paper->size_um,
- /*.vendor_id = */ non_default_paper->vendor_id);
+ /*.size_microns =*/non_default_paper->size_um(),
+ /*.vendor_id = */ non_default_paper->vendor_id());
GetPrintBackendService()->GetPaperPrintableArea(
kDefaultPrinterName, media,
base::BindOnce(&PrintBackendBrowserTest::OnDidGetPaperPrintableArea,
base::Unretained(this), std::ref(printable_area_um)));
WaitUntilCallbackReceived();
ASSERT_TRUE(!printable_area_um.IsEmpty());
- EXPECT_NE(printable_area_um, non_default_paper->printable_area_um);
+ EXPECT_NE(printable_area_um, non_default_paper->printable_area_um());
}
#endif // BUILDFLAG(IS_WIN)
diff --git a/chromium/chrome/browser/printing/print_backend_service_manager.cc b/chromium/chrome/browser/printing/print_backend_service_manager.cc
index c121bc50d33..ffb10f03746 100644
--- a/chromium/chrome/browser/printing/print_backend_service_manager.cc
+++ b/chromium/chrome/browser/printing/print_backend_service_manager.cc
@@ -33,6 +33,10 @@
#include "printing/buildflags/buildflags.h"
#include "printing/printing_features.h"
+#if BUILDFLAG(IS_LINUX)
+#include "content/public/common/content_switches.h"
+#endif
+
#if BUILDFLAG(IS_WIN)
#include "base/win/win_util.h"
#include "chrome/browser/printing/printer_xml_parser_impl.h"
@@ -687,7 +691,7 @@ PrintBackendServiceManager::RegisterClient(
query_clients_.insert(client_id);
break;
case ClientType::kQueryWithUi:
-#if !BUILDFLAG(IS_LINUX)
+#if !BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS)
if (!query_with_ui_clients_.empty())
return absl::nullopt;
#endif
@@ -847,6 +851,9 @@ PrintBackendServiceManager::GetServiceFromBundle(
host.BindNewPipeAndPassReceiver(),
content::ServiceProcessHost::Options()
.WithDisplayName(IDS_UTILITY_PROCESS_PRINT_BACKEND_SERVICE_NAME)
+#if BUILDFLAG(IS_LINUX)
+ .WithExtraCommandLineSwitches({switches::kMessageLoopTypeUi})
+#endif
.Pass());
host->BindBackend(service.BindNewPipeAndPassReceiver());
@@ -960,7 +967,7 @@ PrintBackendServiceManager::DetermineIdleTimeoutUpdateOnRegisteredClient(
break;
case ClientType::kQueryWithUi:
-#if BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS)
// No need to update if there were other query with UI clients.
if (query_with_ui_clients_.size() > 1)
return absl::nullopt;
diff --git a/chromium/chrome/browser/printing/print_backend_service_manager.h b/chromium/chrome/browser/printing/print_backend_service_manager.h
index 29bacaf7cd4..9b5d1346f8a 100644
--- a/chromium/chrome/browser/printing/print_backend_service_manager.h
+++ b/chromium/chrome/browser/printing/print_backend_service_manager.h
@@ -709,9 +709,9 @@ class PrintBackendServiceManager {
std::unique_ptr<crash_keys::ScopedPrinterInfo> crash_keys_;
// Override of service to use for testing.
- raw_ptr<mojo::Remote<mojom::PrintBackendService>, LeakedDanglingUntriaged>
+ raw_ptr<mojo::Remote<mojom::PrintBackendService>>
sandboxed_service_remote_for_test_ = nullptr;
- raw_ptr<mojo::Remote<mojom::PrintBackendService>, LeakedDanglingUntriaged>
+ raw_ptr<mojo::Remote<mojom::PrintBackendService>>
unsandboxed_service_remote_for_test_ = nullptr;
};
diff --git a/chromium/chrome/browser/printing/print_backend_service_manager_unittest.cc b/chromium/chrome/browser/printing/print_backend_service_manager_unittest.cc
index ed35d0e77d8..d2532e58563 100644
--- a/chromium/chrome/browser/printing/print_backend_service_manager_unittest.cc
+++ b/chromium/chrome/browser/printing/print_backend_service_manager_unittest.cc
@@ -30,7 +30,7 @@ const RemoteId kRemoteIdTestPrinter{2};
const ClientId kClientIdQuery1{1};
const ClientId kClientIdQuery2{2};
const ClientId kClientIdQueryWithUi1{5};
-#if BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS)
const ClientId kClientIdQueryWithUi2{6};
#endif
const ClientId kClientIdPrintDocument1{10};
@@ -44,7 +44,7 @@ const ClientsSet kTestQueryWithTwoClients{kClientIdQuery1, kClientIdQuery2};
const QueryWithUiClientsMap kTestQueryWithUiNoClients;
const QueryWithUiClientsMap kTestQueryWithUiOneClient{
{kClientIdQueryWithUi1, kRemoteIdEmpty}};
-#if BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS)
const QueryWithUiClientsMap kTestQueryWithUiTwoClients{
{kClientIdQueryWithUi1, kRemoteIdEmpty},
{kClientIdQueryWithUi2, kRemoteIdEmpty}};
@@ -132,7 +132,7 @@ TEST(PrintBackendServiceManagerTest,
PrintBackendServiceManager::ClientType::kQueryWithUi,
kMaxTimeout,
},
-#if BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS)
// A new query with UI client with an existing query with UI client
// should yield no new timeout needed.
{
@@ -277,7 +277,7 @@ TEST(PrintBackendServiceManagerTest,
PrintBackendServiceManager::ClientType::kQueryWithUi,
PrintBackendServiceManager::kClientsRegisteredResetOnIdleTimeout,
},
-#if BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS)
// Any remaining query with UI client should yield no new timeout needed.
{
kTestQueryNoClients,
diff --git a/chromium/chrome/browser/printing/print_browsertest.cc b/chromium/chrome/browser/printing/print_browsertest.cc
index e6dd15e3ec3..4b474905f52 100644
--- a/chromium/chrome/browser/printing/print_browsertest.cc
+++ b/chromium/chrome/browser/printing/print_browsertest.cc
@@ -9,9 +9,11 @@
#include <vector>
#include "base/auto_reset.h"
+#include "base/base64.h"
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/files/file_path.h"
+#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
@@ -26,6 +28,7 @@
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
+#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/printing/browser_printing_context_factory_for_test.h"
#include "chrome/browser/printing/print_error_dialog.h"
@@ -39,6 +42,7 @@
#include "chrome/browser/printing/test_print_preview_observer.h"
#include "chrome/browser/printing/test_print_view_manager.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_paths.h"
@@ -87,8 +91,11 @@
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
+#include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/size_f.h"
#if BUILDFLAG(ENABLE_OOP_PRINTING)
#include "chrome/browser/printing/print_backend_service_manager.h"
@@ -329,6 +336,7 @@ class TestPrintRenderFrame
mojo::AssociatedReceiver<mojom::PrintRenderFrame> receiver_{this};
};
+// Lives on the UI thread.
class TestPrintViewManagerForDLP : public TestPrintViewManager {
public:
// Used to simulate Data Leak Prevention polices and possible user actions.
@@ -369,21 +377,27 @@ class TestPrintViewManagerForDLP : public TestPrintViewManager {
RestrictionLevel restriction_level)
: TestPrintViewManager(web_contents),
restriction_level_(restriction_level) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
PrintViewManager::SetReceiverImplForTesting(this);
}
TestPrintViewManagerForDLP(const TestPrintViewManagerForDLP&) = delete;
TestPrintViewManagerForDLP& operator=(const TestPrintViewManagerForDLP&) =
delete;
~TestPrintViewManagerForDLP() override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
PrintViewManager::SetReceiverImplForTesting(nullptr);
}
- PrintAllowance GetPrintAllowance() const { return allowance_; }
+ PrintAllowance GetPrintAllowance() const {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ return allowance_;
+ }
private:
void RejectPrintPreviewRequestIfRestricted(
content::GlobalRenderFrameHostId rfh_id,
base::OnceCallback<void(bool)> callback) override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
switch (restriction_level_) {
case RestrictionLevel::kNotSet:
case RestrictionLevel::kWarnAllow:
@@ -397,11 +411,13 @@ class TestPrintViewManagerForDLP : public TestPrintViewManager {
}
void PrintPreviewRejectedForTesting() override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
run_loop_->Quit();
allowance_ = PrintAllowance::kDisallowed;
}
void PrintPreviewAllowedForTesting() override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
run_loop_->Quit();
allowance_ = PrintAllowance::kAllowed;
}
@@ -410,14 +426,43 @@ class TestPrintViewManagerForDLP : public TestPrintViewManager {
PrintAllowance allowance_ = PrintAllowance::kUnknown;
};
+PrintBrowserTest::WorkerHelper::WorkerHelper(
+ base::WeakPtr<PrintBrowserTest> owner)
+ : owner_(owner) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
+PrintBrowserTest::WorkerHelper::~WorkerHelper() = default;
+
+void PrintBrowserTest::WorkerHelper::OnNewDocument(
+#if BUILDFLAG(IS_MAC)
+ bool destination_is_preview,
+#endif
+ const PrintSettings& settings) {
+ if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
+ content::GetUIThreadTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(&WorkerHelper::OnNewDocument, this,
+#if BUILDFLAG(IS_MAC)
+ destination_is_preview,
+#endif
+ settings));
+ return;
+ }
+ if (owner_) {
+ owner_->OnNewDocument(
+#if BUILDFLAG(IS_MAC)
+ destination_is_preview,
+#endif
+ settings);
+ }
+}
+
PrintBrowserTest::PrintBrowserTest() = default;
PrintBrowserTest::~PrintBrowserTest() = default;
void PrintBrowserTest::SetUp() {
test_print_backend_ = base::MakeRefCounted<TestPrintBackend>();
PrintBackend::SetPrintBackendForTesting(test_print_backend_.get());
- PrintingContext::SetPrintingContextFactoryForTest(
- &test_printing_context_factory_);
num_expected_messages_ = 1; // By default, only wait on one message.
num_received_messages_ = 0;
@@ -425,17 +470,26 @@ void PrintBrowserTest::SetUp() {
}
void PrintBrowserTest::SetUpOnMainThread() {
- // Safe to use `base::Unretained(this)` since this testing class
- // necessarily must outlive all interactions from the tests which will
- // run through the printing stack using derivatives of
- // `PrintViewManagerBase` and `PrintPreviewHandler`, which can trigger
- // this callback.
+ // Create `worker_helper_` here, once the UI thread exists.
+ worker_helper_ =
+ base::MakeRefCounted<WorkerHelper>(weak_factory_.GetWeakPtr());
+ test_printing_context_factory_.SetOnNewDocumentCallback(
+ base::BindRepeating(&WorkerHelper::OnNewDocument, worker_helper_));
+ PrintingContext::SetPrintingContextFactoryForTest(
+ &test_printing_context_factory_);
+
SetShowPrintErrorDialogForTest(base::BindRepeating(
- &PrintBrowserTest::ShowPrintErrorDialog, base::Unretained(this)));
+ &PrintBrowserTest::ShowPrintErrorDialog, weak_factory_.GetWeakPtr()));
host_resolver()->AddRule("*", "127.0.0.1");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
+
+ // `run_loop_` and `quit_callback_` are initialized here to avoid having a
+ // race between the last expected `CheckForQuit()` call and
+ // `WaitUntilCallbackReceived()` being called.
+ run_loop_ = std::make_unique<base::RunLoop>();
+ quit_callback_ = run_loop_->QuitClosure();
}
void PrintBrowserTest::TearDownOnMainThread() {
@@ -443,16 +497,20 @@ void PrintBrowserTest::TearDownOnMainThread() {
frame_content_.clear();
SetShowPrintErrorDialogForTest(base::NullCallback());
+
+ PrintingContext::SetPrintingContextFactoryForTest(/*factory=*/nullptr);
+ test_printing_context_factory_.SetOnNewDocumentCallback(base::NullCallback());
+
InProcessBrowserTest::TearDownOnMainThread();
}
void PrintBrowserTest::TearDown() {
InProcessBrowserTest::TearDown();
- PrintingContext::SetPrintingContextFactoryForTest(/*factory=*/nullptr);
PrintBackend::SetPrintBackendForTesting(/*print_backend=*/nullptr);
}
void PrintBrowserTest::AddPrinter(const std::string& printer_name) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
PrinterBasicInfo printer_info(
printer_name,
/*display_name=*/"test printer",
@@ -473,70 +531,91 @@ void PrintBrowserTest::AddPrinter(const std::string& printer_name) {
void PrintBrowserTest::SetPrinterNameForSubsequentContexts(
const std::string& printer_name) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
test_printing_context_factory_.SetPrinterNameForSubsequentContexts(
printer_name);
}
void PrintBrowserTest::PrintAndWaitUntilPreviewIsReady() {
- const PrintParams kParams;
- PrintAndWaitUntilPreviewIsReady(kParams);
+ PrintAndWaitUntilPreviewIsReady(PrintParams());
}
void PrintBrowserTest::PrintAndWaitUntilPreviewIsReady(
const PrintParams& params) {
- TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/false,
- params.pages_per_sheet);
-
- StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- /*print_renderer=*/mojo::NullAssociatedRemote(),
-#endif
- /*print_preview_disabled=*/false, params.print_only_selection);
-
- print_preview_observer.WaitUntilPreviewIsReady();
-
- set_rendered_page_count(print_preview_observer.rendered_page_count());
+ PrintAndWaitUntilPreviewIsReadyAndMaybeLoaded(params,
+ /*wait_for_loaded=*/false);
}
-void PrintBrowserTest::PrintAndWaitUntilPreviewIsReadyAndLoaded() {
- const PrintParams kParams;
- PrintAndWaitUntilPreviewIsReadyAndLoaded(kParams);
+content::WebContents*
+PrintBrowserTest::PrintAndWaitUntilPreviewIsReadyAndLoaded() {
+ return PrintAndWaitUntilPreviewIsReadyAndLoaded(PrintParams());
}
-void PrintBrowserTest::PrintAndWaitUntilPreviewIsReadyAndLoaded(
+content::WebContents*
+PrintBrowserTest::PrintAndWaitUntilPreviewIsReadyAndLoaded(
const PrintParams& params) {
- TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true,
+ return PrintAndWaitUntilPreviewIsReadyAndMaybeLoaded(
+ params,
+ /*wait_for_loaded=*/true);
+}
+
+content::WebContents*
+PrintBrowserTest::PrintAndWaitUntilPreviewIsReadyAndMaybeLoaded(
+ const PrintParams& params,
+ bool wait_for_loaded) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ TestPrintPreviewObserver print_preview_observer(wait_for_loaded,
params.pages_per_sheet);
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
- StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
+ switch (params.invoke_method) {
+ case InvokePrintMethod::kStartPrint:
+ StartPrint(web_contents,
#if BUILDFLAG(IS_CHROMEOS_ASH)
- /*print_renderer=*/mojo::NullAssociatedRemote(),
+ /*print_renderer=*/mojo::NullAssociatedRemote(),
#endif
- /*print_preview_disabled=*/false, params.print_only_selection);
+ /*print_preview_disabled=*/false, params.print_only_selection);
+ break;
+ case InvokePrintMethod::kWindowDotPrint:
+ content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(),
+ "window.print();");
+ break;
+ }
- print_preview_observer.WaitUntilPreviewIsReady();
+ content::WebContents* preview_dialog =
+ print_preview_observer.WaitUntilPreviewIsReadyAndReturnPreviewDialog();
set_rendered_page_count(print_preview_observer.rendered_page_count());
+
+ return preview_dialog;
}
- // The following are helper functions for having a wait loop in the test and
- // exit when all expected messages are received.
+// The following are helper functions for having a wait loop in the test and
+// exit when all expected messages are received.
void PrintBrowserTest::SetNumExpectedMessages(unsigned int num) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
num_expected_messages_ = num;
}
void PrintBrowserTest::ResetNumReceivedMessages() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
num_received_messages_ = 0;
}
void PrintBrowserTest::WaitUntilCallbackReceived() {
- base::RunLoop run_loop;
- quit_callback_ = run_loop.QuitClosure();
- run_loop.Run();
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ ASSERT_TRUE(run_loop_);
+ run_loop_->Run();
}
void PrintBrowserTest::CheckForQuit() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (++num_received_messages_ != num_expected_messages_) {
+ // Beware of tests which have more events checking than expected!
+ // Such tests might be exiting too early, and thus be flaky.
+ ASSERT_LT(num_received_messages_, num_expected_messages_);
return;
}
if (quit_callback_) {
@@ -547,11 +626,12 @@ void PrintBrowserTest::CheckForQuit() {
void PrintBrowserTest::CreateTestPrintRenderFrame(
content::RenderFrameHost* frame_host,
content::WebContents* web_contents) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
frame_content_.emplace(
frame_host, std::make_unique<TestPrintRenderFrame>(
frame_host, web_contents, kDefaultDocumentCookie,
base::BindRepeating(&PrintBrowserTest::CheckForQuit,
- base::Unretained(this))));
+ weak_factory_.GetWeakPtr())));
OverrideBinderForTesting(frame_host);
}
@@ -564,6 +644,7 @@ PrintBrowserTest::GetDefaultPrintFrameParams() {
const mojo::AssociatedRemote<mojom::PrintRenderFrame>&
PrintBrowserTest::GetPrintRenderFrame(content::RenderFrameHost* rfh) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!remote_) {
rfh->GetRemoteAssociatedInterfaces()->GetInterface(&remote_);
}
@@ -572,12 +653,14 @@ PrintBrowserTest::GetPrintRenderFrame(content::RenderFrameHost* rfh) {
TestPrintRenderFrame* PrintBrowserTest::GetFrameContent(
content::RenderFrameHost* host) const {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto iter = frame_content_.find(host);
return iter != frame_content_.end() ? iter->second.get() : nullptr;
}
void PrintBrowserTest::OverrideBinderForTesting(
content::RenderFrameHost* render_frame_host) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
render_frame_host->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting(
mojom::PrintRenderFrame::Name_,
base::BindRepeating(
@@ -585,7 +668,23 @@ void PrintBrowserTest::OverrideBinderForTesting(
base::Unretained(GetFrameContent(render_frame_host))));
}
+void PrintBrowserTest::OnNewDocument(
+#if BUILDFLAG(IS_MAC)
+ bool destination_is_preview,
+#endif
+ const PrintSettings& settings) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ DVLOG(1) << " Observed: new document";
+ new_document_called_count_++;
+ document_print_settings_ = settings;
+#if BUILDFLAG(IS_MAC)
+ destination_is_preview_ = destination_is_preview;
+#endif
+}
+
void PrintBrowserTest::ShowPrintErrorDialog() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
++error_dialog_shown_count_;
CheckForQuit();
}
@@ -1142,7 +1241,7 @@ IN_PROC_BROWSER_TEST_F(PrintBrowserTest,
client->DoCompositeDocumentToPdf(
kDefaultDocumentCookie, main_frame,
*TestPrintRenderFrame::GetDefaultDidPrintContentParams(),
- base::DoNothing());
+ ui::AXTreeUpdate(), base::DoNothing());
ASSERT_TRUE(client->GetCompositeRequest(kDefaultDocumentCookie));
// `requested_subframes_` should be empty.
ASSERT_TRUE(client->requested_subframes_.empty());
@@ -1453,13 +1552,13 @@ IN_PROC_BROWSER_TEST_F(PrintExtensionBrowserTest,
// Size and printable area are in device units, which is different for macOS.
// See PrintSettings::device_units_per_inch().
#if BUILDFLAG(IS_MAC)
- static constexpr gfx::Size kLetterPdfPhysicalSize{612, 792};
- static constexpr gfx::Rect kExpectedPrintableArea{72, 72, 432, 684};
- static constexpr gfx::Size kExpectedContentSize{432, 656};
+ static constexpr gfx::SizeF kLetterPdfPhysicalSize{612, 792};
+ static constexpr gfx::RectF kExpectedPrintableArea{72, 72, 432, 684};
+ static constexpr gfx::SizeF kExpectedContentSize{432, 656};
#else
- static constexpr gfx::Size kLetterPdfPhysicalSize{2550, 3300};
- static constexpr gfx::Rect kExpectedPrintableArea{300, 300, 1800, 2850};
- static constexpr gfx::Size kExpectedContentSize{1800, 2732};
+ static constexpr gfx::SizeF kLetterPdfPhysicalSize{2550, 3300};
+ static constexpr gfx::RectF kExpectedPrintableArea{300, 300, 1800, 2850};
+ static constexpr gfx::SizeF kExpectedContentSize{1800, 2732};
#endif
LoadExtensionAndNavigateToOptionPage();
@@ -1503,9 +1602,9 @@ IN_PROC_BROWSER_TEST_F(
// Size is in device units, which is different for macOS. See
// PrintSettings::device_units_per_inch().
#if BUILDFLAG(IS_MAC)
- static constexpr gfx::Size kIsoA4PdfPhysicalSize{595, 841};
+ static constexpr gfx::SizeF kIsoA4PdfPhysicalSize{595, 841};
#else
- static constexpr gfx::Size kIsoA4PdfPhysicalSize{2480, 3507};
+ static constexpr gfx::SizeF kIsoA4PdfPhysicalSize{2480, 3507};
#endif
LoadExtensionAndNavigateToOptionPage();
@@ -1728,6 +1827,102 @@ IN_PROC_BROWSER_TEST_F(PrintBrowserTest,
EXPECT_EQ(true, content::EvalJs(rfh, "firedAfterPrint"));
}
+IN_PROC_BROWSER_TEST_F(PrintBrowserTest,
+ WindowDotPrintWhilePrintPreviewIsInProgress) {
+ const char kHtmlData[] = "data:text/html,hello";
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kHtmlData)));
+
+ PrintAndWaitUntilPreviewIsReady();
+
+ // This should not crash the renderer. In older builds, this can trigger 2
+ // beforeprint events in a row without an afterprint event in between.
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(content::ExecJs(web_contents, "window.print()"));
+}
+
+IN_PROC_BROWSER_TEST_F(PrintBrowserTest,
+ WindowDotPrintWhilePrintPreviewForNodeIsInProgress) {
+ // This test is a bit quirky and brittle:
+ // - `ContextMenuWaiter` does not seem to work in general, but does work for
+ // the data scheme.
+ // - Normally only PDF nodes can be printed, but loading PDFs in an iframe
+ // reliably is very hard, so use an image instead.
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ const char kHtmlData[] =
+ "data:text/html,"
+ "<iframe src='data:image/png;base64,%s' name='imageframe'>";
+ base::FilePath image_path =
+ base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
+ .AppendASCII("printing")
+ .AppendASCII("test1.png");
+ absl::optional<std::vector<uint8_t>> image_data =
+ base::ReadFileToBytes(image_path);
+ ASSERT_TRUE(image_data.has_value());
+ GURL data_url(base::StringPrintf(
+ kHtmlData, base::Base64Encode(image_data.value()).c_str()));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), data_url));
+ }
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ content::RenderFrameHost* main_rfh = web_contents->GetPrimaryMainFrame();
+ ASSERT_TRUE(main_rfh);
+
+ content::RenderFrameHost* iframe_rfh = nullptr;
+ main_rfh->ForEachRenderFrameHost(
+ [&iframe_rfh](content::RenderFrameHost* rfh) {
+ if (rfh->GetFrameName() != "imageframe") {
+ return;
+ }
+
+ DCHECK(!iframe_rfh); // There can be only 1.
+ iframe_rfh = rfh;
+ });
+ ASSERT_TRUE(iframe_rfh);
+ EXPECT_NE(main_rfh, iframe_rfh);
+
+ {
+ // `ContextMenuWaiter` can issue `IDC_PRINT` even though it is not in the
+ // menu.
+ ContextMenuWaiter menu_observer(IDC_PRINT);
+
+ // Send mouse right-click to activate context menu. Otherwise the renderer
+ // does not have a WebNode to print.
+ content::SimulateMouseClickAt(web_contents, /*modifiers=*/0,
+ blink::WebMouseEvent::Button::kRight,
+ gfx::Point(100, 100));
+ menu_observer.WaitForMenuOpenAndClose();
+ EXPECT_EQ(menu_observer.params().media_type,
+ blink::mojom::ContextMenuDataMediaType::kImage);
+ }
+
+ // This should not crash the renderer. In older builds, this can trigger 2
+ // beforeprint events in a row without an afterprint event in between.
+ ASSERT_TRUE(content::ExecJs(iframe_rfh, "window.print()"));
+}
+
+IN_PROC_BROWSER_TEST_F(PrintBrowserTest, NoResizeEvent) {
+ const GURL kUrl(
+ embedded_test_server()->GetURL("/printing/resize_event_counter.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kUrl));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ content::RenderFrameHost* rfh = web_contents->GetPrimaryMainFrame();
+
+ // In case there's some resizing taking place before printing, keep track of
+ // it.
+ int before = content::EvalJs(rfh, "resizeCount").ExtractInt();
+
+ // Printing itself should not trigger window resize events.
+ PrintAndWaitUntilPreviewIsReadyAndLoaded();
+
+ int after = content::EvalJs(rfh, "resizeCount").ExtractInt();
+ EXPECT_EQ(before, after);
+}
+
class PrintPrerenderBrowserTest : public PrintBrowserTest {
public:
PrintPrerenderBrowserTest()
@@ -1741,7 +1936,7 @@ class PrintPrerenderBrowserTest : public PrintBrowserTest {
}
void SetUp() override {
- prerender_helper_.SetUp(embedded_test_server());
+ prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
PrintBrowserTest::SetUp();
}
diff --git a/chromium/chrome/browser/printing/print_browsertest.h b/chromium/chrome/browser/printing/print_browsertest.h
index 657d50ff663..3ec2e93c91c 100644
--- a/chromium/chrome/browser/printing/print_browsertest.h
+++ b/chromium/chrome/browser/printing/print_browsertest.h
@@ -5,12 +5,16 @@
#ifndef CHROME_BROWSER_PRINTING_PRINT_BROWSERTEST_H_
#define CHROME_BROWSER_PRINTING_PRINT_BROWSERTEST_H_
-#include "chrome/test/base/in_process_browser_test.h"
-
+#include <map>
+#include <memory>
#include <string>
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
#include "build/build_config.h"
#include "chrome/browser/printing/browser_printing_context_factory_for_test.h"
+#include "chrome/test/base/in_process_browser_test.h"
#include "components/printing/common/print.mojom.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
@@ -25,9 +29,17 @@ class TestPrintRenderFrame;
class PrintBrowserTest : public InProcessBrowserTest {
public:
+ enum class InvokePrintMethod {
+ // Browser starts print (e.g., like CTRL+P keyboard shortcut).
+ kStartPrint,
+ // Initiate printing for from JavaScript.
+ kWindowDotPrint,
+ };
+
struct PrintParams {
bool print_only_selection = false;
int pages_per_sheet = 1;
+ InvokePrintMethod invoke_method = InvokePrintMethod::kStartPrint;
};
PrintBrowserTest();
@@ -41,10 +53,13 @@ class PrintBrowserTest : public InProcessBrowserTest {
void AddPrinter(const std::string& printer_name);
void SetPrinterNameForSubsequentContexts(const std::string& printer_name);
+
void PrintAndWaitUntilPreviewIsReady();
void PrintAndWaitUntilPreviewIsReady(const PrintParams& params);
- void PrintAndWaitUntilPreviewIsReadyAndLoaded();
- void PrintAndWaitUntilPreviewIsReadyAndLoaded(const PrintParams& params);
+ // Returns the Print Preview dialog.
+ content::WebContents* PrintAndWaitUntilPreviewIsReadyAndLoaded();
+ content::WebContents* PrintAndWaitUntilPreviewIsReadyAndLoaded(
+ const PrintParams& params);
void SetNumExpectedMessages(unsigned int num);
void ResetNumReceivedMessages();
@@ -66,6 +81,12 @@ class PrintBrowserTest : public InProcessBrowserTest {
}
protected:
+ void OnNewDocument(
+#if BUILDFLAG(IS_MAC)
+ bool destination_is_preview,
+#endif
+ const PrintSettings& settings);
+
TestPrintBackend* test_print_backend() { return test_print_backend_.get(); }
BrowserPrintingContextFactoryForTest* test_printing_context_factory() {
@@ -76,25 +97,62 @@ class PrintBrowserTest : public InProcessBrowserTest {
rendered_page_count_ = page_count;
}
+ int new_document_called_count() const { return new_document_called_count_; }
+
const absl::optional<PrintSettings>& document_print_settings() const {
- return test_printing_context_factory_.document_print_settings();
+ return document_print_settings_;
}
+#if BUILDFLAG(IS_MAC)
+ bool destination_is_preview() const { return destination_is_preview_; }
+#endif
+
private:
+ // Helper to bounce worker thread callbacks onto PrintBrowserTest's callback
+ // equivalent on the UI thread.
+ class WorkerHelper : public base::RefCountedThreadSafe<WorkerHelper> {
+ public:
+ explicit WorkerHelper(base::WeakPtr<PrintBrowserTest> owner);
+
+ void OnNewDocument(
+#if BUILDFLAG(IS_MAC)
+ bool destination_is_preview,
+#endif
+ const PrintSettings& settings);
+
+ private:
+ friend class base::RefCountedThreadSafe<WorkerHelper>;
+ ~WorkerHelper();
+
+ // Only accessed on the UI thread.
+ const base::WeakPtr<PrintBrowserTest> owner_;
+ };
+
+ content::WebContents* PrintAndWaitUntilPreviewIsReadyAndMaybeLoaded(
+ const PrintParams& params,
+ bool wait_for_loaded);
TestPrintRenderFrame* GetFrameContent(content::RenderFrameHost* host) const;
void OverrideBinderForTesting(content::RenderFrameHost* render_frame_host);
void ShowPrintErrorDialog();
+ int new_document_called_count_ = 0;
+ absl::optional<PrintSettings> document_print_settings_;
+#if BUILDFLAG(IS_MAC)
+ bool destination_is_preview_ = false;
+#endif
uint32_t error_dialog_shown_count_ = 0;
uint32_t rendered_page_count_ = 0;
unsigned int num_expected_messages_;
unsigned int num_received_messages_;
+ std::unique_ptr<base::RunLoop> run_loop_;
base::OnceClosure quit_callback_;
mojo::AssociatedRemote<mojom::PrintRenderFrame> remote_;
std::map<content::RenderFrameHost*, std::unique_ptr<TestPrintRenderFrame>>
frame_content_;
scoped_refptr<TestPrintBackend> test_print_backend_;
BrowserPrintingContextFactoryForTest test_printing_context_factory_;
+ scoped_refptr<WorkerHelper> worker_helper_;
+ base::WeakPtrFactory<PrintBrowserTest> weak_factory_{this};
};
} // namespace printing
diff --git a/chromium/chrome/browser/printing/print_job.cc b/chromium/chrome/browser/printing/print_job.cc
index 3a66a52b8d3..819ea8a1902 100644
--- a/chromium/chrome/browser/printing/print_job.cc
+++ b/chromium/chrome/browser/printing/print_job.cc
@@ -34,12 +34,12 @@
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
-#include "content/public/browser/web_contents.h"
#include "printing/page_number.h"
#include "printing/pdf_render_settings.h"
#include "printing/printed_page_win.h"
#include "printing/printing_features.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
#endif
@@ -165,10 +165,11 @@ std::vector<uint32_t> PrintJob::GetFullPageMapping(
const std::vector<uint32_t>& pages,
uint32_t total_page_count) {
std::vector<uint32_t> mapping(total_page_count, kInvalidPageIndex);
- for (uint32_t page_number : pages) {
+ for (uint32_t page_index : pages) {
// Make sure the page is in range.
- if (page_number < total_page_count)
- mapping[page_number] = page_number;
+ if (page_index < total_page_count) {
+ mapping[page_index] = page_index;
+ }
}
return mapping;
}
@@ -177,7 +178,8 @@ void PrintJob::StartConversionToNativeFormat(
scoped_refptr<base::RefCountedMemory> print_data,
const gfx::Size& page_size,
const gfx::Rect& content_area,
- const gfx::Point& physical_offsets) {
+ const gfx::Point& physical_offsets,
+ const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (PrintedDocument::HasDebugDumpPath())
@@ -185,13 +187,13 @@ void PrintJob::StartConversionToNativeFormat(
const PrintSettings& settings = document()->settings();
if (settings.printer_language_is_textonly()) {
- StartPdfToTextConversion(print_data, page_size);
+ StartPdfToTextConversion(print_data, page_size, url);
} else if (settings.printer_language_is_ps2() ||
settings.printer_language_is_ps3()) {
StartPdfToPostScriptConversion(print_data, content_area, physical_offsets,
- settings.printer_language_is_ps2());
+ settings.printer_language_is_ps2(), url);
} else {
- StartPdfToEmfConversion(print_data, page_size, content_area);
+ StartPdfToEmfConversion(print_data, page_size, content_area, url);
}
// Indicate that the PDF is fully rendered and we no longer need the renderer
@@ -271,6 +273,13 @@ void PrintJob::Cancel() {
is_canceling_ = false;
}
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+void PrintJob::CleanupAfterContentAnalysisDenial() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ worker_->CleanupAfterContentAnalysisDenial();
+}
+#endif
+
bool PrintJob::FlushJob(base::TimeDelta timeout) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -320,16 +329,18 @@ class PrintJob::PdfConversionState {
public:
PdfConversionState(const gfx::Size& page_size,
const gfx::Rect& content_area,
- const absl::optional<bool>& use_skia)
+ const absl::optional<bool>& use_skia,
+ const GURL& url)
: page_size_(page_size),
content_area_(content_area),
- use_skia_(use_skia) {}
+ use_skia_(use_skia),
+ url_(url) {}
void Start(scoped_refptr<base::RefCountedMemory> data,
const PdfRenderSettings& conversion_settings,
PdfConverter::StartCallback start_callback) {
converter_ = PdfConverter::StartPdfConverter(
- data, conversion_settings, use_skia_, std::move(start_callback));
+ data, conversion_settings, use_skia_, url_, std::move(start_callback));
}
void GetMorePages(PdfConverter::GetPageCallback get_page_callback) {
@@ -359,18 +370,20 @@ class PrintJob::PdfConversionState {
int pages_in_progress_ = 0;
const gfx::Size page_size_;
const gfx::Rect content_area_;
- absl::optional<bool> use_skia_;
+ const absl::optional<bool> use_skia_;
+ const GURL url_;
std::unique_ptr<PdfConverter> converter_;
};
void PrintJob::StartPdfToEmfConversion(
scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Size& page_size,
- const gfx::Rect& content_area) {
+ const gfx::Rect& content_area,
+ const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!pdf_conversion_state_);
- pdf_conversion_state_ =
- std::make_unique<PdfConversionState>(page_size, content_area, use_skia_);
+ pdf_conversion_state_ = std::make_unique<PdfConversionState>(
+ page_size, content_area, use_skia_, url);
const PrintSettings& settings = document()->settings();
@@ -405,13 +418,13 @@ void PrintJob::OnPdfConversionStarted(uint32_t page_count) {
base::BindRepeating(&PrintJob::OnPdfPageConverted, this));
}
-void PrintJob::OnPdfPageConverted(uint32_t page_number,
+void PrintJob::OnPdfPageConverted(uint32_t page_index,
float scale_factor,
std::unique_ptr<MetafilePlayer> metafile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(pdf_conversion_state_);
- if (!document_ || !metafile || page_number == kInvalidPageIndex ||
- page_number >= pdf_page_mapping_.size()) {
+ if (!document_ || !metafile || page_index == kInvalidPageIndex ||
+ page_index >= pdf_page_mapping_.size()) {
// Be sure to live long enough.
scoped_refptr<PrintJob> handle(this);
pdf_conversion_state_.reset();
@@ -421,9 +434,9 @@ void PrintJob::OnPdfPageConverted(uint32_t page_number,
// Add the page to the document if it is one of the pages requested by the
// user. If it is not, ignore it.
- if (pdf_page_mapping_[page_number] != kInvalidPageIndex) {
+ if (pdf_page_mapping_[page_index] != kInvalidPageIndex) {
// Update the rendered document. It will send notifications to the listener.
- document_->SetPage(pdf_page_mapping_[page_number], std::move(metafile),
+ document_->SetPage(pdf_page_mapping_[page_index], std::move(metafile),
scale_factor, pdf_conversion_state_->page_size(),
pdf_conversion_state_->content_area());
}
@@ -434,11 +447,12 @@ void PrintJob::OnPdfPageConverted(uint32_t page_number,
void PrintJob::StartPdfToTextConversion(
scoped_refptr<base::RefCountedMemory> bytes,
- const gfx::Size& page_size) {
+ const gfx::Size& page_size,
+ const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!pdf_conversion_state_);
- pdf_conversion_state_ =
- std::make_unique<PdfConversionState>(gfx::Size(), gfx::Rect(), use_skia_);
+ pdf_conversion_state_ = std::make_unique<PdfConversionState>(
+ gfx::Size(), gfx::Rect(), use_skia_, url);
gfx::Rect page_area = gfx::Rect(0, 0, page_size.width(), page_size.height());
const PrintSettings& settings = document()->settings();
PdfRenderSettings render_settings(
@@ -454,11 +468,12 @@ void PrintJob::StartPdfToPostScriptConversion(
scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Rect& content_area,
const gfx::Point& physical_offsets,
- bool ps_level2) {
+ bool ps_level2,
+ const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!pdf_conversion_state_);
- pdf_conversion_state_ =
- std::make_unique<PdfConversionState>(gfx::Size(), gfx::Rect(), use_skia_);
+ pdf_conversion_state_ = std::make_unique<PdfConversionState>(
+ gfx::Size(), gfx::Rect(), use_skia_, url);
const PrintSettings& settings = document()->settings();
PdfRenderSettings::Mode mode;
@@ -616,7 +631,7 @@ bool PrintJob::PostTask(const base::Location& from_here,
void PrintJob::HoldUntilStopIsCalled() {
}
-void PrintJob::set_job_pending(bool pending) {
+void PrintJob::set_job_pending_for_testing(bool pending) {
is_job_pending_ = pending;
}
diff --git a/chromium/chrome/browser/printing/print_job.h b/chromium/chrome/browser/printing/print_job.h
index 85fa9f2efc5..83caebfe59d 100644
--- a/chromium/chrome/browser/printing/print_job.h
+++ b/chromium/chrome/browser/printing/print_job.h
@@ -19,6 +19,8 @@
#include "printing/print_settings.h"
#if BUILDFLAG(IS_CHROMEOS)
+#include <string>
+
#include "chromeos/crosapi/mojom/local_printer.mojom.h"
#endif
@@ -26,6 +28,10 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
#endif
+#if BUILDFLAG(IS_WIN)
+class GURL;
+#endif
+
namespace base {
class Location;
class RefCountedMemory;
@@ -92,7 +98,8 @@ class PrintJob : public base::RefCountedThreadSafe<PrintJob> {
scoped_refptr<base::RefCountedMemory> print_data,
const gfx::Size& page_size,
const gfx::Rect& content_area,
- const gfx::Point& physical_offsets);
+ const gfx::Point& physical_offsets,
+ const GURL& url);
// Overwrites the PDF page mapping to fill in values of -1 for all indices
// that are not selected. This is needed when the user opens the system
@@ -127,6 +134,13 @@ class PrintJob : public base::RefCountedThreadSafe<PrintJob> {
// since Cancel() calls Stop(). See WARNING above for Stop().
virtual void Cancel();
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ // Cleanup a printing job after content analysis denies printing. Performs
+ // any extra cleanup for this particular case that can't be safely done from
+ // within Cancel().
+ void CleanupAfterContentAnalysisDenial();
+#endif
+
// Synchronously wait for the job to finish. It is mainly useful when the
// process is about to be shut down and we're waiting for the spooler to eat
// our data.
@@ -151,14 +165,14 @@ class PrintJob : public base::RefCountedThreadSafe<PrintJob> {
// Returns the ID of the source.
const std::string& source_id() const;
-#endif // BUILDFLAG(IS_CHROMEOS)
-
- // Posts the given task to be run.
- bool PostTask(const base::Location& from_here, base::OnceClosure task);
const base::ObserverList<Observer>& GetObserversForTesting() {
return observers_;
}
+#endif // BUILDFLAG(IS_CHROMEOS)
+
+ // Posts the given task to be run.
+ bool PostTask(const base::Location& from_here, base::OnceClosure task);
// Adds and removes observers for `PrintJob` events. The order in
// which notifications are sent to observers is undefined. Observers must be
@@ -176,13 +190,19 @@ class PrintJob : public base::RefCountedThreadSafe<PrintJob> {
// testing contexts.
PrintJob();
- // The functions below are used for tests only.
- void set_job_pending(bool pending);
+ void set_job_pending_for_testing(bool pending);
// Updates `document_` to a new instance. Protected so that tests can access
// it.
void UpdatePrintedDocument(scoped_refptr<PrintedDocument> new_document);
+#if BUILDFLAG(IS_WIN)
+ // Virtual to support testing.
+ virtual void OnPdfPageConverted(uint32_t page_index,
+ float scale_factor,
+ std::unique_ptr<MetafilePlayer> metafile);
+#endif
+
private:
#if BUILDFLAG(IS_WIN)
FRIEND_TEST_ALL_PREFIXES(PrintJobTest, PageRangeMapping);
@@ -209,22 +229,22 @@ class PrintJob : public base::RefCountedThreadSafe<PrintJob> {
virtual void StartPdfToEmfConversion(
scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Size& page_size,
- const gfx::Rect& content_area);
+ const gfx::Rect& content_area,
+ const GURL& url);
virtual void StartPdfToPostScriptConversion(
scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Rect& content_area,
const gfx::Point& physical_offsets,
- bool ps_level2);
+ bool ps_level2,
+ const GURL& url);
virtual void StartPdfToTextConversion(
scoped_refptr<base::RefCountedMemory> bytes,
- const gfx::Size& page_size);
+ const gfx::Size& page_size,
+ const GURL& url);
void OnPdfConversionStarted(uint32_t page_count);
- void OnPdfPageConverted(uint32_t page_number,
- float scale_factor,
- std::unique_ptr<MetafilePlayer> metafile);
// Helper method to do the work for ResetPageMapping(). Split for unit tests.
static std::vector<uint32_t> GetFullPageMapping(
diff --git a/chromium/chrome/browser/printing/print_job_utils_lacros.cc b/chromium/chrome/browser/printing/print_job_utils_lacros.cc
index fbcbff64831..b455c54fbd0 100644
--- a/chromium/chrome/browser/printing/print_job_utils_lacros.cc
+++ b/chromium/chrome/browser/printing/print_job_utils_lacros.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/printing/print_job_utils_lacros.h"
+#include "base/check_op.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/printing/print_job.h"
@@ -30,8 +31,10 @@ crosapi::mojom::PrintJobPtr PrintJobToMojom(int job_id,
}
const PrintSettings& settings = document.settings();
int duplex = static_cast<int>(settings.duplex_mode());
- DCHECK(duplex >= 0);
- DCHECK(duplex < 3);
+ CHECK_GE(duplex, 0);
+ CHECK_LT(duplex, 3);
+
+ CHECK_NE(settings.color(), mojom::ColorModel::kUnknownColorModel);
return crosapi::mojom::PrintJob::New(
base::UTF16ToUTF8(settings.device_name()), base::UTF16ToUTF8(title),
job_id, document.page_count(), source, source_id, settings.color(),
diff --git a/chromium/chrome/browser/printing/print_job_worker.cc b/chromium/chrome/browser/printing/print_job_worker.cc
index ef8aad14b01..56dd4b09025 100644
--- a/chromium/chrome/browser/printing/print_job_worker.cc
+++ b/chromium/chrome/browser/printing/print_job_worker.cc
@@ -228,6 +228,13 @@ void PrintJobWorker::Cancel() {
// context we run.
}
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+void PrintJobWorker::CleanupAfterContentAnalysisDenial() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DVLOG(1) << "Canceling job due to content analysis";
+}
+#endif
+
bool PrintJobWorker::IsRunning() const {
return thread_.IsRunning();
}
@@ -271,6 +278,7 @@ void PrintJobWorker::OnDocumentDone() {
}
void PrintJobWorker::FinishDocumentDone(int job_id) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
DCHECK(document_);
print_job_->PostTask(
FROM_HERE, base::BindOnce(&DocDoneNotificationCallback,
diff --git a/chromium/chrome/browser/printing/print_job_worker.h b/chromium/chrome/browser/printing/print_job_worker.h
index 4f4ab568ec9..f58cbeeea3f 100644
--- a/chromium/chrome/browser/printing/print_job_worker.h
+++ b/chromium/chrome/browser/printing/print_job_worker.h
@@ -53,7 +53,15 @@ class PrintJobWorker {
void OnNewPage();
// Cancels the job.
- void Cancel();
+ virtual void Cancel();
+
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ // The job is canceled due to content analysis denying printing. Called
+ // only from UI thread, before any platform calls are made for the job.
+ // Performs any extra cleanup for this particular case that can't be safely
+ // safely done from within Cancel().
+ virtual void CleanupAfterContentAnalysisDenial();
+#endif
// Returns true if the thread has been started, and not yet stopped.
bool IsRunning() const;
@@ -80,6 +88,12 @@ class PrintJobWorker {
// Setup the document in preparation for printing.
bool SetupDocument(const std::u16string& document_name);
+ // Get the document. Only to be called from the worker thread.
+ PrintedDocument* document() {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ return document_.get();
+ }
+
#if BUILDFLAG(IS_WIN)
// Renders a page in the printer. Returns false if any errors occur.
// This is applicable when using the Windows GDI print API.
@@ -128,6 +142,7 @@ class PrintJobWorker {
const std::unique_ptr<PrintingContext> printing_context_;
// The printed document. Only has read-only access.
+ // Only accessed from worker thread.
scoped_refptr<PrintedDocument> document_;
// The print job owning this worker thread. It is guaranteed to outlive this
diff --git a/chromium/chrome/browser/printing/print_job_worker_oop.cc b/chromium/chrome/browser/printing/print_job_worker_oop.cc
index 43ff03c528e..1b2ae6156ee 100644
--- a/chromium/chrome/browser/printing/print_job_worker_oop.cc
+++ b/chromium/chrome/browser/printing/print_job_worker_oop.cc
@@ -111,6 +111,18 @@ void PrintJobWorkerOop::StartPrinting(PrintedDocument* new_document) {
document_name));
}
+void PrintJobWorkerOop::Cancel() {
+ PrintJobWorker::Cancel();
+ PrintJobWorkerOop::OnCancel();
+}
+
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+void PrintJobWorkerOop::CleanupAfterContentAnalysisDenial() {
+ PrintJobWorker::CleanupAfterContentAnalysisDenial();
+ UnregisterServiceManagerClient();
+}
+#endif
+
void PrintJobWorkerOop::OnDidStartPrinting(mojom::ResultCode result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (result != mojom::ResultCode::kSuccess) {
@@ -196,7 +208,9 @@ void PrintJobWorkerOop::OnDidDocumentDone(int job_id,
UnregisterServiceManagerClient();
base::UmaHistogramEnumeration(kPrintOopPrintResultHistogramName,
PrintOopResult::kSuccessful);
- FinishDocumentDone(job_id);
+ task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&PrintJobWorkerOop::FinishDocumentDone,
+ worker_weak_factory_.GetWeakPtr(), job_id));
// Also done with private document reference.
document_oop_ = nullptr;
@@ -232,7 +246,7 @@ bool PrintJobWorkerOop::SpoolPage(PrintedPage* page) {
DCHECK_NE(page_number(), PageNumber::npos());
#if !defined(NDEBUG)
- DCHECK(document_oop_->IsPageInList(*page));
+ DCHECK(document()->IsPageInList(*page));
#endif
const MetafilePlayer* metafile = page->metafile();
@@ -263,7 +277,7 @@ bool PrintJobWorkerOop::SpoolPage(PrintedPage* page) {
bool PrintJobWorkerOop::SpoolDocument() {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
- const MetafilePlayer* metafile = document_oop_->GetMetafile();
+ const MetafilePlayer* metafile = document()->GetMetafile();
DCHECK(metafile);
base::MappedReadOnlyRegion region_mapping =
metafile->GetDataAsSharedMemoryRegion();
@@ -384,12 +398,21 @@ void PrintJobWorkerOop::NotifyFailure(mojom::ResultCode result) {
}
base::UmaHistogramEnumeration(kPrintOopPrintResultHistogramName, uma_result);
+ if (!document_oop_) {
+ // If no document has been started for printing then don't send cancel.
+ return;
+ }
+
// Initiate rest of regular failure handling.
SendCancel(base::BindOnce(&PrintJobWorkerOop::OnDidCancel,
ui_weak_factory_.GetWeakPtr(),
base::WrapRefCounted(print_job()), result));
}
+void PrintJobWorkerOop::FinishDocumentDone(int job_id) {
+ PrintJobWorker::FinishDocumentDone(job_id);
+}
+
void PrintJobWorkerOop::SendEstablishPrintingContext() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(features::kEnableOopPrintDriversJobPrint.Get());
@@ -464,6 +487,13 @@ void PrintJobWorkerOop::SendRenderPrintedPage(
// Page numbers are 0-based for the printing context.
const uint32_t page_index = page->page_number() - 1;
const int32_t document_cookie = document_oop_->cookie();
+ if (print_cancel_requested_) {
+ VLOG(1) << "Dropping page " << page_index << " of document "
+ << document_cookie << " to `" << device_name_
+ << "` because job was canceled";
+ return;
+ }
+
VLOG(1) << "Sending page " << page_index << " of document " << document_cookie
<< " to `" << device_name_ << "` for printing";
PrintBackendServiceManager& service_mgr =
diff --git a/chromium/chrome/browser/printing/print_job_worker_oop.h b/chromium/chrome/browser/printing/print_job_worker_oop.h
index 95038015f38..ee148ae86a1 100644
--- a/chromium/chrome/browser/printing/print_job_worker_oop.h
+++ b/chromium/chrome/browser/printing/print_job_worker_oop.h
@@ -50,6 +50,10 @@ class PrintJobWorkerOop : public PrintJobWorker {
// `PrintJobWorker` overrides.
void StartPrinting(PrintedDocument* new_document) override;
+ void Cancel() override;
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ void CleanupAfterContentAnalysisDenial() override;
+#endif
protected:
// For testing.
@@ -94,6 +98,9 @@ class PrintJobWorkerOop : public PrintJobWorker {
// Initiate failure handling, including notification to the user.
void NotifyFailure(mojom::ResultCode result);
+ // Helper function for document done processing, to get onto worker thread.
+ void FinishDocumentDone(int job_id);
+
// Mojo support to send messages from UI thread.
void SendEstablishPrintingContext();
void SendStartPrinting(const std::string& device_name,
@@ -136,6 +143,11 @@ class PrintJobWorkerOop : public PrintJobWorker {
// the `PrintJob` should drop its reference as part of failure/cancel
// processing. Named differently than base (even though both are private)
// to avoid any potential confusion between them.
+ // Once set at the start of printing on the worker thread, it is only
+ // referenced thereafter from the UI thread. UI thread accesses only occur
+ // once the interactions with the Print Backend service occur as a result of
+ // starting to print the job. Any document access from worker thread happens
+ // by methods in base class, which use the base `document_` field.
scoped_refptr<PrintedDocument> document_oop_;
// Indicates if the print job was initiated from the print system dialog.
diff --git a/chromium/chrome/browser/printing/print_preview_context_menu_observer.h b/chromium/chrome/browser/printing/print_preview_context_menu_observer.h
index 3530bb3e389..4d39bf9e235 100644
--- a/chromium/chrome/browser/printing/print_preview_context_menu_observer.h
+++ b/chromium/chrome/browser/printing/print_preview_context_menu_observer.h
@@ -30,7 +30,7 @@ class PrintPreviewContextMenuObserver : public RenderViewContextMenuObserver {
private:
bool IsPrintPreviewDialog();
- raw_ptr<content::WebContents, DanglingUntriaged> contents_;
+ raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> contents_;
};
#endif // CHROME_BROWSER_PRINTING_PRINT_PREVIEW_CONTEXT_MENU_OBSERVER_H_
diff --git a/chromium/chrome/browser/printing/print_preview_dialog_controller.cc b/chromium/chrome/browser/printing/print_preview_dialog_controller.cc
index a2f8a47b29a..1a6740b3634 100644
--- a/chromium/chrome/browser/printing/print_preview_dialog_controller.cc
+++ b/chromium/chrome/browser/printing/print_preview_dialog_controller.cc
@@ -386,14 +386,7 @@ void PrintPreviewDialogController::OnPreviewDialogNavigated(
ui::PageTransitionCoreTypeIs(type, ui::PAGE_TRANSITION_AUTO_TOPLEVEL) &&
!navigation_handle->IsSameDocument()) {
SaveInitiatorTitle(preview_dialog);
- return;
}
-
- // Cloud print sign-in causes a reload, but other cases should not be reached
- // here.
- DCHECK(ui::PageTransitionCoreTypeIs(type, ui::PAGE_TRANSITION_RELOAD));
- DCHECK(
- IsPrintPreviewURL(navigation_handle->GetPreviousPrimaryMainFrameURL()));
}
WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
diff --git a/chromium/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc b/chromium/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
index 51808f0fa80..105da69afb3 100644
--- a/chromium/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
+++ b/chromium/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
@@ -138,9 +138,10 @@ class PrintPreviewDialogControllerBrowserTest : public InProcessBrowserTest {
std::unique_ptr<printing::TestPrintPreviewDialogClonedObserver>
cloned_tab_observer_;
- raw_ptr<printing::TestPrintViewManagerForRequestPreview, DanglingUntriaged>
+ raw_ptr<printing::TestPrintViewManagerForRequestPreview,
+ AcrossTasksDanglingUntriaged>
test_print_view_manager_;
- raw_ptr<WebContents, DanglingUntriaged> initiator_ = nullptr;
+ raw_ptr<WebContents, AcrossTasksDanglingUntriaged> initiator_ = nullptr;
};
// Test to verify that when a initiator navigates, we can create a new preview
diff --git a/chromium/chrome/browser/printing/print_test_utils.cc b/chromium/chrome/browser/printing/print_test_utils.cc
index bbe91a31d50..8db8368df42 100644
--- a/chromium/chrome/browser/printing/print_test_utils.cc
+++ b/chromium/chrome/browser/printing/print_test_utils.cc
@@ -12,9 +12,14 @@
#include "base/values.h"
#include "chrome/browser/printing/print_view_manager_common.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
+#include "printing/buildflags/buildflags.h"
#include "printing/mojom/print.mojom.h"
#include "printing/print_job_constants.h"
+#if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
+#include "printing/printing_features.h"
+#endif
+
namespace printing::test {
const char kPrinterName[] = "DefaultPrinter";
@@ -110,6 +115,32 @@ std::unique_ptr<PrintSettings> MakeUserModifiedPrintSettings(
std::unique_ptr<PrintSettings> settings =
MakeDefaultPrintSettings(printer_name);
settings->set_copies(kPrintSettingsCopies + 1);
+#if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
+ if (features::kEnableOopPrintDriversJobPrint.Get()) {
+ // Supply fake data to mimic what might be collected from the system print
+ // dialog. Platform-specific since the fake data still has to be able to
+ // pass mojom data validation.
+ base::Value::Dict data;
+
+#if BUILDFLAG(IS_MAC)
+ data.Set(kMacSystemPrintDialogDataDestinationType, 2);
+ data.Set(kMacSystemPrintDialogDataPageFormat,
+ base::Value::BlobStorage({0xF1}));
+ data.Set(kMacSystemPrintDialogDataPrintSettings,
+ base::Value::BlobStorage({0xB2}));
+
+#elif BUILDFLAG(IS_LINUX)
+ data.Set(kLinuxSystemPrintDialogDataPrinter, printer_name);
+ data.Set(kLinuxSystemPrintDialogDataPrintSettings, "print-settings");
+ data.Set(kLinuxSystemPrintDialogDataPageSetup, "page-setup");
+
+#else
+#error "Missing fake system print dialog data for this platform."
+#endif
+
+ settings->set_system_print_dialog_data(std::move(data));
+ }
+#endif // BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
return settings;
}
diff --git a/chromium/chrome/browser/printing/print_view_manager.cc b/chromium/chrome/browser/printing/print_view_manager.cc
index 4ac9ea4a06b..40b0a71a265 100644
--- a/chromium/chrome/browser/printing/print_view_manager.cc
+++ b/chromium/chrome/browser/printing/print_view_manager.cc
@@ -181,10 +181,6 @@ void PrintViewManager::PrintPreviewForWebNode(content::RenderFrameHost* rfh) {
SetPrintPreviewRenderFrameHost(rfh);
print_preview_state_ = USER_INITIATED_PREVIEW;
-
- for (auto& observer : GetTestObservers()) {
- observer.OnPrintPreview(print_preview_rfh_);
- }
}
void PrintViewManager::PrintPreviewAlmostDone() {
@@ -240,6 +236,9 @@ void PrintViewManager::PrintPreviewDone() {
}
print_preview_state_ = NOT_PREVIEWING;
print_preview_rfh_ = nullptr;
+ for (auto& observer : GetTestObservers()) {
+ observer.OnPrintPreviewDone();
+ }
}
void PrintViewManager::RejectPrintPreviewRequestIfRestricted(
@@ -295,8 +294,9 @@ void PrintViewManager::RejectPrintPreviewRequestIfRestrictedByContentAnalysis(
content::GlobalRenderFrameHostId rfh_id,
base::OnceCallback<void(bool should_proceed)> callback) {
absl::optional<enterprise_connectors::ContentAnalysisDelegate::Data>
- scanning_data = enterprise_connectors::GetBeforePrintPreviewAnalysisData(
- web_contents());
+ scanning_data = enterprise_connectors::GetPrintAnalysisData(
+ web_contents(),
+ enterprise_connectors::PrintScanningContext::kBeforePreview);
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(rfh_id);
if (rfh && scanning_data) {
GetPrintRenderFrame(rfh)->SnapshotForContentAnalysis(base::BindOnce(
@@ -346,11 +346,6 @@ bool PrintViewManager::PrintPreview(
SetPrintPreviewRenderFrameHost(rfh);
print_preview_state_ = USER_INITIATED_PREVIEW;
-
- for (auto& observer : GetTestObservers()) {
- observer.OnPrintPreview(print_preview_rfh_);
- }
-
return true;
}
@@ -420,7 +415,9 @@ void PrintViewManager::ShowScriptedPrintPreview(bool source_is_modifiable) {
DCHECK(print_preview_rfh_);
if (GetCurrentTargetFrame() != print_preview_rfh_)
return;
-
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ set_analyzing_content(/*analyzing=*/true);
+#endif
RejectPrintPreviewRequestIfRestricted(
print_preview_rfh_->GetGlobalId(),
base::BindOnce(&PrintViewManager::OnScriptedPrintPreviewCallback,
@@ -432,6 +429,9 @@ void PrintViewManager::OnScriptedPrintPreviewCallback(
bool source_is_modifiable,
content::GlobalRenderFrameHostId rfh_id,
bool should_proceed) {
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ set_analyzing_content(/*analyzing=*/false);
+#endif
if (!should_proceed) {
OnPrintPreviewRequestRejected(rfh_id);
return;
diff --git a/chromium/chrome/browser/printing/print_view_manager_base.cc b/chromium/chrome/browser/printing/print_view_manager_base.cc
index c976fb2a814..666ee7afbd6 100644
--- a/chromium/chrome/browser/printing/print_view_manager_base.cc
+++ b/chromium/chrome/browser/printing/print_view_manager_base.cc
@@ -168,6 +168,21 @@ std::string PrintMsgPrintParamsErrorDetails(const mojom::PrintParams& params) {
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+bool ContentAnalysisAfterDialog(
+ const enterprise_connectors::ContentAnalysisDelegate::Data& scanning_data) {
+ bool cloud_analysis_after_dialog =
+ scanning_data.settings.cloud_or_local_settings.is_cloud_analysis() &&
+ base::FeatureList::IsEnabled(
+ printing::features::kEnableCloudScanAfterPreview);
+ bool local_analysis_after_dialog =
+ scanning_data.settings.cloud_or_local_settings.is_local_analysis() &&
+ base::FeatureList::IsEnabled(
+ printing::features::kEnableLocalScanAfterPreview);
+ return cloud_analysis_after_dialog || local_analysis_after_dialog;
+}
+#endif
+
} // namespace
PrintViewManagerBase::PrintViewManagerBase(content::WebContents* web_contents)
@@ -229,9 +244,12 @@ void PrintViewManagerBase::PrintForPrintPreview(
PrinterHandler::PrintCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ bool show_system_dialog =
+ job_settings.FindBool(kSettingShowSystemDialog).value_or(false);
+
#if BUILDFLAG(ENABLE_OOP_PRINTING)
if (printing::features::kEnableOopPrintDriversJobPrint.Get() &&
- job_settings.FindBool(kSettingShowSystemDialog).value_or(false)) {
+ show_system_dialog) {
if (!RegisterSystemPrintClient()) {
// Platform unable to support system print dialog at this time, treat
// this as a cancel.
@@ -256,6 +274,9 @@ void PrintViewManagerBase::PrintForPrintPreview(
std::move(job_settings),
base::BindOnce(&PrintViewManagerBase::OnPrintSettingsDone,
weak_ptr_factory_.GetWeakPtr(), print_data, page_count,
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ show_system_dialog,
+#endif
std::move(callback), std::move(printer_query)));
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
@@ -275,13 +296,22 @@ void PrintViewManagerBase::PrintDocument(
const gfx::Size& page_size,
const gfx::Rect& content_area,
const gfx::Point& offsets) {
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ if (content_analysis_before_printing_document_) {
+ std::move(content_analysis_before_printing_document_)
+ .Run(print_data, page_size, content_area, offsets);
+ return;
+ }
+#endif
+
#if BUILDFLAG(IS_WIN)
const bool source_is_pdf =
!print_job_->document()->settings().is_modifiable();
if (!printing::features::ShouldPrintUsingXps(source_is_pdf)) {
// Print using GDI, which first requires conversion to EMF.
- print_job_->StartConversionToNativeFormat(print_data, page_size,
- content_area, offsets);
+ print_job_->StartConversionToNativeFormat(
+ print_data, page_size, content_area, offsets,
+ web_contents()->GetLastCommittedURL());
return;
}
#endif
@@ -345,6 +375,9 @@ void PrintViewManagerBase::CompleteUpdatePrintSettings(
void PrintViewManagerBase::OnPrintSettingsDone(
scoped_refptr<base::RefCountedMemory> print_data,
uint32_t page_count,
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ bool show_system_dialog,
+#endif
PrinterHandler::PrintCallback callback,
std::unique_ptr<printing::PrinterQuery> printer_query) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -372,6 +405,7 @@ void PrintViewManagerBase::OnPrintSettingsDone(
}
if (!printer_query->cookie() || !printer_query->settings().dpi()) {
+ PRINTER_LOG(ERROR) << "Unable to update print settings";
std::move(callback).Run(base::Value("Update settings failed"));
return;
}
@@ -381,18 +415,46 @@ void PrintViewManagerBase::OnPrintSettingsDone(
int cookie = printer_query->cookie();
queue_->QueuePrinterQuery(std::move(printer_query));
content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE, base::BindOnce(&PrintViewManagerBase::StartLocalPrintJob,
- weak_ptr_factory_.GetWeakPtr(), print_data,
- page_count, cookie, std::move(callback)));
+ FROM_HERE,
+ base::BindOnce(&PrintViewManagerBase::StartLocalPrintJob,
+ weak_ptr_factory_.GetWeakPtr(), print_data, page_count,
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ show_system_dialog,
+#endif
+ cookie, std::move(callback)));
}
void PrintViewManagerBase::StartLocalPrintJob(
scoped_refptr<base::RefCountedMemory> print_data,
uint32_t page_count,
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ bool show_system_dialog,
+#endif
int cookie,
PrinterHandler::PrintCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ // Populating `content_analysis_before_printing_document_` if needed should be
+ // done first in this function's workflow, this way other code can check if
+ // content analysis is going to happen and delay starting `print_job_` to
+ // avoid needlessly prompting the user.
+ using enterprise_connectors::PrintScanningContext;
+ auto context = show_system_dialog
+ ? PrintScanningContext::kSystemPrintBeforePrintDocument
+ : PrintScanningContext::kNormalPrintBeforePrintDocument;
+
+ absl::optional<enterprise_connectors::ContentAnalysisDelegate::Data>
+ scanning_data =
+ enterprise_connectors::GetPrintAnalysisData(web_contents(), context);
+
+ if (scanning_data) {
+ content_analysis_before_printing_document_ = base::BindOnce(
+ &PrintViewManagerBase::ContentAnalysisBeforePrintingDocument,
+ weak_ptr_factory_.GetWeakPtr(), std::move(*scanning_data));
+ }
+#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+
set_cookie(cookie);
DidGetPrintedPagesCount(cookie, page_count);
@@ -433,7 +495,7 @@ void PrintViewManagerBase::GetDefaultPrintSettingsReply(
set_cookie(params->document_cookie);
std::move(callback).Run(std::move(params));
} else {
- set_cookie(0);
+ set_cookie(PrintSettings::NewInvalidCookie());
std::move(callback).Run(nullptr);
}
}
@@ -460,7 +522,7 @@ void PrintViewManagerBase::ScriptedPrintReply(
set_cookie(params->params->document_cookie);
std::move(callback).Run(std::move(params));
} else {
- set_cookie(0);
+ set_cookie(PrintSettings::NewInvalidCookie());
std::move(callback).Run(nullptr);
}
}
@@ -483,16 +545,6 @@ void PrintViewManagerBase::DidGetPrintedPagesCount(int32_t cookie,
OpportunisticallyCreatePrintJob(cookie);
}
-#if BUILDFLAG(ENABLE_TAGGED_PDF)
-void PrintViewManagerBase::SetAccessibilityTree(
- int32_t cookie,
- const ui::AXTreeUpdate& accessibility_tree) {
- auto* client = PrintCompositeClient::FromWebContents(web_contents());
- if (client)
- client->SetAccessibilityTree(cookie, accessibility_tree);
-}
-#endif
-
bool PrintViewManagerBase::PrintJobHasDocument(int cookie) {
if (!OpportunisticallyCreatePrintJob(cookie))
return false;
@@ -575,6 +627,7 @@ void PrintViewManagerBase::DidPrintDocument(
auto* client = PrintCompositeClient::FromWebContents(web_contents());
client->DoCompositeDocumentToPdf(
params->document_cookie, GetCurrentTargetFrame(), content,
+ ui::AXTreeUpdate(),
base::BindOnce(&PrintViewManagerBase::OnComposePdfDone,
weak_ptr_factory_.GetWeakPtr(), params->document_cookie,
params->page_size, params->content_area,
@@ -619,7 +672,8 @@ void PrintViewManagerBase::GetDefaultPrintSettings(
auto callback_wrapper =
base::BindOnce(&PrintViewManagerBase::GetDefaultPrintSettingsReply,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
- std::unique_ptr<PrinterQuery> printer_query = queue_->PopPrinterQuery(0);
+ std::unique_ptr<PrinterQuery> printer_query =
+ queue_->PopPrinterQuery(PrintSettings::NewInvalidCookie());
if (!printer_query) {
printer_query =
queue_->CreatePrinterQuery(render_frame_host->GetGlobalId());
@@ -729,6 +783,15 @@ void PrintViewManagerBase::UpdatePrintSettings(
CompleteUpdatePrintSettings(std::move(job_settings),
std::move(print_settings), std::move(callback));
}
+
+void PrintViewManagerBase::SetAccessibilityTree(
+ int32_t cookie,
+ const ui::AXTreeUpdate& accessibility_tree) {
+ auto* client = PrintCompositeClient::FromWebContents(web_contents());
+ if (client) {
+ client->SetAccessibilityTree(cookie, accessibility_tree);
+ }
+}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
void PrintViewManagerBase::IsPrintingEnabled(
@@ -763,19 +826,26 @@ void PrintViewManagerBase::ScriptedPrint(mojom::ScriptedPrintParamsPtr params,
#endif
#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
absl::optional<enterprise_connectors::ContentAnalysisDelegate::Data>
- scanning_data = enterprise_connectors::GetBeforePrintPreviewAnalysisData(
- web_contents());
+ scanning_data = enterprise_connectors::GetPrintAnalysisData(
+ web_contents(),
+ enterprise_connectors::PrintScanningContext::kBeforeSystemDialog);
if (scanning_data) {
- auto scanning_done_callback = base::BindOnce(
- &PrintViewManagerBase::CompleteScriptedPrintAfterContentAnalysis,
- weak_ptr_factory_.GetWeakPtr(), std::move(params), std::move(callback));
- set_analyzing_content(/*analyzing=*/true);
- GetPrintRenderFrame(render_frame_host)
- ->SnapshotForContentAnalysis(base::BindOnce(
- &PrintViewManagerBase::OnGotSnapshotCallback,
- weak_ptr_factory_.GetWeakPtr(), std::move(scanning_done_callback),
- std::move(*scanning_data), render_frame_host->GetGlobalId()));
- return;
+ if (!ContentAnalysisAfterDialog(*scanning_data)) {
+ auto scanning_done_callback = base::BindOnce(
+ &PrintViewManagerBase::CompleteScriptedPrintAfterContentAnalysis,
+ weak_ptr_factory_.GetWeakPtr(), std::move(params),
+ std::move(callback));
+ set_analyzing_content(/*analyzing=*/true);
+ GetPrintRenderFrame(render_frame_host)
+ ->SnapshotForContentAnalysis(base::BindOnce(
+ &PrintViewManagerBase::OnGotSnapshotCallback,
+ weak_ptr_factory_.GetWeakPtr(), std::move(scanning_done_callback),
+ std::move(*scanning_data), render_frame_host->GetGlobalId()));
+ return;
+ }
+ content_analysis_before_printing_document_ = base::BindOnce(
+ &PrintViewManagerBase::ContentAnalysisBeforePrintingDocument,
+ weak_ptr_factory_.GetWeakPtr(), std::move(*scanning_data));
}
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
@@ -934,7 +1004,12 @@ void PrintViewManagerBase::ShouldQuitFromInnerMessageLoop() {
}
}
-bool PrintViewManagerBase::CreateNewPrintJob(
+scoped_refptr<PrintJob> PrintViewManagerBase::CreatePrintJob(
+ PrintJobManager* print_job_manager) {
+ return base::MakeRefCounted<PrintJob>(print_job_manager);
+}
+
+bool PrintViewManagerBase::SetupNewPrintJob(
std::unique_ptr<PrinterQuery> query) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!quit_inner_loop_);
@@ -952,8 +1027,7 @@ bool PrintViewManagerBase::CreateNewPrintJob(
}
DCHECK(!print_job_);
- print_job_ =
- base::MakeRefCounted<PrintJob>(g_browser_process->print_job_manager());
+ print_job_ = CreatePrintJob(g_browser_process->print_job_manager());
print_job_->Initialize(std::move(query), RenderSourceName(), number_pages());
#if BUILDFLAG(IS_CHROMEOS)
print_job_->SetSource(web_contents()->GetBrowserContext()->IsOffTheRecord()
@@ -1098,14 +1172,16 @@ bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
return false;
}
- if (!CreateNewPrintJob(std::move(queued_query))) {
+ if (!SetupNewPrintJob(std::move(queued_query))) {
// Don't kill anything.
return false;
}
#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
- // Don't start printing if the print job was created only for snapshotting.
- if (analyzing_content_) {
+ // Don't start printing if the print job was created only for snapshotting,
+ // or if content analysis is going to take place right before starting
+ // `print_job_`.
+ if (analyzing_content_ || content_analysis_before_printing_document_) {
return true;
}
#endif
@@ -1211,7 +1287,7 @@ void PrintViewManagerBase::ReleasePrinterQuery() {
if (!current_cookie)
return;
- set_cookie(0);
+ set_cookie(PrintSettings::NewInvalidCookie());
PrintJobManager* print_job_manager = g_browser_process->print_job_manager();
// May be NULL in tests.
@@ -1271,7 +1347,7 @@ void PrintViewManagerBase::CompleteScriptedPrintAfterContentAnalysis(
mojom::ScriptedPrintParamsPtr params,
ScriptedPrintCallback callback,
bool allowed) {
- set_analyzing_content(/*analyzing*/ false);
+ set_analyzing_content(/*analyzing=*/false);
if (!allowed || !printing_rfh_ || IsCrashed() ||
!printing_rfh_->IsRenderFrameLive()) {
std::move(callback).Run(nullptr);
@@ -1280,6 +1356,22 @@ void PrintViewManagerBase::CompleteScriptedPrintAfterContentAnalysis(
CompleteScriptedPrint(printing_rfh_, std::move(params), std::move(callback));
}
+void PrintViewManagerBase::CompletePrintDocumentAfterContentAnalysis(
+ scoped_refptr<base::RefCountedMemory> print_data,
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const gfx::Point& offsets,
+ bool allowed) {
+ if (!allowed || IsCrashed()) {
+ ReleasePrinterQuery();
+ print_job_->CleanupAfterContentAnalysisDenial();
+ TerminatePrintJob(/*cancel=*/true);
+ return;
+ }
+ print_job_->StartPrinting();
+ PrintDocument(print_data, page_size, content_area, offsets);
+}
+
void PrintViewManagerBase::OnGotSnapshotCallback(
base::OnceCallback<void(bool should_proceed)> callback,
enterprise_connectors::ContentAnalysisDelegate::Data data,
@@ -1296,7 +1388,7 @@ void PrintViewManagerBase::OnGotSnapshotCallback(
if (IsOopifEnabled() && print_job_->document()->settings().is_modifiable()) {
auto* client = PrintCompositeClient::FromWebContents(web_contents());
client->DoCompositeDocumentToPdf(
- params->document_cookie, rfh, *params->content,
+ params->document_cookie, rfh, *params->content, ui::AXTreeUpdate(),
base::BindOnce(&PrintViewManagerBase::OnCompositedForContentAnalysis,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
std::move(data), rfh_id));
@@ -1340,8 +1432,28 @@ void PrintViewManagerBase::OnCompositedForContentAnalysis(
safe_browsing::DeepScanAccessPoint::PRINT);
}
+void PrintViewManagerBase::ContentAnalysisBeforePrintingDocument(
+ enterprise_connectors::ContentAnalysisDelegate::Data scanning_data,
+ scoped_refptr<base::RefCountedMemory> print_data,
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const gfx::Point& offsets) {
+ scanning_data.printer_name =
+ base::UTF16ToUTF8(print_job_->document()->settings().device_name());
+
+ auto on_verdict = base::BindOnce(
+ &PrintViewManagerBase::CompletePrintDocumentAfterContentAnalysis,
+ weak_ptr_factory_.GetWeakPtr(), print_data, page_size, content_area,
+ offsets);
+
+ enterprise_connectors::PrintIfAllowedByPolicy(
+ print_data, web_contents()->GetOutermostWebContents(),
+ std::move(scanning_data), std::move(on_verdict));
+}
+
void PrintViewManagerBase::set_analyzing_content(bool analyzing) {
- DVLOG(1) << (analyzing ? "Starting" : "Completed") << " content analysis";
+ PRINTER_LOG(EVENT) << (analyzing ? "Starting" : "Completed")
+ << " content analysis";
analyzing_content_ = analyzing;
}
diff --git a/chromium/chrome/browser/printing/print_view_manager_base.h b/chromium/chrome/browser/printing/print_view_manager_base.h
index 4ae11ba1c9c..ae2339d1ab8 100644
--- a/chromium/chrome/browser/printing/print_view_manager_base.h
+++ b/chromium/chrome/browser/printing/print_view_manager_base.h
@@ -25,6 +25,7 @@
#include "components/printing/common/print.mojom-forward.h"
#include "components/services/print_compositor/public/mojom/print_compositor.mojom.h"
#include "printing/buildflags/buildflags.h"
+#include "ui/accessibility/ax_tree_update_forward.h"
#if BUILDFLAG(ENABLE_OOP_PRINTING)
#include "chrome/browser/printing/print_backend_service_manager.h"
@@ -34,10 +35,6 @@
#include "chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h"
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
-#if BUILDFLAG(ENABLE_TAGGED_PDF)
-#include "ui/accessibility/ax_tree_update_forward.h"
-#endif
-
namespace base {
class RefCountedMemory;
}
@@ -59,7 +56,7 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
virtual void OnScriptedPrint() {}
// This method is never called unless `ENABLE_PRINT_PREVIEW`.
- virtual void OnPrintPreview(const content::RenderFrameHost* rfh) {}
+ virtual void OnPrintPreviewDone() {}
// This method is never called unless `ENABLE_OOP_PRINTING`.
virtual void OnRegisterSystemPrintClient(bool succeeded) {}
@@ -122,16 +119,14 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
void DidGetPrintedPagesCount(int32_t cookie, uint32_t number_pages) override;
void DidPrintDocument(mojom::DidPrintDocumentParamsPtr params,
DidPrintDocumentCallback callback) override;
-#if BUILDFLAG(ENABLE_TAGGED_PDF)
- void SetAccessibilityTree(
- int32_t cookie,
- const ui::AXTreeUpdate& accessibility_tree) override;
-#endif
void GetDefaultPrintSettings(
GetDefaultPrintSettingsCallback callback) override;
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
void UpdatePrintSettings(base::Value::Dict job_settings,
UpdatePrintSettingsCallback callback) override;
+ void SetAccessibilityTree(
+ int32_t cookie,
+ const ui::AXTreeUpdate& accessibility_tree) override;
#endif
void IsPrintingEnabled(IsPrintingEnabledCallback callback) override;
void ScriptedPrint(mojom::ScriptedPrintParamsPtr params,
@@ -174,11 +169,15 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
// content::WebContentsObserver implementation.
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
- // Creates a new empty print job. It has no settings loaded. If there is
+ // Creates a new print job.
+ virtual scoped_refptr<PrintJob> CreatePrintJob(
+ PrintJobManager* print_job_manager);
+
+ // Sets up a new empty print job with no settings loaded. If there is
// currently a print job, safely disconnect from it. Returns false if it is
// impossible to safely disconnect from the current print job or it is
// impossible to create a new print job.
- virtual bool CreateNewPrintJob(std::unique_ptr<PrinterQuery> query);
+ virtual bool SetupNewPrintJob(std::unique_ptr<PrinterQuery> query);
// Makes sure the current print_job_ has all its data before continuing, and
// disconnect from it.
@@ -222,6 +221,16 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
mojom::PrintCompositor::Status status,
base::ReadOnlySharedMemoryRegion page_region);
+ // Helper method bound to `content_analysis_before_printing_document_` when
+ // content analysis should happen right before the document is to be printed.
+ // This method is virtual for testing purposes.
+ virtual void ContentAnalysisBeforePrintingDocument(
+ enterprise_connectors::ContentAnalysisDelegate::Data scanning_data,
+ scoped_refptr<base::RefCountedMemory> print_data,
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const gfx::Point& offsets);
+
// Helper method to set `analyzing_content_` in child classes.
void set_analyzing_content(bool analyzing);
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
@@ -279,19 +288,19 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
// Helpers for PrintForPrintPreview();
void OnPrintSettingsDone(scoped_refptr<base::RefCountedMemory> print_data,
uint32_t page_count,
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ bool show_system_dialog,
+#endif
PrinterHandler::PrintCallback callback,
std::unique_ptr<PrinterQuery> printer_query);
void StartLocalPrintJob(scoped_refptr<base::RefCountedMemory> print_data,
uint32_t page_count,
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ bool show_system_dialog,
+#endif
int cookie,
PrinterHandler::PrintCallback callback);
-
- // Runs `callback` with `params` to reply to UpdatePrintSettings().
- void UpdatePrintSettingsReply(
- mojom::PrintManagerHost::UpdatePrintSettingsCallback callback,
- mojom::PrintPagesParamsPtr params,
- bool canceled);
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
// Runs `callback` with `params` to reply to GetDefaultPrintSettings().
@@ -366,6 +375,17 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
mojom::ScriptedPrintParamsPtr params,
ScriptedPrintCallback callback,
bool allowed);
+
+ // Helper method called after a verdict has been obtained from scanning
+ // to-be-printed content, right before the actual `print_job_` starts.
+ // Printing will proceed only if `allowed` is set to true, otherwise the print
+ // job will be cancelled.
+ void CompletePrintDocumentAfterContentAnalysis(
+ scoped_refptr<base::RefCountedMemory> print_data,
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const gfx::Point& offsets,
+ bool allowed);
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
// The current RFH that is printing with a system printing dialog.
@@ -393,6 +413,15 @@ class PrintViewManagerBase : public PrintManager, public PrintJob::Observer {
#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
// Indicates that the page/document is currently undergoing content analysis.
bool analyzing_content_ = false;
+
+ // Called by `PrintDocument` to insert content analysis logic before key
+ // printing steps like `PrintJob::StartPrinting`.
+ using PrintDocumentCallback =
+ base::OnceCallback<void(scoped_refptr<base::RefCountedMemory> print_data,
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const gfx::Point& offsets)>;
+ PrintDocumentCallback content_analysis_before_printing_document_;
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
const scoped_refptr<PrintQueriesQueue> queue_;
diff --git a/chromium/chrome/browser/printing/print_view_manager_unittest.cc b/chromium/chrome/browser/printing/print_view_manager_unittest.cc
index b7e0d7492ae..bc7cdd367f7 100644
--- a/chromium/chrome/browser/printing/print_view_manager_unittest.cc
+++ b/chromium/chrome/browser/printing/print_view_manager_unittest.cc
@@ -9,6 +9,7 @@
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr_exclusion.h"
+#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
@@ -16,13 +17,13 @@
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/printing/print_job.h"
#include "chrome/browser/printing/print_job_manager.h"
#include "chrome/browser/printing/print_job_worker.h"
#include "chrome/browser/printing/print_test_utils.h"
#include "chrome/browser/printing/print_view_manager.h"
#include "chrome/browser/printing/print_view_manager_base.h"
#include "chrome/browser/printing/printer_query.h"
-#include "chrome/browser/printing/test_print_job.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/browser_with_test_window_test.h"
@@ -33,8 +34,12 @@
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "printing/print_settings.h"
#include "printing/print_settings_conversion.h"
+#include "printing/printed_document.h"
#include "printing/units.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
#if BUILDFLAG(IS_WIN)
#include "printing/mojom/print.mojom.h"
@@ -200,6 +205,96 @@ class TestPrintViewManagerForSystemDialogPrint : public PrintViewManager {
}
};
+class TestPrintJob : public PrintJob {
+ public:
+ // Create an empty `PrintJob`. When initializing with this constructor,
+ // post-constructor initialization must be done with `Initialize()`.
+ TestPrintJob() = default;
+
+ // Getters for values stored by `TestPrintJob` in Start...Converter functions.
+ const gfx::Size& page_size() const { return page_size_; }
+ const gfx::Rect& content_area() const { return content_area_; }
+ const gfx::Point& physical_offsets() const { return physical_offsets_; }
+#if BUILDFLAG(IS_WIN)
+ mojom::PrinterLanguageType type() const { return type_; }
+#endif
+
+ // All remaining functions are `PrintJob` implementation.
+ void Initialize(std::unique_ptr<PrinterQuery> query,
+ const std::u16string& name,
+ uint32_t page_count) override {
+ // Since we do not actually print in these tests, just let this get
+ // destroyed when this function exits.
+ std::unique_ptr<PrintJobWorker> worker =
+ query->TransferContextToNewWorker(nullptr);
+
+ scoped_refptr<PrintedDocument> new_doc =
+ base::MakeRefCounted<PrintedDocument>(query->ExtractSettings(), name,
+ query->cookie());
+
+ new_doc->set_page_count(page_count);
+ UpdatePrintedDocument(new_doc.get());
+ }
+
+ // Sets `job_pending_` to true.
+ void StartPrinting() override { set_job_pending_for_testing(true); }
+
+ // Sets `job_pending_` to false and deletes the worker.
+ void Stop() override { set_job_pending_for_testing(false); }
+
+ // Sets `job_pending_` to false and deletes the worker.
+ void Cancel() override { set_job_pending_for_testing(false); }
+
+ void OnFailed() override {}
+
+ void OnDocDone(int job_id, PrintedDocument* document) override {}
+
+ // Intentional no-op, returns true.
+ bool FlushJob(base::TimeDelta timeout) override { return true; }
+
+#if BUILDFLAG(IS_WIN)
+ // These functions fill in the corresponding member variables based on the
+ // arguments passed in.
+ void StartPdfToEmfConversion(scoped_refptr<base::RefCountedMemory> bytes,
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const GURL& url) override {
+ page_size_ = page_size;
+ content_area_ = content_area;
+ type_ = mojom::PrinterLanguageType::kNone;
+ }
+
+ void StartPdfToPostScriptConversion(
+ scoped_refptr<base::RefCountedMemory> bytes,
+ const gfx::Rect& content_area,
+ const gfx::Point& physical_offsets,
+ bool ps_level2,
+ const GURL& url) override {
+ content_area_ = content_area;
+ physical_offsets_ = physical_offsets;
+ type_ = ps_level2 ? mojom::PrinterLanguageType::kPostscriptLevel2
+ : mojom::PrinterLanguageType::kPostscriptLevel3;
+ }
+
+ void StartPdfToTextConversion(scoped_refptr<base::RefCountedMemory> bytes,
+ const gfx::Size& page_size,
+ const GURL& url) override {
+ page_size_ = page_size;
+ type_ = mojom::PrinterLanguageType::kTextOnly;
+ }
+#endif // BUILDFLAG(IS_WIN)
+
+ private:
+ ~TestPrintJob() override { set_job_pending_for_testing(false); }
+
+ gfx::Size page_size_;
+ gfx::Rect content_area_;
+ gfx::Point physical_offsets_;
+#if BUILDFLAG(IS_WIN)
+ mojom::PrinterLanguageType type_;
+#endif
+};
+
} // namespace
class TestPrintViewManager : public PrintViewManagerBase {
@@ -262,7 +357,7 @@ class TestPrintViewManager : public PrintViewManagerBase {
protected:
// Override to create a `TestPrintJob` instead of a real one.
- bool CreateNewPrintJob(std::unique_ptr<PrinterQuery> query) override {
+ bool SetupNewPrintJob(std::unique_ptr<PrinterQuery> query) override {
print_job_ = base::MakeRefCounted<TestPrintJob>();
print_job_->Initialize(std::move(query), RenderSourceName(),
number_pages());
diff --git a/chromium/chrome/browser/printing/printer_query.cc b/chromium/chrome/browser/printing/printer_query.cc
index 0b6ff160cc0..83bce57ef52 100644
--- a/chromium/chrome/browser/printing/printer_query.cc
+++ b/chromium/chrome/browser/printing/printer_query.cc
@@ -143,7 +143,7 @@ void PrinterQuery::GetSettingsDone(base::OnceClosure callback,
cookie_ = PrintSettings::NewCookie();
} else {
// Failure.
- cookie_ = 0;
+ cookie_ = PrintSettings::NewInvalidCookie();
}
std::move(callback).Run();
@@ -418,6 +418,7 @@ void PrinterQuery::GetSettingsWithUI(uint32_t document_page_count,
web_contents->ExitFullscreen(true);
}
+ PRINTER_LOG(EVENT) << "Getting printer settings from user in-process";
printing_context_->AskUserForSettings(
base::checked_cast<int>(document_page_count), has_selection, is_scripted,
base::BindOnce(&PrinterQuery::InvokeSettingsCallback,
@@ -427,6 +428,7 @@ void PrinterQuery::GetSettingsWithUI(uint32_t document_page_count,
void PrinterQuery::UseDefaultSettings(SettingsCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ PRINTER_LOG(EVENT) << "Using printer default settings in-process";
mojom::ResultCode result;
{
#if BUILDFLAG(IS_WIN)
diff --git a/chromium/chrome/browser/printing/printer_query_oop.cc b/chromium/chrome/browser/printing/printer_query_oop.cc
index b9d684febad..1aa03b27c85 100644
--- a/chromium/chrome/browser/printing/printer_query_oop.cc
+++ b/chromium/chrome/browser/printing/printer_query_oop.cc
@@ -77,7 +77,7 @@ void PrinterQueryOop::OnDidUseDefaultSettings(
} else {
VLOG(1) << "Use default settings from service complete";
result = mojom::ResultCode::kSuccess;
- printing_context()->ApplyPrintSettings(print_settings->get_settings());
+ printing_context()->SetPrintSettings(print_settings->get_settings());
}
InvokeSettingsCallback(std::move(callback), result);
@@ -92,7 +92,7 @@ void PrinterQueryOop::OnDidAskUserForSettings(
if (print_settings->is_settings()) {
VLOG(1) << "Ask user for settings from service complete";
result = mojom::ResultCode::kSuccess;
- printing_context()->ApplyPrintSettings(print_settings->get_settings());
+ printing_context()->SetPrintSettings(print_settings->get_settings());
// Use the same PrintBackendService for querying and printing, so that the
// same device context can be used with both.
@@ -136,6 +136,8 @@ void PrinterQueryOop::UseDefaultSettings(SettingsCallback callback) {
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
CHECK(query_with_ui_client_id_.has_value());
+ PRINTER_LOG(EVENT) << "Using printer default settings via service";
+
// Any settings selected from the system dialog could need to be retained
// for printing, so establish a printing context.
CHECK(!context_id_.has_value());
@@ -160,6 +162,7 @@ void PrinterQueryOop::GetSettingsWithUI(uint32_t document_page_count,
print_from_system_dialog_ = true;
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
+ PRINTER_LOG(EVENT) << "Getting printer settings from user via service";
SendAskUserForSettings(document_page_count, has_selection, is_scripted,
std::move(callback));
#else
@@ -253,7 +256,7 @@ void PrinterQueryOop::OnDidUpdatePrintSettings(
} else {
VLOG(1) << "Update print settings via service complete for " << device_name;
result = mojom::ResultCode::kSuccess;
- printing_context()->ApplyPrintSettings(print_settings->get_settings());
+ printing_context()->SetPrintSettings(print_settings->get_settings());
if (query_with_ui_client_id_.has_value()) {
// Use the same PrintBackendService for querying and printing, so that the
diff --git a/chromium/chrome/browser/printing/system_access_process_print_browsertest.cc b/chromium/chrome/browser/printing/system_access_process_print_browsertest.cc
index 9e66219355b..287d49a9405 100644
--- a/chromium/chrome/browser/printing/system_access_process_print_browsertest.cc
+++ b/chromium/chrome/browser/printing/system_access_process_print_browsertest.cc
@@ -24,6 +24,7 @@
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
+#include "components/enterprise/common/proto/connectors.pb.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
@@ -35,8 +36,11 @@
#include "printing/printing_context.h"
#include "printing/printing_features.h"
#include "printing/printing_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size_f.h"
#if BUILDFLAG(ENABLE_OOP_PRINTING)
#include "chrome/browser/printing/print_backend_service_manager.h"
@@ -51,6 +55,11 @@
#include "chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h" // nogncheck
#include "chrome/browser/enterprise/connectors/test/fake_content_analysis_delegate.h" // nogncheck
#include "chrome/browser/policy/dm_token_utils.h"
+#include "components/enterprise/buildflags/buildflags.h"
+
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+#include "chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.h" // nogncheck
+#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
#if BUILDFLAG(IS_CHROMEOS)
@@ -64,21 +73,46 @@ namespace printing {
namespace {
#if !BUILDFLAG(IS_CHROMEOS)
-constexpr gfx::Size kLetterPhysicalSize = gfx::Size(612, 792);
-constexpr gfx::Rect kLetterPrintableArea = gfx::Rect(5, 5, 602, 782);
-constexpr gfx::Size kLegalPhysicalSize = gfx::Size(612, 1008);
-constexpr gfx::Rect kLegalPrintableArea = gfx::Rect(5, 5, 602, 998);
+constexpr gfx::SizeF kLetterPhysicalSize = gfx::SizeF(612, 792);
+constexpr gfx::RectF kLetterPrintableArea = gfx::RectF(5, 5, 602, 782);
+constexpr gfx::SizeF kLegalPhysicalSize = gfx::SizeF(612, 1008);
+constexpr gfx::RectF kLegalPrintableArea = gfx::RectF(5, 5, 602, 998);
// The default margins are set to 1.0cm in //printing/print_settings.cc, which
// is about 28 printer units. The resulting content size is 556 x 736 for
// Letter, and similarly is 556 x 952 for Legal.
-constexpr gfx::Size kLetterExpectedContentSize = gfx::Size(556, 736);
-constexpr gfx::Size kLegalExpectedContentSize = gfx::Size(556, 952);
+constexpr gfx::SizeF kLetterExpectedContentSize = gfx::SizeF(556, 736);
+constexpr gfx::SizeF kLegalExpectedContentSize = gfx::SizeF(556, 952);
#endif // !BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
constexpr char kFakeDmToken[] = "fake-dm-token";
+// The policy values below correspond to the schema described in
+// https://chromeenterprise.google/policies/#OnPrintEnterpriseConnector
+constexpr char kCloudAnalysisBlockingPolicy[] = R"({
+ "service_provider": "google",
+ "enable": [ {"url_list": ["*"], "tags": ["dlp"]} ],
+ "block_until_verdict": 1,
+ "block_large_files": true
+})";
+
+constexpr char kCloudAnalysisNonBlockingPolicy[] = R"({
+ "service_provider": "google",
+ "enable": [ {"url_list": ["*"], "tags": ["dlp"]} ],
+ "block_until_verdict": 0,
+ "block_large_files": true
+})";
+
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+constexpr char kLocalAnalysisPolicy[] = R"({
+ "service_provider": "local_user_agent",
+ "enable": [ {"url_list": ["*"], "tags": ["dlp"]} ],
+ "block_until_verdict": 1,
+ "block_large_files": true
+})";
+#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+
using OnDidCompositeForContentAnalysis =
base::RepeatingCallback<void(bool allowed)>;
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
@@ -158,9 +192,14 @@ class TestPrintJobWorkerOop : public PrintJobWorkerOop {
// processing was done before possibly quitting the test run loop.
struct PrintCallbacks {
ErrorCheckCallback error_check_callback;
- OnDidUseDefaultSettingsCallback did_use_default_settings_callback;
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
+ OnDidUseDefaultSettingsCallback did_use_default_settings_callback;
OnDidAskUserForSettingsCallback did_ask_user_for_settings_callback;
+#else
+ // Need to use the base class version of callbacks when the system dialog
+ // must be displayed from the browser process.
+ OnUseDefaultSettingsCallback did_use_default_settings_callback;
+ OnGetSettingsWithUICallback did_get_settings_with_ui_callback;
#endif
OnDidUpdatePrintSettingsCallback did_update_print_settings_callback;
OnDidStartPrintingCallback did_start_printing_callback;
@@ -247,6 +286,7 @@ class TestPrinterQueryOop : public PrinterQueryOop {
simulate_spooling_memory_errors_(simulate_spooling_memory_errors),
callbacks_(callbacks) {}
+#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
void OnDidUseDefaultSettings(
SettingsCallback callback,
mojom::PrintSettingsResultPtr print_settings) override {
@@ -260,7 +300,6 @@ class TestPrinterQueryOop : public PrinterQueryOop {
callbacks_->did_use_default_settings_callback.Run(result);
}
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
void OnDidAskUserForSettings(
SettingsCallback callback,
mojom::PrintSettingsResultPtr print_settings) override {
@@ -273,6 +312,22 @@ class TestPrinterQueryOop : public PrinterQueryOop {
std::move(print_settings));
callbacks_->did_ask_user_for_settings_callback.Run(result);
}
+#else
+ void UseDefaultSettings(SettingsCallback callback) override {
+ DVLOG(1) << "Observed: invoke use default settings";
+ PrinterQueryOop::UseDefaultSettings(std::move(callback));
+ callbacks_->did_use_default_settings_callback.Run();
+ }
+
+ void GetSettingsWithUI(uint32_t document_page_count,
+ bool has_selection,
+ bool is_scripted,
+ SettingsCallback callback) override {
+ DVLOG(1) << "Observed: invoke get settings with UI";
+ PrinterQueryOop::GetSettingsWithUI(document_page_count, has_selection,
+ is_scripted, std::move(callback));
+ callbacks_->did_get_settings_with_ui_callback.Run();
+ }
#endif // BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
void OnDidUpdatePrintSettings(
@@ -316,15 +371,44 @@ class SystemAccessProcessPrintBrowserTestBase
// Only of interest when `UseService()` returns true.
virtual bool SandboxService() = 0;
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ // Only of interest for content analysis tests. This will enable/disable the
+ // kEnableLocalScanAfterPreview and kEnableCloudScanAfterPreview features so
+ // that content analysis is done after the printing settings are picked from a
+ // dialog.
+ virtual bool EnableContentAnalysisAfterDialog() = 0;
+#endif
+
+ void SetUpFeatures() {
+ std::vector<base::test::FeatureRefAndParams> enabled_features;
+ std::vector<base::test::FeatureRef> disabled_features;
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ if (EnableContentAnalysisAfterDialog()) {
+ enabled_features.push_back({features::kEnableLocalScanAfterPreview, {}});
+ enabled_features.push_back({features::kEnableCloudScanAfterPreview, {}});
+ } else {
+ disabled_features.push_back(features::kEnableLocalScanAfterPreview);
+ disabled_features.push_back(features::kEnableCloudScanAfterPreview);
+ }
+#endif
+ if (UseService()) {
+ enabled_features.push_back(
+ {features::kEnableOopPrintDrivers,
+ {{features::kEnableOopPrintDriversJobPrint.name, "true"},
+ {features::kEnableOopPrintDriversSandbox.name,
+ SandboxService() ? "true" : "false"}}});
+ } else {
+ disabled_features.push_back(features::kEnableOopPrintDrivers);
+ }
+ feature_list_.InitWithFeaturesAndParameters(enabled_features,
+ disabled_features);
+ }
+
void SetUp() override {
#if BUILDFLAG(ENABLE_OOP_PRINTING)
- if (UseService()) {
- feature_list_.InitAndEnableFeatureWithParameters(
- features::kEnableOopPrintDrivers,
- {{features::kEnableOopPrintDriversJobPrint.name, "true"},
- {features::kEnableOopPrintDriversSandbox.name,
- SandboxService() ? "true" : "false"}});
+ SetUpFeatures();
+ if (UseService()) {
// Safe to use `base::Unretained(this)` since this testing class
// necessarily must outlive all interactions from the tests which will
// run through `TestPrintJobWorkerOop`, the user of these callbacks.
@@ -332,16 +416,25 @@ class SystemAccessProcessPrintBrowserTestBase
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::ErrorCheck,
base::Unretained(this));
+#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
test_print_job_worker_oop_callbacks_.did_use_default_settings_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidUseDefaultSettings,
base::Unretained(this));
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
test_print_job_worker_oop_callbacks_.did_ask_user_for_settings_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidAskUserForSettings,
base::Unretained(this));
-#endif
+#else
+ test_print_job_worker_oop_callbacks_.did_use_default_settings_callback =
+ base::BindRepeating(
+ &SystemAccessProcessPrintBrowserTestBase::OnUseDefaultSettings,
+ base::Unretained(this));
+ test_print_job_worker_oop_callbacks_.did_get_settings_with_ui_callback =
+ base::BindRepeating(
+ &SystemAccessProcessPrintBrowserTestBase::OnGetSettingsWithUI,
+ base::Unretained(this));
+#endif // BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
test_print_job_worker_oop_callbacks_
.did_update_print_settings_callback = base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnDidUpdatePrintSettings,
@@ -369,10 +462,6 @@ class SystemAccessProcessPrintBrowserTestBase
&SystemAccessProcessPrintBrowserTestBase::OnDidCancel,
base::Unretained(this));
} else {
- feature_list_.InitWithFeatures(
- /*enabled_features=*/{},
- /*disabled_features=*/{features::kEnableOopPrintDrivers});
-
test_print_job_worker_callbacks_.did_use_default_settings_callback =
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnUseDefaultSettings,
@@ -466,6 +555,12 @@ class SystemAccessProcessPrintBrowserTestBase
&SystemAccessProcessPrintBrowserTestBase::OnCreatedPrintJob,
base::Unretained(this)));
manager->AddTestObserver(*this);
+#if BUILDFLAG(IS_WIN)
+ if (simulate_pdf_conversion_error_on_page_index_.has_value()) {
+ manager->set_simulate_pdf_conversion_error_on_page_index(
+ *simulate_pdf_conversion_error_on_page_index_);
+ }
+#endif
TestPrintViewManager* manager_ptr = manager.get();
web_contents->SetUserData(PrintViewManager::UserDataKey(),
std::move(manager));
@@ -477,15 +572,15 @@ class SystemAccessProcessPrintBrowserTestBase
}
void PrintAfterPreviewIsReadyAndLoaded() {
- // First invoke the Print Preview dialog with `StartPrint()`.
- TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true);
- test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents());
+ PrintAfterPreviewIsReadyAndLoaded(PrintParams());
+ }
+
+ void PrintAfterPreviewIsReadyAndLoaded(const PrintParams& params) {
+ // First invoke the Print Preview dialog with requested method.
content::WebContents* preview_dialog =
- print_preview_observer.WaitUntilPreviewIsReadyAndReturnPreviewDialog();
+ PrintAndWaitUntilPreviewIsReadyAndLoaded(params);
ASSERT_TRUE(preview_dialog);
- set_rendered_page_count(print_preview_observer.rendered_page_count());
-
// Print Preview is completely ready, can now initiate printing.
// This script locates and clicks the Print button.
const char kScript[] = R"(
@@ -494,26 +589,41 @@ class SystemAccessProcessPrintBrowserTestBase
.shadowRoot.querySelector('print-preview-button-strip')
.shadowRoot.querySelector('.action-button');
button.click();)";
- ASSERT_TRUE(content::ExecJs(preview_dialog, kScript));
+ auto result = content::ExecJs(preview_dialog, kScript);
+ // TODO(crbug.com/1472464): Update once it is known if the assertion
+ // should not happen if the failure is just because the renderer
+ // terminated.
+ // If the renderer terminates, it will return a failing result. It has
+ // been observed in other tests that sometimes the renderer terminates
+ // and the test was successful; all the needed callbacks happened before
+ // ExecJs() returned.
+ // Add a warning for the logs to help with debugging, and then only do
+ // the assert check after having done the wait.
+ // If the renderer terminated but the printing was all successful, then
+ // `WaitUntilCallbackReceived()` should return successfully, and any crash
+ // logs should show the assert. Otherwise the crashes for this bug should
+ // change to become the test timeouts.
+ if (!result) {
+ LOG(ERROR) << "ExecJs() failed; if reason is because the renderer "
+ "terminated, it is possibly okay?";
+ LOG(ERROR) << result.message();
+ }
WaitUntilCallbackReceived();
+ ASSERT_TRUE(result);
}
void AdjustMediaAfterPreviewIsReadyAndLoaded() {
// First invoke the Print Preview dialog with `StartPrint()`.
- TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true);
- test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents());
content::WebContents* preview_dialog =
- print_preview_observer.WaitUntilPreviewIsReadyAndReturnPreviewDialog();
+ PrintAndWaitUntilPreviewIsReadyAndLoaded();
ASSERT_TRUE(preview_dialog);
- set_rendered_page_count(print_preview_observer.rendered_page_count());
-
// Initial Print Preview is completely ready.
- // Reset the observer, and then modify the paper size. This will initiate
- // another preview render.
+ // Create an observer and modify the paper size. This will initiate another
+ // preview render.
// The default paper size is first in the list at index zero, so choose
// the second item from the list to cause a change.
- print_preview_observer.ResetForAnotherPreview();
+ TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true);
const char kSetPaperSizeScript[] = R"(
var element =
document.getElementsByTagName('print-preview-app')[0]
@@ -527,24 +637,20 @@ class SystemAccessProcessPrintBrowserTestBase
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
void SystemPrintFromPreviewOnceReadyAndLoaded(bool wait_for_callback) {
// First invoke the Print Preview dialog with `StartPrint()`.
- TestPrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true);
- test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents());
content::WebContents* preview_dialog =
- print_preview_observer.WaitUntilPreviewIsReadyAndReturnPreviewDialog();
+ PrintAndWaitUntilPreviewIsReadyAndLoaded();
ASSERT_TRUE(preview_dialog);
- set_rendered_page_count(print_preview_observer.rendered_page_count());
-
// Print Preview is completely ready, can now initiate printing.
// This script locates and clicks the "Print using system dialog",
// which is still enabled even if it is hidden.
const char kPrintWithSystemDialogScript[] = R"(
- const printSystemDialog
- = document.getElementsByTagName('print-preview-app')[0]
+ const printSystemDialog =
+ document.getElementsByTagName('print-preview-app')[0]
.$['sidebar']
.shadowRoot.querySelector('print-preview-link-container')
.$['systemDialogLink'];
- printSystemDialog.click();)";
+ printSystemDialog.click();)";
// It is possible for sufficient processing for the system print to
// complete such that the renderer naturally terminates before ExecJs()
// returns here. This causes ExecJs() to return false, with a JavaScript
@@ -559,8 +665,36 @@ class SystemAccessProcessPrintBrowserTestBase
}
#endif // BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
+#if BUILDFLAG(IS_MAC)
+ void OpenPdfInPreviewOnceReadyAndLoaded() {
+ // First invoke the Print Preview dialog with `StartPrint()`.
+ content::WebContents* preview_dialog =
+ PrintAndWaitUntilPreviewIsReadyAndLoaded();
+ ASSERT_TRUE(preview_dialog);
+
+ // Print Preview is completely ready, can now initiate printing.
+ // This script locates and clicks "Open PDF in Preview", which is still
+ // enabled even if it is hidden.
+ const char kOpenPdfWithPreviewScript[] = R"(
+ const openPdfInPreview =
+ document.getElementsByTagName('print-preview-app')[0]
+ .$['sidebar']
+ .shadowRoot.querySelector('print-preview-link-container')
+ .$['openPdfInPreviewLink'];
+ openPdfInPreview.click();)";
+ ASSERT_TRUE(content::ExecJs(preview_dialog, kOpenPdfWithPreviewScript));
+ WaitUntilCallbackReceived();
+ }
+#endif // BUILDFLAG(IS_MAC)
+
void PrimeAsRepeatingErrorGenerator() { reset_errors_after_check_ = false; }
+#if BUILDFLAG(IS_WIN)
+ void PrimeForPdfConversionErrorOnPageIndex(uint32_t page_index) {
+ simulate_pdf_conversion_error_on_page_index_ = page_index;
+ }
+#endif
+
#if BUILDFLAG(ENABLE_OOP_PRINTING)
void PrimeForSpoolingSharedMemoryErrors() {
simulate_spooling_memory_errors_ = true;
@@ -802,6 +936,9 @@ class SystemAccessProcessPrintBrowserTestBase
bool did_get_settings_with_ui_ = false;
bool print_backend_service_use_detected_ = false;
bool simulate_spooling_memory_errors_ = false;
+#if BUILDFLAG(IS_WIN)
+ absl::optional<uint32_t> simulate_pdf_conversion_error_on_page_index_;
+#endif
mojo::Remote<mojom::PrintBackendService> test_remote_;
std::unique_ptr<PrintBackendServiceTestImpl> print_backend_service_;
#endif // BUILDFLAG(ENABLE_OOP_PRINTING)
@@ -849,6 +986,9 @@ class SystemAccessProcessSandboxedServicePrintBrowserTest
bool UseService() override { return true; }
bool SandboxService() override { return true; }
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ bool EnableContentAnalysisAfterDialog() override { return false; }
+#endif
};
class SystemAccessProcessServicePrintBrowserTest
@@ -862,6 +1002,9 @@ class SystemAccessProcessServicePrintBrowserTest
bool SandboxService() override {
return GetParam() == PrintBackendFeatureVariation::kOopSandboxedService;
}
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ bool EnableContentAnalysisAfterDialog() override { return false; }
+#endif
};
INSTANTIATE_TEST_SUITE_P(
@@ -880,6 +1023,9 @@ class SystemAccessProcessInBrowserPrintBrowserTest
bool UseService() override { return false; }
bool SandboxService() override { return false; }
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ bool EnableContentAnalysisAfterDialog() override { return false; }
+#endif
};
class SystemAccessProcessPrintBrowserTest
@@ -895,6 +1041,9 @@ class SystemAccessProcessPrintBrowserTest
bool SandboxService() override {
return GetParam() == PrintBackendFeatureVariation::kOopSandboxedService;
}
+#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+ bool EnableContentAnalysisAfterDialog() override { return false; }
+#endif
};
INSTANTIATE_TEST_SUITE_P(
@@ -996,7 +1145,7 @@ IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
// 3. Rendering for 1 page of document of content.
// 4. Completes with document done.
// 5. Wait for the one print job to be destroyed, to ensure printing
- // finished cleanly before completing the test.
+ // finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
PrintAfterPreviewIsReadyAndLoaded();
@@ -1121,14 +1270,59 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
EXPECT_EQ(print_job_destruction_count(), 1);
}
-// TODO(crbug.com/1384459): Flaky on MSan builds.
-#if defined(MEMORY_SANITIZER)
-#define MAYBE_StartPrintingFails DISABLED_StartPrintingFails
-#else
-#define MAYBE_StartPrintingFails StartPrintingFails
-#endif
+#if BUILDFLAG(IS_WIN)
+IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
+ StartPrintingPdfConversionFails) {
+ AddPrinter("printer1");
+ SetPrinterNameForSubsequentContexts("printer1");
+ PrimeForPdfConversionErrorOnPageIndex(/*page_index=*/1);
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/multipage.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ SetUpPrintViewManager(web_contents);
+
+ if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
+ // There are no callbacks for print stages with in-browser printing. So
+ // the print job is started, but that fails, and there is no capturing of
+ // that result.
+ // The expected events for this are:
+ // 1. Print job is started, but is destroyed due to failure during PDF
+ // conversion failure.
+ // No error dialog is shown.
+ SetNumExpectedMessages(/*num=*/1);
+ } else {
+ // The expected events for this are:
+ // 1. Update print settings.
+ // 2. A print job is started.
+ // 3. PDF conversion fails, which results in the print job being
+ // canceled.
+ // 4. Wait for the print job to be destroyed, to ensure printing finished
+ // cleanly before completing the test.
+ // No error dialog is shown.
+ SetNumExpectedMessages(/*num=*/4);
+ }
+ PrintAfterPreviewIsReadyAndLoaded();
+
+ // No tracking of start printing or cancel callbacks for in-browser tests,
+ // only for OOP.
+ if (GetParam() != PrintBackendFeatureVariation::kInBrowserProcess) {
+ EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
+ // TODO(crbug.com/1008222) Include Windows coverage of
+ // RenderPrintedDocument() once XPS print pipeline is added.
+ EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kFailed);
+ }
+ EXPECT_EQ(error_dialog_shown_count(), 0u);
+ EXPECT_EQ(print_job_destruction_count(), 1);
+}
+#endif // BUILDFLAG(IS_WIN)
+
IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
- MAYBE_StartPrintingFails) {
+ StartPrintingFails) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForErrorsInNewDocument();
@@ -1514,15 +1708,17 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
#else
// Once the transition to system print is initiated, the expected events
// are:
- // 1. A print job is started.
- // 2. Rendering for 1 page of document of content.
- // 3. Completes with document done.
- // 4. Wait until all processing for DidPrintDocument is known to have
+ // 1. Use default settings.
+ // 2. Ask the user for settings.
+ // 3. A print job is started.
+ // 4. Rendering for 1 page of document of content.
+ // 5. Completes with document done.
+ // 6. Wait until all processing for DidPrintDocument is known to have
// completed, to ensure printing finished cleanly before completing the
// test.
- // 5. Wait for the one print job to be destroyed, to ensure printing
+ // 7. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
- SetNumExpectedMessages(/*num=*/5);
+ SetNumExpectedMessages(/*num=*/7);
#endif // BUILDFLAG(IS_WIN)
}
SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true);
@@ -1545,15 +1741,8 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess);
#endif
EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess);
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
EXPECT_EQ(*test::MakeUserModifiedPrintSettings("printer1"),
*document_print_settings());
-#else
- // TODO(crbug.com/1414968): Update the expectation once system print
- // settings are properly reflected at start of job print.
- EXPECT_NE(*test::MakeUserModifiedPrintSettings("printer1"),
- *document_print_settings());
-#endif
}
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(print_job_destruction_count(), 1);
@@ -1620,7 +1809,6 @@ IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
// The expected events for this are:
// 1. Get the default settings.
// 2. Ask the user for settings.
@@ -1631,18 +1819,6 @@ IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
// 7. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/7);
-#else
- // The expected events for this are:
- // 1. Get default settings, followed by asking user for settings. This is
- // invoked from the browser process, so there is no override to observe
- // this. Then a print job is started.
- // 2. The print compositor will complete generating the document.
- // 3. The document is rendered.
- // 4. Receive document done notification.
- // 5. Wait for the one print job to be destroyed, to ensure printing
- // finished cleanly before completing the test.
- SetNumExpectedMessages(/*num=*/5);
-#endif
StartBasicPrint(web_contents);
@@ -1654,14 +1830,12 @@ IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
EXPECT_EQ(use_default_settings_result(), mojom::ResultCode::kSuccess);
EXPECT_EQ(ask_user_for_settings_result(), mojom::ResultCode::kSuccess);
- EXPECT_EQ(*test::MakeUserModifiedPrintSettings("printer1"),
- *document_print_settings());
#else
- // TODO(crbug.com/1414968): Update the expectation once system print
- // settings are properly reflected at start of job print.
- EXPECT_NE(*test::MakeUserModifiedPrintSettings("printer1"),
- *document_print_settings());
+ EXPECT_TRUE(did_use_default_settings());
+ EXPECT_TRUE(did_get_settings_with_ui());
#endif
+ EXPECT_EQ(*test::MakeUserModifiedPrintSettings("printer1"),
+ *document_print_settings());
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
#if BUILDFLAG(IS_WIN)
// TODO(crbug.com/1008222) Include Windows coverage of
@@ -1677,14 +1851,8 @@ IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
EXPECT_EQ(print_job_destruction_count(), 1);
}
-// TODO(crbug.com/1375007): Very flaky on Mac and slightly on Linux.
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-#define MAYBE_StartBasicPrintCancel DISABLED_StartBasicPrintCancel
-#else
-#define MAYBE_StartBasicPrintCancel StartBasicPrintCancel
-#endif
-IN_PROC_BROWSER_TEST_F(SystemAccessProcessInBrowserPrintBrowserTest,
- MAYBE_StartBasicPrintCancel) {
+IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
+ StartBasicPrintCancel) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
PrimeForCancelInAskUserForSettings();
@@ -1698,29 +1866,35 @@ IN_PROC_BROWSER_TEST_F(SystemAccessProcessInBrowserPrintBrowserTest,
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
// The expected events for this are:
// 1. Get the default settings.
// 2. Ask the user for settings, which indicates to cancel the print
// request. No further printing calls are made.
// No print job is created because of such an early cancel.
SetNumExpectedMessages(/*num=*/2);
-#else
- // TODO(crbug.com/1375007) Need a good signal to use for test expectations.
-#endif
StartBasicPrint(web_contents);
WaitUntilCallbackReceived();
- EXPECT_TRUE(did_use_default_settings());
- EXPECT_TRUE(did_get_settings_with_ui());
+ if (UseService()) {
+#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
+ EXPECT_EQ(use_default_settings_result(), mojom::ResultCode::kSuccess);
+ EXPECT_EQ(ask_user_for_settings_result(), mojom::ResultCode::kCanceled);
+#else
+ EXPECT_TRUE(did_use_default_settings());
+ EXPECT_TRUE(did_get_settings_with_ui());
+#endif
+ } else {
+ EXPECT_TRUE(did_use_default_settings());
+ EXPECT_TRUE(did_get_settings_with_ui());
+
+ // `PrintBackendService` should never be used when printing in-browser.
+ EXPECT_FALSE(print_backend_service_use_detected());
+ }
EXPECT_EQ(error_dialog_shown_count(), 0u);
EXPECT_EQ(did_print_document_count(), 0);
EXPECT_EQ(print_job_destruction_count(), 0);
-
- // `PrintBackendService` should never be used when printing in-browser.
- EXPECT_FALSE(print_backend_service_use_detected());
}
IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
@@ -1753,7 +1927,6 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/5);
} else {
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
// The expected events for this are:
// 1. Gets default settings.
// 2. Asks user for settings.
@@ -1768,21 +1941,6 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
// DidPrintDocument is known to have completed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/7);
-#else
- // The expected events for this are:
- // 1. Get default settings, followed by asking user for settings. This is
- // invoked from the browser process, so there is no override to observe
- // this. Then a print job is started, which fails.
- // 2. An error dialog is shown.
- // 3. The print job is canceled. The callback from the service could occur
- // after the print job has been destroyed.
- // 4. Wait for the one print job to be destroyed, to ensure printing
- // finished cleanly before completing the test.
- // 5. The print compositor will have started to generate the document.
- // Wait until that is known to have completed, to ensure printing
- // finished cleanly before completing the test.
- SetNumExpectedMessages(/*num=*/5);
-#endif // BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
}
StartBasicPrint(web_contents);
@@ -1798,25 +1956,15 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
EXPECT_EQ(print_job_destruction_count(), 1);
}
-// macOS and Linux currently have to invoke a system dialog from within the
-// browser process. There is not a callback to capture the result in these
-// cases.
-// TODO(crbug.com/1374188) Re-enable for Linux once `AskForUserSettings()` is
-// able to be pushed OOP for Linux.
-#undef MAYBE_StartBasicPrintCancel
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-#define MAYBE_StartBasicPrintCancel DISABLED_StartBasicPrintCancel
-#else
-#define MAYBE_StartBasicPrintCancel StartBasicPrintCancel
-#endif
-IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
- MAYBE_StartBasicPrintCancel) {
+#if BUILDFLAG(IS_WIN)
+IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest,
+ StartBasicPrintPdfConversionFails) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
- PrimeForCancelInAskUserForSettings();
+ PrimeForPdfConversionErrorOnPageIndex(/*page_index=*/1);
ASSERT_TRUE(embedded_test_server()->Started());
- GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
+ GURL url(embedded_test_server()->GetURL("/printing/multipage.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* web_contents =
@@ -1824,36 +1972,55 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
- // The expected events for this are:
- // 1. Get the default settings.
- // 2. Ask the user for settings, which indicates to cancel the print
- // request. No further printing calls are made.
- // No print job is created because of such an early cancel.
- SetNumExpectedMessages(/*num=*/2);
+ if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
+ // There are only partial overrides to track most steps in the printing
+ // pipeline, so the expected events for this are:
+ // 1. Gets default settings.
+ // 2. Asks user for settings.
+ // 3. A print job is started, but is destroyed due to failure during PDF
+ // conversion.
+ // 4. The renderer will have initiated printing of document, which could
+ // invoke the print compositor. Wait until all processing for
+ // DidPrintDocument is known to have completed, to ensure printing
+ // finished cleanly before completing the test.
+ // No error dialog is shown.
+ SetNumExpectedMessages(/*num=*/4);
+ } else {
+ // The expected events for this are:
+ // 1. Gets default settings.
+ // 2. Asks user for settings.
+ // 3. A print job is started.
+ // 4. Notified of DidPrintDocument(), that composition of the print
+ // document has completed.
+ // 5. The PDF conversion fails, resulting in canceling the print job.
+ // 6. The print job is destroyed.
+ // No error dialog is shown.
+ SetNumExpectedMessages(/*num=*/6);
+ }
StartBasicPrint(web_contents);
WaitUntilCallbackReceived();
- EXPECT_EQ(use_default_settings_result(), mojom::ResultCode::kSuccess);
- EXPECT_EQ(ask_user_for_settings_result(), mojom::ResultCode::kCanceled);
+ if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
+ EXPECT_EQ(start_printing_result(), mojom::ResultCode::kFailed);
+ } else {
+ EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
+ // TODO(crbug.com/1008222) Include Windows coverage of
+ // RenderPrintedDocument() once XPS print pipeline is added.
+ EXPECT_EQ(render_printed_page_result(), mojom::ResultCode::kFailed);
+ }
EXPECT_EQ(error_dialog_shown_count(), 0u);
- EXPECT_EQ(did_print_document_count(), 0);
- EXPECT_EQ(print_job_construction_count(), 0);
+ EXPECT_EQ(print_job_destruction_count(), 1);
}
+#endif // BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS)
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
- StartBasicPrintConcurrent) {
- // Linux allows concurrent printing, so regular setup for printing is needed.
- // It is uninteresting to do a full print in this case, it is better to exit
- // the print sequence early, but at a known time after when PrintNow() would
- // fail if concurrent printing isn't allowed. That can be achieved by just
- // canceling out from asking for settings.
-#if BUILDFLAG(IS_LINUX)
+ StartBasicPrintConcurrentAllowed) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
- PrimeForCancelInAskUserForSettings();
-#endif
ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
@@ -1870,32 +2037,25 @@ IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
PrintBackendServiceManager::GetInstance().RegisterQueryWithUiClient();
ASSERT_TRUE(client_id.has_value());
-#if BUILDFLAG(IS_LINUX)
// The expected events for this are:
- // 1. Get the default settings.
- // 2. Ask the user for settings, which indicates to cancel the print
- // request. No further printing calls are made.
- // No print job is created because of such an early cancel.
- SetNumExpectedMessages(/*num=*/2);
-#endif
+ // 1. Gets default settings.
+ // 2. Asks user for settings.
+ // 3. Start the print job.
+ // 4. Rendering for 1 page of document of content.
+ // 5. Completes with document done.
+ // 6. Wait until all processing for DidPrintDocument is known to have
+ // completed, to ensure printing finished cleanly before completing the
+ // test.
+ // 7. Wait for the one print job to be destroyed, to ensure printing
+ // finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/7);
// Now initiate a system print that would exist concurrently with that.
StartBasicPrint(web_contents);
-#if BUILDFLAG(IS_LINUX)
WaitUntilCallbackReceived();
-#endif
- const absl::optional<bool>& result = print_view_manager->print_now_result();
- ASSERT_TRUE(result.has_value());
- // With the exception of Linux, concurrent system print is not allowed.
-#if BUILDFLAG(IS_LINUX)
- EXPECT_TRUE(*result);
-#else
- // The denied concurrent print is silent without an error.
- EXPECT_EQ(error_dialog_shown_count(), 0u);
- EXPECT_FALSE(*result);
-#endif
+ EXPECT_THAT(print_view_manager->print_now_result(), testing::Optional(true));
// Cleanup before test shutdown.
PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id);
@@ -1903,7 +2063,7 @@ IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
- SystemPrintFromPrintPreviewConcurrent) {
+ SystemPrintFromPrintPreviewConcurrentAllowed) {
AddPrinter("printer1");
SetPrinterNameForSubsequentContexts("printer1");
@@ -1924,39 +2084,99 @@ IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
ASSERT_TRUE(client_id.has_value());
// Now do a print preview which will try to switch to doing system print.
-#if BUILDFLAG(IS_LINUX)
// The expected events for this are:
- // 1. Start printing.
- // 2. The document is rendered.
- // 3. Receive document done notification.
- // 4. Wait for the one print job to be destroyed, to ensure printing
+ // 1. Gets default settings.
+ // 2. Asks user for settings.
+ // 3. Start the print job.
+ // 4. Rendering for 1 page of document of content.
+ // 5. Completes with document done.
+ // 6. Wait until all processing for DidPrintDocument is known to have
+ // completed, to ensure printing finished cleanly before completing the
+ // test.
+ // 7. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
- SetNumExpectedMessages(/*num=*/4);
+ SetNumExpectedMessages(/*num=*/7);
- constexpr bool kWaitForCallback = true;
-#else
+ SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true);
+
+ // Concurrent system print is allowed.
+ EXPECT_THAT(system_print_registration_succeeded(), testing::Optional(true));
+
+ // Cleanup before test shutdown.
+ PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id);
+}
+#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
+
+#else // BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS)
+
+IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
+ StartBasicPrintConcurrentNotAllowed) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ TestPrintViewManager* print_view_manager =
+ SetUpAndReturnPrintViewManager(web_contents);
+
+ // Pretend that a window has started a system print.
+ absl::optional<PrintBackendServiceManager::ClientId> client_id =
+ PrintBackendServiceManager::GetInstance().RegisterQueryWithUiClient();
+ ASSERT_TRUE(client_id.has_value());
+
+ // Now initiate a system print that would exist concurrently with that.
+ StartBasicPrint(web_contents);
+
+ // Concurrent system print is not allowed.
+ EXPECT_THAT(print_view_manager->print_now_result(), testing::Optional(false));
+ // The denied concurrent print is silent without an error.
+ EXPECT_EQ(error_dialog_shown_count(), 0u);
+
+ // Cleanup before test shutdown.
+ PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id);
+}
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+IN_PROC_BROWSER_TEST_F(SystemAccessProcessSandboxedServicePrintBrowserTest,
+ SystemPrintFromPrintPreviewConcurrentNotAllowed) {
+ AddPrinter("printer1");
+ SetPrinterNameForSubsequentContexts("printer1");
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ SetUpPrintViewManager(web_contents);
+
+ // Pretend that another tab has started a system print.
+ // TODO(crbug.com/809738) Improve on this test by using a persistent fake
+ // system print dialog.
+ absl::optional<PrintBackendServiceManager::ClientId> client_id =
+ PrintBackendServiceManager::GetInstance().RegisterQueryWithUiClient();
+ ASSERT_TRUE(client_id.has_value());
+
+ // Now do a print preview which will try to switch to doing system print.
// Inability to support this should be detected immediately without needing
// to wait for callback.
- constexpr bool kWaitForCallback = false;
-#endif
-
- SystemPrintFromPreviewOnceReadyAndLoaded(kWaitForCallback);
+ SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/false);
- // With the exception of Linux, concurrent system print is not allowed.
- ASSERT_TRUE(system_print_registration_succeeded().has_value());
-#if BUILDFLAG(IS_LINUX)
- EXPECT_TRUE(*system_print_registration_succeeded());
-#else
+ // Concurrent system print is not allowed.
+ EXPECT_THAT(system_print_registration_succeeded(), testing::Optional(false));
// The denied concurrent print is silent without an error.
- EXPECT_FALSE(*system_print_registration_succeeded());
EXPECT_EQ(error_dialog_shown_count(), 0u);
-#endif
// Cleanup before test shutdown.
PrintBackendServiceManager::GetInstance().UnregisterClient(*client_id);
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
+#endif // BUILDFLAG(ENABLE_CONCURRENT_BASIC_PRINT_DIALOGS)
+
IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
StartBasicPrintUseDefaultFails) {
PrimeForFailInUseDefaultSettings();
@@ -1970,19 +2190,11 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
ASSERT_TRUE(web_contents);
SetUpPrintViewManager(web_contents);
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
// The expected events for this are:
// 1. Get the default settings, which fails.
// 2. The print error dialog is shown.
// No print job is created from such an early failure.
SetNumExpectedMessages(/*num=*/2);
-#else
- // When get default settings is invoked from the browser process, there is no
- // override to observe this failure. This means the expected events are:
- // 1. The print error dialog is shown.
- // No print job is created from such an early failure.
- SetNumExpectedMessages(/*num=*/1);
-#endif
StartBasicPrint(web_contents);
@@ -1990,6 +2202,8 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
EXPECT_EQ(use_default_settings_result(), mojom::ResultCode::kFailed);
+#else
+ EXPECT_TRUE(did_use_default_settings());
#endif
EXPECT_EQ(error_dialog_shown_count(), 1u);
EXPECT_EQ(did_print_document_count(), 0);
@@ -1997,7 +2211,49 @@ IN_PROC_BROWSER_TEST_P(SystemAccessProcessServicePrintBrowserTest,
}
#endif // BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
-#endif // BUILDFLAG(ENABLE_OOP_PRINTING)
+#if BUILDFLAG(IS_MAC)
+IN_PROC_BROWSER_TEST_P(SystemAccessProcessPrintBrowserTest, OpenPdfInPreview) {
+ AddPrinter("printer1");
+ SetPrinterNameForSubsequentContexts("printer1");
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ SetUpPrintViewManager(web_contents);
+
+ if (GetParam() == PrintBackendFeatureVariation::kInBrowserProcess) {
+ // The expected events for this are:
+ // 1. Wait for the one print job to be destroyed, to ensure printing
+ // finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/1);
+ } else {
+ // The expected events for this are:
+ // 1. Update printer settings.
+ // 2. A print job is started.
+ // 3. Rendering for 1 page of document of content.
+ // 4. Completes with document done.
+ // 5. Wait for the one print job to be destroyed, to ensure printing
+ // finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/5);
+ }
+ OpenPdfInPreviewOnceReadyAndLoaded();
+
+ if (GetParam() != PrintBackendFeatureVariation::kInBrowserProcess) {
+ EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
+ EXPECT_EQ(render_printed_document_result(), mojom::ResultCode::kSuccess);
+ EXPECT_EQ(document_done_result(), mojom::ResultCode::kSuccess);
+ }
+ EXPECT_TRUE(destination_is_preview());
+ EXPECT_EQ(error_dialog_shown_count(), 0u);
+ EXPECT_EQ(print_job_destruction_count(), 1);
+}
+#endif // BUILDFLAG(IS_MAC)
+
+#endif // BUILDFLAG(ENABLE_OOP_PRINTING)
#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
class TestPrintViewManagerForContentAnalysis : public TestPrintViewManager {
@@ -2010,38 +2266,37 @@ class TestPrintViewManagerForContentAnalysis : public TestPrintViewManager {
void OnScriptedPrint() override { scripted_print_called_ = true; }
+ void OnPrintPreviewDone() override {
+ if (on_print_preview_done_) {
+ std::move(on_print_preview_done_).Run();
+ }
+ }
+
bool print_now_called() const { return print_now_called_; }
bool scripted_print_called() const { return scripted_print_called_; }
+ void set_on_print_preview_done_closure(base::OnceClosure closure) {
+ on_print_preview_done_ = std::move(closure);
+ }
+
private:
bool print_now_called_ = false;
bool scripted_print_called_ = false;
+ base::OnceClosure on_print_preview_done_;
};
- static TestPrintViewManagerForContentAnalysis* CreateForWebContents(
- content::WebContents* web_contents) {
- auto manager =
- std::make_unique<TestPrintViewManagerForContentAnalysis>(web_contents);
- auto* manager_ptr = manager.get();
- web_contents->SetUserData(PrintViewManager::UserDataKey(),
- std::move(manager));
- return manager_ptr;
- }
-
- explicit TestPrintViewManagerForContentAnalysis(
- content::WebContents* web_contents)
- : TestPrintViewManagerForContentAnalysis(
- web_contents,
- /*create_print_job_callback=*/base::DoNothing(),
- /*composite_for_content_analysis_callback=*/base::DoNothing()) {}
-
TestPrintViewManagerForContentAnalysis(
content::WebContents* web_contents,
+ const char* policy_value,
+ absl::optional<enterprise_connectors::ContentAnalysisRequest::Reason>
+ expected_reason,
OnDidCreatePrintJobCallback create_print_job_callback,
OnDidCompositeForContentAnalysis composite_for_content_analysis_callback)
: TestPrintViewManager(web_contents,
std::move(create_print_job_callback)),
+ expected_reason_(expected_reason),
+ policy_value_(policy_value),
did_composite_for_content_analysis_callback_(
std::move(composite_for_content_analysis_callback)) {
AddTestObserver(observer_);
@@ -2070,6 +2325,10 @@ class TestPrintViewManagerForContentAnalysis : public TestPrintViewManager {
void set_allowed_by_dlp(bool allowed) { allowed_by_dlp_ = allowed; }
#endif // BUILDFLAG(IS_CHROMEOS)
+ void set_on_print_preview_done_closure(base::OnceClosure closure) {
+ observer_.set_on_print_preview_done_closure(std::move(closure));
+ }
+
protected:
void OnGotSnapshotCallback(
base::OnceCallback<void(bool should_proceed)> callback,
@@ -2081,6 +2340,11 @@ class TestPrintViewManagerForContentAnalysis : public TestPrintViewManager {
EXPECT_TRUE(params->content->metafile_data_region.IsValid());
EXPECT_EQ(data.url,
web_contents()->GetOutermostWebContents()->GetLastCommittedURL());
+ // TODO(http://b/285243428): Change `expected_reason_` to a normal enum
+ // value instead of an optional to check it in every test.
+ if (expected_reason_) {
+ EXPECT_EQ(data.reason, *expected_reason_);
+ }
PrintViewManager::OnGotSnapshotCallback(
std::move(callback), std::move(data), rfh_id, std::move(params));
@@ -2100,13 +2364,22 @@ class TestPrintViewManagerForContentAnalysis : public TestPrintViewManager {
// print Connector policy.
EXPECT_EQ(data.settings.tags.size(), 1u);
EXPECT_TRUE(base::Contains(data.settings.tags, "dlp"));
- EXPECT_TRUE(data.settings.cloud_or_local_settings.is_cloud_analysis());
- EXPECT_EQ(data.settings.cloud_or_local_settings.dm_token(), kFakeDmToken);
- EXPECT_EQ(data.settings.block_until_verdict,
- enterprise_connectors::BlockUntilVerdict::kBlock);
+ if (data.settings.cloud_or_local_settings.is_cloud_analysis()) {
+ EXPECT_EQ(data.settings.cloud_or_local_settings.dm_token(), kFakeDmToken);
+ } else {
+ EXPECT_EQ(data.settings.cloud_or_local_settings.local_path(),
+ "path_user");
+ EXPECT_TRUE(data.settings.cloud_or_local_settings.user_specific());
+ }
+ EXPECT_TRUE(ExpectedBlockUntilVerdict(data.settings.block_until_verdict));
EXPECT_TRUE(data.settings.block_large_files);
EXPECT_EQ(data.url,
web_contents()->GetOutermostWebContents()->GetLastCommittedURL());
+ // TODO(http://b/285243428): Change `expected_reason_` to a normal enum
+ // value instead of an optional to check it in every test.
+ if (expected_reason_) {
+ EXPECT_EQ(data.reason, *expected_reason_);
+ }
// The snapshot should be valid and populated.
EXPECT_TRUE(LooksLikePdf(page_region.Map().GetMemoryAsSpan<char>()));
@@ -2124,6 +2397,45 @@ class TestPrintViewManagerForContentAnalysis : public TestPrintViewManager {
std::move(data), rfh_id, status, std::move(page_region));
}
+ void ContentAnalysisBeforePrintingDocument(
+ enterprise_connectors::ContentAnalysisDelegate::Data scanning_data,
+ scoped_refptr<base::RefCountedMemory> print_data,
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const gfx::Point& offsets) override {
+ // The settings passed to this function should match the content of the
+ // print Connector policy.
+ EXPECT_EQ(scanning_data.settings.tags.size(), 1u);
+ EXPECT_TRUE(base::Contains(scanning_data.settings.tags, "dlp"));
+ if (scanning_data.settings.cloud_or_local_settings.is_cloud_analysis()) {
+ EXPECT_EQ(scanning_data.settings.cloud_or_local_settings.dm_token(),
+ kFakeDmToken);
+ } else {
+ EXPECT_EQ(scanning_data.settings.cloud_or_local_settings.local_path(),
+ "path_user");
+ EXPECT_TRUE(
+ scanning_data.settings.cloud_or_local_settings.user_specific());
+ }
+ EXPECT_TRUE(
+ ExpectedBlockUntilVerdict(scanning_data.settings.block_until_verdict));
+ EXPECT_TRUE(scanning_data.settings.block_large_files);
+ EXPECT_EQ(scanning_data.url,
+ web_contents()->GetOutermostWebContents()->GetLastCommittedURL());
+ // TODO(http://b/285243428): Change `expected_reason_` to a normal enum
+ // value instead of an optional to check it in every test.
+ if (expected_reason_) {
+ EXPECT_EQ(scanning_data.reason, *expected_reason_);
+ }
+
+ // The data of the document should be a valid PDF as this code should be
+ // called as the print job is about to start printing.
+ EXPECT_TRUE(LooksLikePdf(base::span<const char>(
+ print_data->front_as<const char>(), print_data->size())));
+
+ TestPrintViewManager::ContentAnalysisBeforePrintingDocument(
+ std::move(scanning_data), print_data, page_size, content_area, offsets);
+ }
+
#if BUILDFLAG(IS_CHROMEOS)
void OnDlpPrintingRestrictionsChecked(
content::GlobalRenderFrameHostId rfh_id,
@@ -2156,14 +2468,36 @@ class TestPrintViewManagerForContentAnalysis : public TestPrintViewManager {
preview_run_loop_.Quit();
}
+ bool ExpectedBlockUntilVerdict(
+ enterprise_connectors::BlockUntilVerdict block_until_verdict) {
+ if (policy_value_ == kCloudAnalysisNonBlockingPolicy) {
+ return block_until_verdict ==
+ enterprise_connectors::BlockUntilVerdict::kNoBlock;
+ }
+
+ return block_until_verdict ==
+ enterprise_connectors::BlockUntilVerdict::kBlock;
+ }
+
#if BUILDFLAG(IS_CHROMEOS)
bool allowed_by_dlp_ = true;
#endif // BUILDFLAG(IS_CHROMEOS)
// Indicates whether the preview was allowed after checking against content
- // analysis and DLP (if on CrOS). This is unpopulated until then.
+ // analysis and DLP (if on CrOS). This is `absl::nullopt` until then.
absl::optional<bool> preview_allowed_;
+ // Used to validate the corresponding `ContentAnalysisDelegate::Data` passed
+ // in various content analysis-related functions. A value of `absl::nullopt`
+ // means the value shouldn't be checked.
+ absl::optional<enterprise_connectors::ContentAnalysisRequest::Reason>
+ expected_reason_;
+
+ // Used to validate the corresponding `ContentAnalysisDelegate::Data` passed
+ // in various content analysis-related functions. Corresponds to the value
+ // return by `PolicyValue()` for the current test.
+ const char* policy_value_ = nullptr;
+
base::RunLoop preview_run_loop_;
OnDidCompositeForContentAnalysis did_composite_for_content_analysis_callback_;
Observer observer_;
@@ -2172,16 +2506,10 @@ class TestPrintViewManagerForContentAnalysis : public TestPrintViewManager {
int got_snapshot_count_ = 0;
};
-struct ContentAnalysisTestCase {
- bool content_analysis_allows_print = false;
- bool oop_enabled = false;
-};
-
-class ContentAnalysisPrintBrowserTest
- : public SystemAccessProcessPrintBrowserTestBase,
- public testing::WithParamInterface<ContentAnalysisTestCase> {
+class ContentAnalysisPrintBrowserTestBase
+ : public SystemAccessProcessPrintBrowserTestBase {
public:
- ContentAnalysisPrintBrowserTest() {
+ ContentAnalysisPrintBrowserTestBase() {
policy::SetDMTokenForTesting(
policy::DMToken::CreateValidToken(kFakeDmToken));
enterprise_connectors::ContentAnalysisDelegate::SetFactoryForTesting(
@@ -2189,11 +2517,38 @@ class ContentAnalysisPrintBrowserTest
&enterprise_connectors::test::FakeContentAnalysisDelegate::Create,
base::DoNothing(),
base::BindRepeating(
- &ContentAnalysisPrintBrowserTest::ScanningResponse,
+ &ContentAnalysisPrintBrowserTestBase::ScanningResponse,
base::Unretained(this)),
kFakeDmToken));
+
+ // These overrides make the overall tests faster as the content analysis
+ // dialog won't stay in each state for mandatory minimum times.
+ enterprise_connectors::ContentAnalysisDialog::
+ SetMinimumPendingDialogTimeForTesting(base::Milliseconds(0));
enterprise_connectors::ContentAnalysisDialog::SetShowDialogDelayForTesting(
base::Milliseconds(0));
+ enterprise_connectors::ContentAnalysisDialog::
+ SetSuccessDialogTimeoutForTesting(base::Milliseconds(0));
+ }
+
+ enterprise_connectors::ContentAnalysisResponse ScanningResponse(
+ const std::string& contents,
+ const base::FilePath& path) {
+ ++scanning_responses_;
+ enterprise_connectors::ContentAnalysisResponse response;
+
+ auto* result = response.add_results();
+ result->set_tag("dlp");
+ result->set_status(
+ enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS);
+
+ if (!ContentAnalysisAllowsPrint()) {
+ auto* rule = result->add_triggered_rules();
+ rule->set_rule_name("blocking_rule_name");
+ rule->set_action(enterprise_connectors::TriggeredRule::BLOCK);
+ }
+
+ return response;
}
void SetUp() override {
@@ -2203,27 +2558,30 @@ class ContentAnalysisPrintBrowserTest
}
void SetUpOnMainThread() override {
+ SystemAccessProcessPrintBrowserTestBase::SetUpOnMainThread();
enterprise_connectors::test::SetAnalysisConnector(
browser()->profile()->GetPrefs(),
- enterprise_connectors::AnalysisConnector::PRINT,
- R"({
- "service_provider": "google",
- "enable": [ {"url_list": ["*"], "tags": ["dlp"]} ],
- "block_until_verdict": 1,
- "block_large_files": true
- })");
- SystemAccessProcessPrintBrowserTestBase::SetUpOnMainThread();
+ enterprise_connectors::AnalysisConnector::PRINT, PolicyValue());
+ }
+
+ void TearDownOnMainThread() override {
+ enterprise_connectors::test::ClearAnalysisConnector(
+ browser()->profile()->GetPrefs(),
+ enterprise_connectors::AnalysisConnector::PRINT);
+ SystemAccessProcessPrintBrowserTestBase::TearDownOnMainThread();
}
TestPrintViewManagerForContentAnalysis*
SetUpAndReturnPrintViewManagerForContentAnalysis(
- content::WebContents* web_contents) {
+ content::WebContents* web_contents,
+ absl::optional<enterprise_connectors::ContentAnalysisRequest::Reason>
+ expected_reason) {
// Safe to use `base::Unretained(this)` since this testing class
// necessarily must outlive all interactions from the tests which will
// run through `PrintViewManagerBase`, which is what causes new jobs to
// be created and use this callback.
auto manager = std::make_unique<TestPrintViewManagerForContentAnalysis>(
- web_contents,
+ web_contents, PolicyValue(), expected_reason,
base::BindRepeating(
&SystemAccessProcessPrintBrowserTestBase::OnCreatedPrintJob,
base::Unretained(this)),
@@ -2237,43 +2595,107 @@ class ContentAnalysisPrintBrowserTest
return manager_ptr;
}
- bool content_analysis_allows_print() const {
- return GetParam().content_analysis_allows_print;
- }
- bool UseService() override { return GetParam().oop_enabled; }
+ int scanning_responses_count() { return scanning_responses_; }
+
bool SandboxService() override { return true; }
- enterprise_connectors::ContentAnalysisResponse ScanningResponse(
- const std::string& contents,
- const base::FilePath& path) {
- enterprise_connectors::ContentAnalysisResponse response;
+ bool EnableContentAnalysisAfterDialog() override { return false; }
- auto* result = response.add_results();
- result->set_tag("dlp");
- result->set_status(
- enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS);
+ int GetExpectedNewDocumentCalledCount() {
+ return PrintAllowedOrNonBlockingPolicy() ? (UseService() ? 2 : 1) : 0;
+ }
- if (!content_analysis_allows_print()) {
- auto* rule = result->add_triggered_rules();
- rule->set_rule_name("blocking_rule_name");
- rule->set_action(enterprise_connectors::TriggeredRule::BLOCK);
- }
+ // The value OnPrintEnterpriseConnector should be set to.
+ virtual const char* PolicyValue() const = 0;
- return response;
+ // Whether content analysis should let printing proceed.
+ virtual bool ContentAnalysisAllowsPrint() const = 0;
+
+ // Helper to check if printing is allowed altogether or not. It's possible for
+ // the policy to be set to be non-blocking and still obtain a "block" verdict
+ // from a content analysis server, and in such a case the printing will be
+ // allowed to proceed.
+ bool PrintAllowedOrNonBlockingPolicy() {
+ return ContentAnalysisAllowsPrint() ||
+ PolicyValue() == kCloudAnalysisNonBlockingPolicy;
}
- int new_document_called_count() {
- return test_printing_context_factory()->new_document_called_count();
+ private:
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+ enterprise_connectors::FakeContentAnalysisSdkManager sdk_manager_;
+#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+
+ // Counts the number of times `ScanningResponse` is called, why is equivalent
+ // to the number of times a printed page's bytes would reach a scanner.
+ int scanning_responses_ = 0;
+};
+
+using ContentAnalysisBeforePrintPreviewVariation =
+ testing::tuple<const char* /*policy_value*/,
+ bool /*content_analysis_allows_print*/,
+ bool /*oop_enabled*/>;
+
+class ContentAnalysisBeforePrintPreviewBrowserTest
+ : public ContentAnalysisPrintBrowserTestBase,
+ public testing::WithParamInterface<
+ ContentAnalysisBeforePrintPreviewVariation> {
+ public:
+ bool EnableContentAnalysisAfterDialog() override { return false; }
+ const char* PolicyValue() const override { return std::get<0>(GetParam()); }
+ bool ContentAnalysisAllowsPrint() const override {
+ return std::get<1>(GetParam());
}
+ bool UseService() override { return std::get<2>(GetParam()); }
};
-class ContentAnalysisScriptedPreviewlessPrintBrowserTest
- : public ContentAnalysisPrintBrowserTest {
+using ContentAnalysisAfterPrintPreviewVariation =
+ testing::tuple<const char* /*policy_value*/,
+ bool /*content_analysis_allows_print*/,
+ bool /*oop_enabled*/>;
+
+class ContentAnalysisAfterPrintPreviewBrowserTest
+ : public ContentAnalysisPrintBrowserTestBase,
+ public testing::WithParamInterface<
+ ContentAnalysisAfterPrintPreviewVariation> {
public:
+ bool EnableContentAnalysisAfterDialog() override { return true; }
+
+ const char* PolicyValue() const override { return std::get<0>(GetParam()); }
+ bool ContentAnalysisAllowsPrint() const override {
+ return std::get<1>(GetParam());
+ }
+ bool UseService() override { return std::get<2>(GetParam()); }
+
+ // PrintJob::Observer:
+ void OnCanceling() override { CheckForQuit(); }
+};
+
+using ContentAnalysisScriptedPreviewlessVariation =
+ testing::tuple<const char* /*policy_value*/,
+ bool /*content_analysis_allows_print*/,
+ bool /*oop_enabled*/>;
+
+class ContentAnalysisScriptedPreviewlessPrintBrowserTestBase
+ : public ContentAnalysisPrintBrowserTestBase,
+ public testing::WithParamInterface<
+ ContentAnalysisScriptedPreviewlessVariation> {
+ public:
+ const char* PolicyValue() const override { return std::get<0>(GetParam()); }
+ bool ContentAnalysisAllowsPrint() const override {
+ return std::get<1>(GetParam());
+ }
+ bool UseService() override { return std::get<2>(GetParam()); }
+
void SetUpCommandLine(base::CommandLine* cmd_line) override {
cmd_line->AppendSwitch(switches::kDisablePrintPreview);
- ContentAnalysisPrintBrowserTest::SetUpCommandLine(cmd_line);
+ ContentAnalysisPrintBrowserTestBase::SetUpCommandLine(cmd_line);
}
+};
+
+class ContentAnalysisScriptedPreviewlessPrintBeforeDialogBrowserTest
+ : public ContentAnalysisScriptedPreviewlessPrintBrowserTestBase {
+ public:
+ bool EnableContentAnalysisAfterDialog() override { return false; }
void RunScriptedPrintTest(const std::string& script) {
AddPrinter("printer_name");
@@ -2285,12 +2707,12 @@ class ContentAnalysisScriptedPreviewlessPrintBrowserTest
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
- auto* print_view_manager =
- SetUpAndReturnPrintViewManagerForContentAnalysis(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::SYSTEM_DIALOG_PRINT);
- if (content_analysis_allows_print()) {
+ if (PrintAllowedOrNonBlockingPolicy()) {
if (UseService()) {
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
// The expected events are:
// 1. The document is composited for content analysis.
// 2. The print job used for scanning is destroyed.
@@ -2305,22 +2727,6 @@ class ContentAnalysisScriptedPreviewlessPrintBrowserTest
// 9. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/9);
-#else
- // The expected events are:
- // 1. The document is composited for content analysis.
- // 2. The print job used for scanning is destroyed.
- // 3. Getting the default settings and asking user for settings are
- // done in-browser, where there is no override to notice the events.
- // A print job is then started.
- // 4. The one page of the document is rendered.
- // 5. Receive document done notification.
- // 6. Wait until all processing for DidPrintDocument is known to have
- // completed, to ensure printing finished cleanly before completing
- // the test.
- // 7. Wait for the one print job to be destroyed, to ensure printing
- // finished cleanly before completing the test.
- SetNumExpectedMessages(/*num=*/7);
-#endif
} else {
// The expected events for this are:
// 1. The document is composited for content analysis.
@@ -2335,26 +2741,11 @@ class ContentAnalysisScriptedPreviewlessPrintBrowserTest
SetNumExpectedMessages(/*num=*/6);
}
} else {
-#if BUILDFLAG(IS_WIN)
// The expected events for this are:
// 1. Use default settings.
// 2. The document is composited for content analysis.
// 3. The print job used for scanning is destroyed.
SetNumExpectedMessages(/*num=*/3);
-#else
- if (UseService()) {
- // The expected events for this are:
- // 1. The document is composited for content analysis.
- // 2. The print job used for scanning is destroyed.
- SetNumExpectedMessages(/*num=*/2);
- } else {
- // The expected events for this are:
- // 1. Use default settings.
- // 2. The document is composited for content analysis.
- // 3. The print job used for scanning is destroyed.
- SetNumExpectedMessages(/*num=*/3);
- }
-#endif
if (UseService()) {
// When printing is denied, the printing context in the Print Backend
@@ -2370,26 +2761,101 @@ class ContentAnalysisScriptedPreviewlessPrintBrowserTest
WaitUntilCallbackReceived();
ASSERT_EQ(print_view_manager->scripted_print_called(),
- content_analysis_allows_print());
+ PrintAllowedOrNonBlockingPolicy());
EXPECT_EQ(composited_for_content_analysis_count(), 1);
+ EXPECT_EQ(scanning_responses_count(), 1);
+
+ // Validate that `NewDocument()` is only called for actual printing, not as
+ // part of content analysis, since that can needlessly prompt the user.
+ // When printing OOP, an extra call for a new document will occur since it
+ // gets called in both the browser process and in the Print Backend service.
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
+ }
+};
+
+class ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest
+ : public ContentAnalysisScriptedPreviewlessPrintBrowserTestBase {
+ public:
+ bool EnableContentAnalysisAfterDialog() override { return true; }
+
+ void RunScriptedPrintTest(const std::string& script) {
+ AddPrinter("printer_name");
+
+ if (UseService() && !PrintAllowedOrNonBlockingPolicy()) {
+ // This results in a stranded context left in the Print Backend service.
+ // It will persist harmlessly until the service terminates after a short
+ // period of no printing activity.
+ SkipPersistentContextsCheckOnShutdown();
+ }
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test1.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::SYSTEM_DIALOG_PRINT);
+
+ if (PrintAllowedOrNonBlockingPolicy()) {
+ if (UseService()) {
+ // The expected events are:
+ // 1. Get the default settings.
+ // 2. Ask the user for settings.
+ // 3. A print job is started.
+ // 4. The one page of the document is rendered.
+ // 5. Receive document done notification.
+ // 6. Wait until all processing for DidPrintDocument is known to have
+ // completed, to ensure printing finished cleanly before
+ // completing the test.
+ // 7. Wait for the one print job to be destroyed, to ensure printing
+ // finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/7);
+ } else {
+ // The expected events for this are:
+ // 1. Use default settings.
+ // 2. Ask the user for settings.
+ // 3. The print compositor will complete generating the document.
+ // 4. The print job is destroyed.
+ SetNumExpectedMessages(/*num=*/4);
+ }
+ } else {
+ // The expected events for this are:
+ // 1. Use default settings.
+ // 2. Ask the user for settings.
+ // 3. Wait until all processing for DidPrintDocument is known to have
+ // completed, to ensure printing finished cleanly before
+ // completing the test.
+ // 4. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/4);
+ }
- // Validate that `NewDocument` is only called for actual printing, not as
+ content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(), script);
+
+ WaitUntilCallbackReceived();
+
+ ASSERT_TRUE(print_view_manager->scripted_print_called());
+ EXPECT_EQ(composited_for_content_analysis_count(), 0);
+ EXPECT_EQ(scanning_responses_count(), 1);
+
+ // Validate that `NewDocument()` is only called for actual printing, not as
// part of content analysis, since that can needlessly prompt the user.
// When printing OOP, an extra call for a new document will occur since it
// gets called in both the browser process and in the Print Backend service.
- EXPECT_EQ(new_document_called_count(),
- content_analysis_allows_print() ? (UseService() ? 2 : 1) : 0);
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
}
};
#if !BUILDFLAG(IS_CHROMEOS)
-IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest, PrintNow) {
+IN_PROC_BROWSER_TEST_P(ContentAnalysisBeforePrintPreviewBrowserTest,
+ PrintWithPreview) {
AddPrinter("printer_name");
- if (UseService() && !content_analysis_allows_print()) {
- // This results in a stranded context left in the Print Backend service.
- // It will persist harmlessly until the service terminates after a short
- // period of no printing activity.
+ if (UseService()) {
+ // Test does not do extra cleanup beyond the check for analysis permission.
SkipPersistentContextsCheckOnShutdown();
}
@@ -2400,98 +2866,60 @@ IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest, PrintNow) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
- auto* print_view_manager =
- SetUpAndReturnPrintViewManagerForContentAnalysis(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT);
- if (content_analysis_allows_print()) {
+ if (PrintAllowedOrNonBlockingPolicy()) {
if (UseService()) {
-#if BUILDFLAG(ENABLE_OOP_BASIC_PRINT_DIALOG)
- // The expected events after having successfully passed the scan are:
- // 1. The document is composited for content analysis.
- // 2. The print job used for scanning is destroyed.
- // 3. Get the default settings.
- // 4. Ask the user for settings.
- // 5. A print job is started.
- // 6. The print compositor will complete generating the document.
- // 7. The one page of the document is rendered.
- // 8. Receive document done notification.
- // 9. Wait for the one print job to be destroyed, to ensure printing
- // finished cleanly before completing the test.
- SetNumExpectedMessages(/*num=*/9);
-#else
- // The expected events after having successfully passed the scan are:
+ // The expected events for this are:
// 1. The document is composited for content analysis.
// 2. The print job used for scanning is destroyed.
- // 3. Getting the default settings and asking user for settings are done
- // in-browser, where there is no override to notice the events. A
- // print job is then started.
- // 4. The print compositor will complete generating the document.
- // 5. The one page of the document is rendered.
- // 6. Receive document done notification.
+ // 3. Update print settings.
+ // 4. A print job is started.
+ // 5. Rendering for 1 page of document of content.
+ // 6. Completes with document done.
// 7. Wait for the one print job to be destroyed, to ensure printing
// finished cleanly before completing the test.
SetNumExpectedMessages(/*num=*/7);
-#endif
} else {
// The expected events for this are:
// 1. The document is composited for content analysis.
// 2. The print job used for scanning is destroyed.
- // 3. Get the default settings.
- // 4. Ask the user for settings.
- // 5. The print compositor will complete generating the document.
- // 6. Wait for the actual printing job to be destroyed, to ensure
+ // 3. Wait for the actual printing job to be destroyed, to ensure
// printing finished cleanly before completing the test.
- SetNumExpectedMessages(/*num=*/6);
+ SetNumExpectedMessages(/*num=*/3);
}
+ PrintAfterPreviewIsReadyAndLoaded();
} else {
-#if BUILDFLAG(IS_WIN)
// The expected events for this are:
- // 1. Get the default settings.
- // 2. The document is composited for content analysis.
- // 3. The print job used for scanning is destroyed.
- SetNumExpectedMessages(/*num=*/3);
-#else
- if (UseService()) {
- // The expected events for this are:
- // 1. The document is composited for content analysis.
- // 2. The print job used for scanning is destroyed.
- SetNumExpectedMessages(/*num=*/2);
- } else {
- // The expected events for this are:
- // 1. Get the default settings.
- // 2. The document is composited for content analysis.
- // 3. The print job used for scanning is destroyed.
- SetNumExpectedMessages(/*num=*/3);
- }
-#endif
+ // 1. The document is composited for content analysis.
+ // 2. The print job used for scanning is destroyed.
+ SetNumExpectedMessages(/*num=*/2);
+ test::StartPrint(web_contents);
+ WaitUntilCallbackReceived();
}
- StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- /*print_renderer=*/mojo::NullAssociatedRemote(),
-#endif
- /*print_preview_disabled=*/true,
- /*has_selection=*/false);
-
- WaitUntilCallbackReceived();
-
- // PrintNow uses the same code path as scripted prints to scan printed pages,
- // so print_now_called() should always happen and scripted_print_called()
- // should be called with the same result that is expected from scanning.
- EXPECT_TRUE(print_view_manager->print_now_called());
- EXPECT_EQ(print_view_manager->scripted_print_called(),
- content_analysis_allows_print());
+ ASSERT_EQ(print_view_manager->preview_allowed(),
+ PrintAllowedOrNonBlockingPolicy());
EXPECT_EQ(composited_for_content_analysis_count(), 1);
-
- // Validate that `NewDocument` is only called for actual printing, not as
+ EXPECT_EQ(print_view_manager->got_snapshot_count(), 1);
+ EXPECT_EQ(scanning_responses_count(), 1);
+ // Validate that `NewDocument()` is only called for actual printing, not as
// part of content analysis, since that can needlessly prompt the user.
// When printing OOP, an extra call for a new document will occur since it
// gets called in both the browser process and in the Print Backend service.
- EXPECT_EQ(new_document_called_count(),
- content_analysis_allows_print() ? (UseService() ? 2 : 1) : 0);
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
}
-IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest, PrintWithPreview) {
+IN_PROC_BROWSER_TEST_P(ContentAnalysisBeforePrintPreviewBrowserTest,
+ WindowDotPrint) {
+ if (UseService()) {
+ // TODO(crbug.com/1464566): Enable this test variant once an extra system
+ // dialog is not being displayed before analysis completes.
+ GTEST_SKIP();
+ }
+
AddPrinter("printer_name");
if (UseService()) {
@@ -2506,27 +2934,56 @@ IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest, PrintWithPreview) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
- auto* print_view_manager =
- SetUpAndReturnPrintViewManagerForContentAnalysis(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT);
- // The expected events for this are:
- // 1. The document is composited for content analysis.
- // 2. The print job used for scanning is destroyed.
- SetNumExpectedMessages(/*num=*/2);
-
- test::StartPrint(web_contents);
- WaitUntilCallbackReceived();
+ if (PrintAllowedOrNonBlockingPolicy()) {
+ if (UseService()) {
+ // The expected events for this are:
+ // 1. The document is composited for content analysis.
+ // 2. The print job used for scanning is destroyed.
+ // 3. Update print settings.
+ // 4. A print job is started.
+ // 5. Rendering for 1 page of document of content.
+ // 6. Completes with document done.
+ // 7. Wait for the one print job to be destroyed, to ensure printing
+ // finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/7);
+ } else {
+ // The expected events for this are:
+ // 1. The document is composited for content analysis.
+ // 2. The print job used for scanning is destroyed.
+ // 3. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/3);
+ }
+ const PrintParams kParams{.invoke_method =
+ InvokePrintMethod::kWindowDotPrint};
+ PrintAfterPreviewIsReadyAndLoaded(kParams);
+ } else {
+ // The expected events for this are:
+ // 1. The document is composited for content analysis.
+ // 2. The print job used for scanning is destroyed.
+ SetNumExpectedMessages(/*num=*/2);
+ content::ExecuteScriptAsync(web_contents->GetPrimaryMainFrame(),
+ "window.print();");
+ WaitUntilCallbackReceived();
+ }
ASSERT_EQ(print_view_manager->preview_allowed(),
- content_analysis_allows_print());
+ PrintAllowedOrNonBlockingPolicy());
EXPECT_EQ(composited_for_content_analysis_count(), 1);
-
- // Validate that `NewDocument` was never call as that can needlessly
- // prompt the user.
- ASSERT_EQ(new_document_called_count(), 0);
+ EXPECT_EQ(print_view_manager->got_snapshot_count(), 1);
+ EXPECT_EQ(scanning_responses_count(), 1);
+ // Validate that `NewDocument()` is only called for actual printing, not as
+ // part of content analysis, since that can needlessly prompt the user.
+ // When printing OOP, an extra call for a new document will occur since it
+ // gets called in both the browser process and in the Print Backend service.
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
}
-IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest,
+IN_PROC_BROWSER_TEST_P(ContentAnalysisBeforePrintPreviewBrowserTest,
SystemPrintFromPrintPreview) {
AddPrinter("printer_name");
@@ -2537,15 +2994,26 @@ IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest,
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
- auto* print_view_manager =
- SetUpAndReturnPrintViewManagerForContentAnalysis(web_contents);
+
+#if BUILDFLAG(IS_WIN)
+ // `PRINT_PREVIEW_PRINT` is expected here since scanning takes place before
+ // the print preview dialog where the system dialog print is selected.
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT);
+#else
+ // TODO(http://b/285243428): Update expectation once a second analysis scan
+ // isn't done for system print from Print Preview.
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents, absl::nullopt);
+#endif
// Since the content analysis scan happens before the Print Preview dialog,
// checking behavior when requesting the system print dialog from print
// preview only is possible if the scan permits it.
// TODO(http://b/266119859): Update test behavior and expectations for when
// scans are done after hitting Print from Print Preview.
- if (content_analysis_allows_print()) {
+ if (PrintAllowedOrNonBlockingPolicy()) {
if (UseService()) {
#if BUILDFLAG(IS_WIN)
// The expected events for this are:
@@ -2564,15 +3032,17 @@ IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest,
// The expected events for this are:
// 1. The document is composited for content analysis.
// 2. The print job used for scanning before Print Preview is destroyed.
- // 3. The document is composited again for content analysis.
- // 4. The print job used for scanning before system print is destroyed.
- // 5. A print job is started for actual printing.
- // 6. The print compositor will complete generating the document.
- // 7. Rendering for 1 page of document of content.
- // 8. Completes with document done.
- // 9. Wait for the actual printing job to be destroyed, to ensure
+ // 3. Use default settings.
+ // 4. Ask the user for settings.
+ // 5. The document is composited again for content analysis.
+ // 6. The print job used for scanning before system print is destroyed.
+ // 7. A print job is started for actual printing.
+ // 8. The print compositor will complete generating the document.
+ // 9. Rendering for 1 page of document of content.
+ // 10. Completes with document done.
+ // 11. Wait for the actual printing job to be destroyed, to ensure
// printing finished cleanly before completing the test.
- SetNumExpectedMessages(/*num=*/9);
+ SetNumExpectedMessages(/*num=*/11);
#endif
} else {
#if BUILDFLAG(IS_WIN)
@@ -2606,7 +3076,6 @@ IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest,
// 1. The document is composited for content analysis.
// 2. The print job used for scanning is destroyed.
SetNumExpectedMessages(/*num=*/2);
-
test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents());
WaitUntilCallbackReceived();
}
@@ -2614,23 +3083,24 @@ IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest,
// TODO(http://b/266119859): Change this check when scans are done after
// clicking Print from Print Preview instead of before displaying the dialog.
ASSERT_EQ(print_view_manager->preview_allowed(),
- content_analysis_allows_print());
+ PrintAllowedOrNonBlockingPolicy());
#if BUILDFLAG(IS_WIN)
const int kCompositedForContentAnalysisCount = 1;
#else
// TODO(http://b/285243428): Update expectation once a second analysis scan
// isn't done for system print from Print Preview.
const int kCompositedForContentAnalysisCount =
- content_analysis_allows_print() ? 2 : 1;
+ PrintAllowedOrNonBlockingPolicy() ? 2 : 1;
#endif
EXPECT_EQ(composited_for_content_analysis_count(),
kCompositedForContentAnalysisCount);
+ EXPECT_EQ(scanning_responses_count(), kCompositedForContentAnalysisCount);
#if BUILDFLAG(IS_WIN)
// One print job is always used to do scanning, and if printing is allowed
// then a second print job will be used for actual printing.
EXPECT_EQ(print_job_destruction_count(),
- content_analysis_allows_print() ? 2 : 1);
+ PrintAllowedOrNonBlockingPolicy() ? 2 : 1);
// There should be only one scan made, even though there could be up to two
// printing dialogs presented to the user.
@@ -2642,33 +3112,492 @@ IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest,
// A separate print job is always used for each scan, and if printing is
// allowed then another print job will be used for actual printing.
EXPECT_EQ(print_job_destruction_count(),
- content_analysis_allows_print() ? 3 : 1);
+ PrintAllowedOrNonBlockingPolicy() ? 3 : 1);
EXPECT_EQ(print_view_manager->got_snapshot_count(),
- content_analysis_allows_print() ? 2 : 1);
+ PrintAllowedOrNonBlockingPolicy() ? 2 : 1);
#endif
- // Validate that `NewDocument` is only called for actual printing, not as
+ // Validate that `NewDocument()` is only called for actual printing, not as
+ // part of content analysis, since that can needlessly prompt the user.
+ // When printing OOP, an extra call for a new document will occur since it
+ // gets called in both the browser process and in the Print Backend service.
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
+}
+
+#if BUILDFLAG(IS_MAC)
+IN_PROC_BROWSER_TEST_P(ContentAnalysisBeforePrintPreviewBrowserTest,
+ OpenPdfInPreviewFromPrintPreview) {
+ AddPrinter("printer_name");
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT);
+
+ // Since the content analysis scan happens before the Print Preview dialog,
+ // checking behavior when requesting opening in Preview from the print preview
+ // preview only is possible if the scan permits it.
+ if (PrintAllowedOrNonBlockingPolicy()) {
+ if (UseService()) {
+ // The expected events for this are:
+ // 1. The document is composited for content analysis.
+ // 2. The print job used for scanning before Print Preview is destroyed.
+ // 3. Ask the user for settings.
+ // 4. A print job is started for actual printing.
+ // 5. The print compositor will complete generating the document.
+ // 6. Completes with document done.
+ // 7. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/7);
+ } else {
+ // The expected events for this are:
+ // 1. The document is composited for content analysis.
+ // 2. The print job used for scanning is destroyed.
+ // 3. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/3);
+ }
+ OpenPdfInPreviewOnceReadyAndLoaded();
+ } else {
+ // The expected events for this are:
+ // 1. The document is composited for content analysis.
+ // 2. The print job used for scanning is destroyed.
+ SetNumExpectedMessages(/*num=*/2);
+ test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents());
+ WaitUntilCallbackReceived();
+ }
+
+ ASSERT_EQ(print_view_manager->preview_allowed(),
+ PrintAllowedOrNonBlockingPolicy());
+ EXPECT_EQ(composited_for_content_analysis_count(), 1);
+ EXPECT_EQ(scanning_responses_count(), 1);
+
+ // A separate print job is always used for each scan, and if printing is
+ // allowed then another print job will be used for actual printing.
+ EXPECT_EQ(print_job_destruction_count(),
+ PrintAllowedOrNonBlockingPolicy() ? 2 : 1);
+ EXPECT_EQ(print_view_manager->got_snapshot_count(),
+ PrintAllowedOrNonBlockingPolicy() ? 1 : 1);
+
+ // Validate that `NewDocument()` is only called for actual printing, not as
+ // part of content analysis, since that can needlessly prompt the user.
+ // When printing OOP, an extra call for a new document will occur since it
+ // gets called in both the browser process and in the Print Backend service.
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
+}
+#endif // BUILDFLAG(IS_MAC)
+
+IN_PROC_BROWSER_TEST_P(ContentAnalysisAfterPrintPreviewBrowserTest,
+ PrintWithPreview) {
+ AddPrinter("printer_name");
+
+ if (UseService()) {
+ // Test does not do extra cleanup beyond the check for analysis permission.
+ SkipPersistentContextsCheckOnShutdown();
+ }
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test1.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT);
+
+ if (PrintAllowedOrNonBlockingPolicy() && UseService()) {
+ // The expected events for this are:
+ // 1. Update print settings.
+ // 2. A print job is started.
+ // 3. Rendering for 1 page of document of content.
+ // 4. Completes with document done.
+ // 5. Wait for the one print job to be destroyed, to ensure printing
+ // finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/5);
+ } else {
+ print_view_manager->set_on_print_preview_done_closure(base::BindOnce(
+ &ContentAnalysisBeforePrintPreviewBrowserTest::CheckForQuit,
+ base::Unretained(this)));
+ // Expect an extra message for the print job created after content
+ // analysis to be destroyed.
+ SetNumExpectedMessages(/*num=*/PrintAllowedOrNonBlockingPolicy() ? 2 : 1);
+ }
+
+ PrintAfterPreviewIsReadyAndLoaded();
+
+ EXPECT_THAT(print_view_manager->preview_allowed(), testing::Optional(true));
+
+ // Since the scanned document was the one shown in the print preview dialog,
+ // no snapshotting should have taken place.
+ EXPECT_EQ(composited_for_content_analysis_count(), 0);
+ EXPECT_EQ(print_view_manager->got_snapshot_count(), 0);
+ EXPECT_EQ(scanning_responses_count(), 1);
+
+ // Validate that `NewDocument()` is only called for actual printing, not as
+ // part of content analysis, since that can needlessly prompt the user.
+ // When printing OOP, an extra call for a new document will occur since it
+ // gets called in both the browser process and in the Print Backend service.
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
+}
+
+IN_PROC_BROWSER_TEST_P(ContentAnalysisAfterPrintPreviewBrowserTest,
+ SystemPrintFromPrintPreview) {
+ AddPrinter("printer_name");
+
+ if (UseService() && !PrintAllowedOrNonBlockingPolicy()) {
+ // This results in a stranded context left in the Print Backend service.
+ // It will persist harmlessly until the service terminates after a short
+ // period of no printing activity.
+ SkipPersistentContextsCheckOnShutdown();
+ }
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test1.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::SYSTEM_DIALOG_PRINT);
+
+ if (PrintAllowedOrNonBlockingPolicy()) {
+ if (UseService()) {
+#if BUILDFLAG(IS_WIN)
+ // The expected events for this are:
+ // 1. Update print settings.
+ // 2. A print job is started, for actual printing.
+ // 3. Rendering for 1 page of document of content.
+ // 4. Completes with document done.
+ // 5. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/5);
+#else
+ // The expected events for this are:
+ // 1. Get the default settings.
+ // 2. Ask the user for settings.
+ // 3. A print job is started for actual printing.
+ // 4. The print compositor will complete generating the document.
+ // 5. Rendering for 1 page of document of content.
+ // 6. Completes with document done.
+ // 7. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/7);
+#endif // BUILDFLAG(IS_WIN)
+ } else {
+#if BUILDFLAG(IS_WIN)
+ // The expected event for this is:
+ // 1. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/1);
+#else
+ // The expected events for this are:
+ // 1. Get the default settings.
+ // 2. Ask the user for settings.
+ // 3. The print compositor will complete generating the document.
+ // 4. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/4);
+#endif // BUILDFLAG(IS_WIN)
+ }
+ SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true);
+ } else {
+#if BUILDFLAG(IS_WIN)
+ if (UseService()) {
+ // The expected events for this are:
+ // 1. Update print settings.
+ // 2. The print job is cancelled.
+ // 3. The print job is destroyed.
+ SetNumExpectedMessages(/*num=*/3);
+ } else {
+ // The expected events for this are:
+ // 1. The print job is cancelled.
+ // 2. The print job is destroyed.
+ SetNumExpectedMessages(/*num=*/2);
+ }
+#else
+ // The expected events for this are:
+ // 1. Use default settings.
+ // 2. Ask the user for settings.
+ // 3. The print compositor will complete generating the document.
+ // 4. The print job is cancelled.
+ // 5. The print job is destroyed.
+ SetNumExpectedMessages(/*num=*/5);
+#endif // BUILDFLAG(IS_WIN)
+ SystemPrintFromPreviewOnceReadyAndLoaded(/*wait_for_callback=*/true);
+ }
+
+ EXPECT_THAT(print_view_manager->preview_allowed(), testing::Optional(true));
+
+ // TODO(crbug.com/1457901): Update these assertions once all cases for this
+ // test are re-enabled.
+ EXPECT_EQ(composited_for_content_analysis_count(), 0);
+ EXPECT_EQ(print_job_destruction_count(), 1);
+ EXPECT_EQ(print_view_manager->got_snapshot_count(), 0);
+ EXPECT_EQ(scanning_responses_count(), 1);
+
+ // Validate that `NewDocument()` is only called for actual printing, not as
+ // part of content analysis, since that can needlessly prompt the user.
+ // When printing OOP, an extra call for a new document will occur since it
+ // gets called in both the browser process and in the Print Backend service.
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
+}
+
+#if BUILDFLAG(IS_MAC)
+IN_PROC_BROWSER_TEST_P(ContentAnalysisAfterPrintPreviewBrowserTest,
+ OpenPdfInPreviewFromPrintPreview) {
+ AddPrinter("printer_name");
+
+ if (UseService() && !PrintAllowedOrNonBlockingPolicy()) {
+ // This results in a stranded context left in the Print Backend service.
+ // It will persist harmlessly until the service terminates after a short
+ // period of no printing activity.
+ SkipPersistentContextsCheckOnShutdown();
+ }
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT);
+
+ if (PrintAllowedOrNonBlockingPolicy()) {
+ if (UseService()) {
+ // The expected events for this are:
+ // 1. Ask the user for settings.
+ // 2. A print job is started for actual printing.
+ // 3. The print compositor will complete generating the document.
+ // 4. Completes with document done.
+ // 5. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/5);
+ } else {
+ // The expected events for this are:
+ // 1. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/1);
+ }
+ } else {
+ print_view_manager->set_on_print_preview_done_closure(base::BindOnce(
+ &ContentAnalysisBeforePrintPreviewBrowserTest::CheckForQuit,
+ base::Unretained(this)));
+ // Expect an extra message for the print job created after content
+ // analysis to be destroyed.
+ SetNumExpectedMessages(/*num=*/PrintAllowedOrNonBlockingPolicy() ? 2 : 1);
+ }
+ OpenPdfInPreviewOnceReadyAndLoaded();
+
+ EXPECT_THAT(print_view_manager->preview_allowed(), testing::Optional(true));
+
+ EXPECT_EQ(composited_for_content_analysis_count(), 0);
+ EXPECT_EQ(print_job_destruction_count(),
+ PrintAllowedOrNonBlockingPolicy() ? 1 : 0);
+ EXPECT_EQ(print_view_manager->got_snapshot_count(), 0);
+ EXPECT_EQ(scanning_responses_count(), 1);
+
+ // Validate that `NewDocument()` is only called for actual printing, not as
// part of content analysis, since that can needlessly prompt the user.
// When printing OOP, an extra call for a new document will occur since it
// gets called in both the browser process and in the Print Backend service.
- EXPECT_EQ(new_document_called_count(),
- content_analysis_allows_print() ? (UseService() ? 2 : 1) : 0);
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
}
+#endif // BUILDFLAG(IS_MAC)
-IN_PROC_BROWSER_TEST_P(ContentAnalysisScriptedPreviewlessPrintBrowserTest,
- DocumentExecPrint) {
+IN_PROC_BROWSER_TEST_P(
+ ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest,
+ PrintNow) {
+ AddPrinter("printer_name");
+
+ if (UseService() && !PrintAllowedOrNonBlockingPolicy()) {
+ // This results in a stranded context left in the Print Backend service.
+ // It will persist harmlessly until the service terminates after a short
+ // period of no printing activity.
+ SkipPersistentContextsCheckOnShutdown();
+ }
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test1.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::SYSTEM_DIALOG_PRINT);
+
+ if (PrintAllowedOrNonBlockingPolicy()) {
+ if (UseService()) {
+ // The expected events are:
+ // 1. Get the default settings.
+ // 2. Ask the user for settings.
+ // 3. A print job is started.
+ // 4. The one page of the document is rendered.
+ // 5. Receive document done notification.
+ // 6. Wait until all processing for DidPrintDocument is known to have
+ // completed, to ensure printing finished cleanly before completing
+ // the test.
+ // 7. Wait for the one print job to be destroyed, to ensure printing
+ // finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/7);
+ } else {
+ // The expected events for this are:
+ // 1. Use default settings.
+ // 2. Ask the user for settings.
+ // 3. The print compositor will complete generating the document.
+ // 4. The print job is destroyed.
+ SetNumExpectedMessages(/*num=*/4);
+ }
+ } else {
+ // The expected events for this are:
+ // 1. Use default settings.
+ // 2. Ask the user for settings.
+ // 3. The print compositor will complete generating the document.
+ // 4. The print job is destroyed.
+ SetNumExpectedMessages(/*num=*/4);
+ }
+
+ StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
+ /*print_preview_disabled=*/true,
+ /*has_selection=*/false);
+
+ WaitUntilCallbackReceived();
+
+ ASSERT_TRUE(print_view_manager->scripted_print_called());
+ EXPECT_EQ(composited_for_content_analysis_count(), 0);
+ EXPECT_EQ(scanning_responses_count(), 1);
+
+ // Validate that `NewDocument()` is only called for actual printing, not as
+ // part of content analysis, since that can needlessly prompt the user.
+ // When printing OOP, an extra call for a new document will occur since it
+ // gets called in both the browser process and in the Print Backend service.
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
+}
+
+IN_PROC_BROWSER_TEST_P(
+ ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest,
+ DocumentExecPrint) {
RunScriptedPrintTest("document.execCommand('print');");
}
-IN_PROC_BROWSER_TEST_P(ContentAnalysisScriptedPreviewlessPrintBrowserTest,
- WindowPrint) {
+IN_PROC_BROWSER_TEST_P(
+ ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest,
+ WindowPrint) {
RunScriptedPrintTest("window.print()");
}
+IN_PROC_BROWSER_TEST_P(
+ ContentAnalysisScriptedPreviewlessPrintBeforeDialogBrowserTest,
+ PrintNow) {
+ AddPrinter("printer_name");
+
+ if (UseService() && !PrintAllowedOrNonBlockingPolicy()) {
+ // This results in a stranded context left in the Print Backend service.
+ // It will persist harmlessly until the service terminates after a short
+ // period of no printing activity.
+ SkipPersistentContextsCheckOnShutdown();
+ }
+
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test1.html"));
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ auto* print_view_manager = SetUpAndReturnPrintViewManagerForContentAnalysis(
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::SYSTEM_DIALOG_PRINT);
+
+ if (PrintAllowedOrNonBlockingPolicy()) {
+ if (UseService()) {
+ // The expected events are:
+ // 1. The document is composited for content analysis.
+ // 2. The print job used for scanning is destroyed.
+ // 3. Get the default settings.
+ // 4. Ask the user for settings.
+ // 5. A print job is started.
+ // 6. The one page of the document is rendered.
+ // 7. Receive document done notification.
+ // 8. Wait until all processing for DidPrintDocument is known to have
+ // completed, to ensure printing finished cleanly before completing
+ // the test.
+ // 9. Wait for the one print job to be destroyed, to ensure printing
+ // finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/9);
+ } else {
+ // The expected events for this are:
+ // 1. The document is composited for content analysis.
+ // 2. The print job used for scanning is destroyed.
+ // 3. Use default settings.
+ // 4. Ask the user for settings.
+ // 5. Wait until all processing for DidPrintDocument is known to have
+ // completed, to ensure printing finished cleanly before completing
+ // the test.
+ // 6. Wait for the actual printing job to be destroyed, to ensure
+ // printing finished cleanly before completing the test.
+ SetNumExpectedMessages(/*num=*/6);
+ }
+ } else {
+ // The expected events for this are:
+ // 1. Get the default settings.
+ // 2. The document is composited for content analysis.
+ // 3. The print job used for scanning is destroyed.
+ SetNumExpectedMessages(/*num=*/3);
+ }
+
+ StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
+ /*print_preview_disabled=*/true,
+ /*has_selection=*/false);
+
+ WaitUntilCallbackReceived();
+
+ // PrintNow uses the same code path as scripted prints to scan printed
+ // pages, so print_now_called() should always happen and
+ // scripted_print_called() should be called with the same result that is
+ // expected from scanning.
+ EXPECT_TRUE(print_view_manager->print_now_called());
+ EXPECT_EQ(print_view_manager->scripted_print_called(),
+ PrintAllowedOrNonBlockingPolicy());
+ EXPECT_EQ(composited_for_content_analysis_count(), 1);
+ EXPECT_EQ(scanning_responses_count(), 1);
+
+ // Validate that `NewDocument()` is only called for actual printing, not as
+ // part of content analysis, since that can needlessly prompt the user.
+ // When printing OOP, an extra call for a new document will occur since it
+ // gets called in both the browser process and in the Print Backend service.
+ EXPECT_EQ(new_document_called_count(), GetExpectedNewDocumentCalledCount());
+}
+
+IN_PROC_BROWSER_TEST_P(
+ ContentAnalysisScriptedPreviewlessPrintBeforeDialogBrowserTest,
+ DocumentExecPrint) {
+ RunScriptedPrintTest("document.execCommand('print');");
+}
+
+IN_PROC_BROWSER_TEST_P(
+ ContentAnalysisScriptedPreviewlessPrintBeforeDialogBrowserTest,
+ WindowPrint) {
+ RunScriptedPrintTest("window.print()");
+}
#endif // !BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_CHROMEOS)
-IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest,
+IN_PROC_BROWSER_TEST_P(ContentAnalysisBeforePrintPreviewBrowserTest,
BlockedByDLPThenNoContentAnalysis) {
AddPrinter("printer_name");
ASSERT_TRUE(embedded_test_server()->Started());
@@ -2680,14 +3609,15 @@ IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest,
ASSERT_TRUE(web_contents);
auto* print_view_manager =
TestPrintViewManagerForContentAnalysis::CreateForWebContents(
- web_contents);
+ web_contents,
+ enterprise_connectors::ContentAnalysisRequest::PRINT_PREVIEW_PRINT);
print_view_manager->set_allowed_by_dlp(false);
test::StartPrint(browser()->tab_strip_model()->GetActiveWebContents());
print_view_manager->WaitOnPreview();
- ASSERT_TRUE(print_view_manager->preview_allowed().has_value());
- ASSERT_FALSE(print_view_manager->preview_allowed().value());
+ EXPECT_THAT(print_view_manager->preview_allowed(), testing::Optional(false));
+ EXPECT_EQ(scanning_responses_count(), 0);
// This is always 0 because printing is always blocked by the DLP policy.
ASSERT_EQ(new_document_called_count(), 0);
@@ -2696,30 +3626,65 @@ IN_PROC_BROWSER_TEST_P(ContentAnalysisPrintBrowserTest,
INSTANTIATE_TEST_SUITE_P(
All,
- ContentAnalysisPrintBrowserTest,
- testing::Values(
- ContentAnalysisTestCase{/*content_analysis_allows_print=*/true,
- /*oop_enabled=*/true},
- ContentAnalysisTestCase{/*content_analysis_allows_print=*/true,
- /*oop_enabled=*/false},
- ContentAnalysisTestCase{/*content_analysis_allows_print=*/false,
- /*oop_enabled=*/true},
- ContentAnalysisTestCase{/*content_analysis_allows_print=*/false,
- /*oop_enabled=*/false}));
+ ContentAnalysisBeforePrintPreviewBrowserTest,
+ testing::Combine(
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+ /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy,
+ kCloudAnalysisNonBlockingPolicy,
+ kLocalAnalysisPolicy),
+#else
+ /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy,
+ kCloudAnalysisNonBlockingPolicy),
+#endif
+ /*content_analysis_allows_print=*/testing::Bool(),
+ /*oop_enabled=*/testing::Bool()));
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ ContentAnalysisAfterPrintPreviewBrowserTest,
+ testing::Combine(
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+ /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy,
+ kCloudAnalysisNonBlockingPolicy,
+ kLocalAnalysisPolicy),
+#else
+ /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy,
+ kCloudAnalysisNonBlockingPolicy),
+#endif
+ /*content_analysis_allows_print=*/testing::Bool(),
+ /*oop_enabled=*/testing::Bool()));
#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
INSTANTIATE_TEST_SUITE_P(
All,
- ContentAnalysisScriptedPreviewlessPrintBrowserTest,
- testing::Values(
- ContentAnalysisTestCase{/*content_analysis_allows_print=*/true,
- /*oop_enabled=*/true},
- ContentAnalysisTestCase{/*content_analysis_allows_print=*/true,
- /*oop_enabled=*/false},
- ContentAnalysisTestCase{/*content_analysis_allows_print=*/false,
- /*oop_enabled=*/true},
- ContentAnalysisTestCase{/*content_analysis_allows_print=*/false,
- /*oop_enabled=*/false}));
+ ContentAnalysisScriptedPreviewlessPrintBeforeDialogBrowserTest,
+ testing::Combine(
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+ /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy,
+ kCloudAnalysisNonBlockingPolicy,
+ kLocalAnalysisPolicy),
+#else
+ /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy,
+ kCloudAnalysisNonBlockingPolicy),
+#endif
+ /*content_analysis_allows_print=*/testing::Bool(),
+ /*oop_enabled=*/testing::Bool()));
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ ContentAnalysisScriptedPreviewlessPrintAfterDialogBrowserTest,
+ testing::Combine(
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+ /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy,
+ kCloudAnalysisNonBlockingPolicy,
+ kLocalAnalysisPolicy),
+#else
+ /*policy_value=*/testing::Values(kCloudAnalysisBlockingPolicy,
+ kCloudAnalysisNonBlockingPolicy),
+#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+ /*content_analysis_allows_print=*/testing::Bool(),
+ /*oop_enabled=*/testing::Bool()));
+
#endif // BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
diff --git a/chromium/chrome/browser/printing/test_print_job.cc b/chromium/chrome/browser/printing/test_print_job.cc
deleted file mode 100644
index fd462690873..00000000000
--- a/chromium/chrome/browser/printing/test_print_job.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "base/functional/callback.h"
-#include "base/memory/ref_counted_memory.h"
-#include "build/build_config.h"
-#include "chrome/browser/printing/print_job_worker.h"
-#include "chrome/browser/printing/printer_query.h"
-#include "chrome/browser/printing/test_print_job.h"
-#include "printing/printed_document.h"
-#include "ui/gfx/geometry/size.h"
-
-#if BUILDFLAG(IS_WIN)
-#include "printing/mojom/print.mojom.h"
-#endif
-
-namespace printing {
-
-void TestPrintJob::Initialize(std::unique_ptr<PrinterQuery> query,
- const std::u16string& name,
- uint32_t page_count) {
- // Since we do not actually print in these tests, just let this get destroyed
- // when this function exits.
- std::unique_ptr<PrintJobWorker> worker =
- query->TransferContextToNewWorker(nullptr);
-
- scoped_refptr<PrintedDocument> new_doc =
- base::MakeRefCounted<PrintedDocument>(query->ExtractSettings(), name,
- query->cookie());
-
- new_doc->set_page_count(page_count);
- UpdatePrintedDocument(new_doc.get());
-}
-
-void TestPrintJob::StartPrinting() {
- set_job_pending(true);
-}
-
-void TestPrintJob::Stop() {
- set_job_pending(false);
-}
-
-void TestPrintJob::Cancel() {
- set_job_pending(false);
-}
-
-void TestPrintJob::OnFailed() {}
-
-void TestPrintJob::OnDocDone(int job_id, PrintedDocument* document) {}
-
-bool TestPrintJob::FlushJob(base::TimeDelta timeout) {
- return true;
-}
-
-#if BUILDFLAG(IS_WIN)
-void TestPrintJob::StartPdfToEmfConversion(
- scoped_refptr<base::RefCountedMemory> bytes,
- const gfx::Size& page_size,
- const gfx::Rect& content_area) {
- page_size_ = page_size;
- content_area_ = content_area;
- type_ = mojom::PrinterLanguageType::kNone;
-}
-
-void TestPrintJob::StartPdfToPostScriptConversion(
- scoped_refptr<base::RefCountedMemory> bytes,
- const gfx::Rect& content_area,
- const gfx::Point& physical_offsets,
- bool ps_level2) {
- content_area_ = content_area;
- physical_offsets_ = physical_offsets;
- type_ = ps_level2 ? mojom::PrinterLanguageType::kPostscriptLevel2
- : mojom::PrinterLanguageType::kPostscriptLevel3;
-}
-
-void TestPrintJob::StartPdfToTextConversion(
- scoped_refptr<base::RefCountedMemory> bytes,
- const gfx::Size& page_size) {
- page_size_ = page_size;
- type_ = mojom::PrinterLanguageType::kTextOnly;
-}
-#endif // BUILDFLAG(IS_WIN)
-
-TestPrintJob::~TestPrintJob() {
- set_job_pending(false);
-}
-
-} // namespace printing
diff --git a/chromium/chrome/browser/printing/test_print_job.h b/chromium/chrome/browser/printing/test_print_job.h
deleted file mode 100644
index 5cfdf7bdee5..00000000000
--- a/chromium/chrome/browser/printing/test_print_job.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2018 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_PRINTING_TEST_PRINT_JOB_H_
-#define CHROME_BROWSER_PRINTING_TEST_PRINT_JOB_H_
-
-#include <memory>
-
-#include "base/functional/callback.h"
-#include "build/build_config.h"
-#include "chrome/browser/printing/print_job.h"
-#include "printing/print_settings.h"
-
-#if BUILDFLAG(IS_WIN)
-#include "printing/mojom/print.mojom.h"
-#endif
-
-namespace printing {
-
-class PrinterQuery;
-
-class TestPrintJob : public PrintJob {
- public:
- // Create an empty `PrintJob`. When initializing with this constructor,
- // post-constructor initialization must be done with `Initialize()`.
- TestPrintJob() = default;
-
- // Getters for values stored by `TestPrintJob` in Start...Converter functions.
- const gfx::Size& page_size() const { return page_size_; }
- const gfx::Rect& content_area() const { return content_area_; }
- const gfx::Point& physical_offsets() const { return physical_offsets_; }
-#if BUILDFLAG(IS_WIN)
- mojom::PrinterLanguageType type() const { return type_; }
-#endif
-
- // All remaining functions are `PrintJob` implementation.
- void Initialize(std::unique_ptr<PrinterQuery> query,
- const std::u16string& name,
- uint32_t page_count) override;
-
- // Sets `job_pending_` to true.
- void StartPrinting() override;
-
- // Sets `job_pending_` to false and deletes the worker.
- void Stop() override;
-
- // Sets `job_pending_` to false and deletes the worker.
- void Cancel() override;
-
- void OnFailed() override;
-
- void OnDocDone(int job_id, PrintedDocument* document) override;
-
- // Intentional no-op, returns true.
- bool FlushJob(base::TimeDelta timeout) override;
-
-#if BUILDFLAG(IS_WIN)
- // These functions fill in the corresponding member variables based on the
- // arguments passed in.
- void StartPdfToEmfConversion(scoped_refptr<base::RefCountedMemory> bytes,
- const gfx::Size& page_size,
- const gfx::Rect& content_area) override;
-
- void StartPdfToPostScriptConversion(
- scoped_refptr<base::RefCountedMemory> bytes,
- const gfx::Rect& content_area,
- const gfx::Point& physical_offsets,
- bool ps_level2) override;
-
- void StartPdfToTextConversion(scoped_refptr<base::RefCountedMemory> bytes,
- const gfx::Size& page_size) override;
-#endif // BUILDFLAG(IS_WIN)
-
- private:
- ~TestPrintJob() override;
-
- gfx::Size page_size_;
- gfx::Rect content_area_;
- gfx::Point physical_offsets_;
-#if BUILDFLAG(IS_WIN)
- mojom::PrinterLanguageType type_;
-#endif
-};
-
-} // namespace printing
-
-#endif // CHROME_BROWSER_PRINTING_TEST_PRINT_JOB_H_
diff --git a/chromium/chrome/browser/printing/test_print_preview_observer.cc b/chromium/chrome/browser/printing/test_print_preview_observer.cc
index b1e711f4a1a..f3297b13715 100644
--- a/chromium/chrome/browser/printing/test_print_preview_observer.cc
+++ b/chromium/chrome/browser/printing/test_print_preview_observer.cc
@@ -54,10 +54,6 @@ void TestPrintPreviewObserver::WaitUntilPreviewIsReady() {
std::ignore = WaitUntilPreviewIsReadyAndReturnPreviewDialog();
}
-void TestPrintPreviewObserver::ResetForAnotherPreview() {
- rendered_page_count_ = 0;
-}
-
void TestPrintPreviewObserver::EnsureWaitForLoaded() {
if (queue_.has_value()) {
// Have already added event listener.
diff --git a/chromium/chrome/browser/printing/test_print_preview_observer.h b/chromium/chrome/browser/printing/test_print_preview_observer.h
index dd7adc44594..1511a643efe 100644
--- a/chromium/chrome/browser/printing/test_print_preview_observer.h
+++ b/chromium/chrome/browser/printing/test_print_preview_observer.h
@@ -37,13 +37,6 @@ class TestPrintPreviewObserver : PrintPreviewUI::TestDelegate {
// convenience for callers that do not need the returned result.
void WaitUntilPreviewIsReady();
- // If a test modifies certain Print Preview settings, then another preview
- // render will be automatically initiated. This call resets the observer for
- // when that next render will be expected.
- // This doesn't work for all such settings at this time; e.g., changes to
- // N-up are not supported since `pages_per_sheet_` are constant from ctor.
- void ResetForAnotherPreview();
-
uint32_t rendered_page_count() const { return rendered_page_count_; }
private:
diff --git a/chromium/chrome/browser/printing/test_print_view_manager.cc b/chromium/chrome/browser/printing/test_print_view_manager.cc
index 27c6d5ff8a6..08af86943f0 100644
--- a/chromium/chrome/browser/printing/test_print_view_manager.cc
+++ b/chromium/chrome/browser/printing/test_print_view_manager.cc
@@ -12,6 +12,8 @@
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
+#include "build/build_config.h"
+#include "chrome/browser/printing/print_job.h"
#include "chrome/browser/printing/print_job_manager.h"
#include "chrome/browser/printing/print_view_manager_common.h"
#include "chrome/browser/printing/printer_query.h"
@@ -22,6 +24,11 @@
#include "printing/mojom/print.mojom.h"
#include "printing/print_settings.h"
+#if BUILDFLAG(IS_WIN)
+#include "printing/metafile.h"
+#include "printing/print_job_constants.h"
+#endif
+
namespace printing {
namespace {
@@ -45,6 +52,37 @@ void OnDidUpdatePrintSettings(
std::move(callback).Run(std::move(settings));
}
+class TestPrintJob : public PrintJob {
+ public:
+ explicit TestPrintJob(PrintJobManager* print_job_manager)
+ : PrintJob(print_job_manager) {}
+
+#if BUILDFLAG(IS_WIN)
+ void set_simulate_pdf_conversion_error_on_page_index(uint32_t page_index) {
+ simulate_pdf_conversion_error_on_page_index_ = page_index;
+ }
+#endif
+
+ private:
+ ~TestPrintJob() override = default;
+
+#if BUILDFLAG(IS_WIN)
+ // `PrintJob` overrides:
+ void OnPdfPageConverted(uint32_t page_index,
+ float scale_factor,
+ std::unique_ptr<MetafilePlayer> metafile) override {
+ if (simulate_pdf_conversion_error_on_page_index_.has_value() &&
+ page_index == *simulate_pdf_conversion_error_on_page_index_) {
+ // Override the page index to simulate an error.
+ page_index = kInvalidPageIndex;
+ }
+ PrintJob::OnPdfPageConverted(page_index, scale_factor, std::move(metafile));
+ }
+
+ absl::optional<uint32_t> simulate_pdf_conversion_error_on_page_index_;
+#endif // BUILDFLAG(IS_WIN)
+};
+
} // namespace
TestPrintViewManager::TestPrintViewManager(content::WebContents* web_contents)
@@ -92,15 +130,19 @@ bool TestPrintViewManager::PrintNow(content::RenderFrameHost* rfh) {
return *print_now_result_;
}
-bool TestPrintViewManager::CreateNewPrintJob(
- std::unique_ptr<PrinterQuery> query) {
- if (!PrintViewManager::CreateNewPrintJob(std::move(query))) {
- return false;
+scoped_refptr<PrintJob> TestPrintViewManager::CreatePrintJob(
+ PrintJobManager* print_job_manager) {
+ auto print_job = base::MakeRefCounted<TestPrintJob>(print_job_manager);
+#if BUILDFLAG(IS_WIN)
+ if (simulate_pdf_conversion_error_on_page_index_.has_value()) {
+ print_job->set_simulate_pdf_conversion_error_on_page_index(
+ *simulate_pdf_conversion_error_on_page_index_);
}
+#endif
if (on_did_create_print_job_) {
- on_did_create_print_job_.Run(print_job_.get());
+ on_did_create_print_job_.Run(print_job.get());
}
- return true;
+ return print_job;
}
void TestPrintViewManager::PrintPreviewAllowedForTesting() {
diff --git a/chromium/chrome/browser/printing/test_print_view_manager.h b/chromium/chrome/browser/printing/test_print_view_manager.h
index f03f4fcf5b8..4000bd44030 100644
--- a/chromium/chrome/browser/printing/test_print_view_manager.h
+++ b/chromium/chrome/browser/printing/test_print_view_manager.h
@@ -5,8 +5,6 @@
#ifndef CHROME_BROWSER_PRINTING_TEST_PRINT_VIEW_MANAGER_H_
#define CHROME_BROWSER_PRINTING_TEST_PRINT_VIEW_MANAGER_H_
-#include <memory>
-
#include "base/functional/callback.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/run_loop.h"
@@ -30,6 +28,12 @@ class TestPrintViewManager : public PrintViewManager {
TestPrintViewManager& operator=(const TestPrintViewManager&) = delete;
~TestPrintViewManager() override;
+#if BUILDFLAG(IS_WIN)
+ void set_simulate_pdf_conversion_error_on_page_index(uint32_t page_index) {
+ simulate_pdf_conversion_error_on_page_index_ = page_index;
+ }
+#endif
+
bool StartPrinting(content::WebContents* contents);
void WaitUntilPreviewIsShownOrCancelled();
@@ -47,7 +51,6 @@ class TestPrintViewManager : public PrintViewManager {
// `PrintViewManagerBase` overrides.
bool PrintNow(content::RenderFrameHost* rfh) override;
- bool CreateNewPrintJob(std::unique_ptr<PrinterQuery> query) override;
protected:
// This field is not a raw_ptr<> because it was filtered by the rewriter for:
@@ -55,6 +58,11 @@ class TestPrintViewManager : public PrintViewManager {
RAW_PTR_EXCLUSION base::RunLoop* run_loop_ = nullptr;
private:
+ // `PrintViewManagerBase` overrides.
+ scoped_refptr<PrintJob> CreatePrintJob(
+ PrintJobManager* print_job_manager) override;
+
+ // `PrintViewManager` overrides
void PrintPreviewAllowedForTesting() override;
// printing::mojom::PrintManagerHost:
@@ -63,6 +71,9 @@ class TestPrintViewManager : public PrintViewManager {
mojom::PrintPagesParamsPtr snooped_params_;
absl::optional<bool> print_now_result_;
+#if BUILDFLAG(IS_WIN)
+ absl::optional<uint32_t> simulate_pdf_conversion_error_on_page_index_;
+#endif
OnDidCreatePrintJobCallback on_did_create_print_job_;
};
diff --git a/chromium/chrome/browser/privacy/BUILD.gn b/chromium/chrome/browser/privacy/BUILD.gn
index 2a431be57df..3da0a6cabe5 100644
--- a/chromium/chrome/browser/privacy/BUILD.gn
+++ b/chromium/chrome/browser/privacy/BUILD.gn
@@ -12,6 +12,7 @@ if (is_android) {
"//chrome/browser/feed/android:*",
"//chrome/browser/ui/android/omnibox:*",
"//chrome/browser/ui/android/signin:*",
+ "//chrome/browser/xsurface_provider:*",
]
sources = [
"java/src/org/chromium/chrome/browser/privacy/secure_dns/SecureDnsBridge.java",
diff --git a/chromium/chrome/browser/privacy_guide/android/BUILD.gn b/chromium/chrome/browser/privacy_guide/android/BUILD.gn
index c2cb2272ffe..96be0c231c7 100644
--- a/chromium/chrome/browser/privacy_guide/android/BUILD.gn
+++ b/chromium/chrome/browser/privacy_guide/android/BUILD.gn
@@ -16,9 +16,11 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideExplanationItem.java",
"java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragment.java",
"java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideMetricsDelegate.java",
+ "java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuidePageTransformer.java",
"java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuidePagerAdapter.java",
"java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideUtils.java",
"java/src/org/chromium/chrome/browser/privacy_guide/SafeBrowsingFragment.java",
+ "java/src/org/chromium/chrome/browser/privacy_guide/SearchSuggestionsFragment.java",
"java/src/org/chromium/chrome/browser/privacy_guide/StepDisplayHandler.java",
"java/src/org/chromium/chrome/browser/privacy_guide/StepDisplayHandlerImpl.java",
"java/src/org/chromium/chrome/browser/privacy_guide/WelcomeFragment.java",
@@ -28,6 +30,7 @@ android_library("java") {
"//base:base_java",
"//chrome/browser/back_press/android:java",
"//chrome/browser/flags:java",
+ "//chrome/browser/preferences:java",
"//chrome/browser/privacy_sandbox/android:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/safe_browsing/android:java",
@@ -43,6 +46,7 @@ android_library("java") {
"//components/signin/public/android:java",
"//components/sync/android:sync_java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
@@ -67,6 +71,7 @@ robolectric_library("junit") {
"junit/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideMetricsDelegateTest.java",
"junit/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuidePagerAdapterTest.java",
"junit/src/org/chromium/chrome/browser/privacy_guide/SafeBrowsingFragmentTest.java",
+ "junit/src/org/chromium/chrome/browser/privacy_guide/SearchSuggestionsFragmentTest.java",
"junit/src/org/chromium/chrome/browser/privacy_guide/StepDisplayHandlerImplTest.java",
]
deps = [
@@ -75,11 +80,15 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
+ "//chrome/browser/flags:java",
+ "//chrome/browser/preferences:java",
"//chrome/browser/privacy_sandbox/android:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/safe_browsing/android:java",
"//chrome/browser/signin/services/android:java",
"//chrome/browser/sync/android:java",
+ "//chrome/test/android:chrome_java_unit_test_support",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/site_settings/android:java",
"//components/browser_ui/widget/android:java",
@@ -89,6 +98,7 @@ robolectric_library("junit") {
"//components/signin/public/android:java",
"//components/sync/android:sync_java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_fragment_fragment_testing_java",
@@ -109,8 +119,10 @@ android_library("javatests") {
":java_resources",
"//base:base_java",
"//base:base_java_test_support",
+ "//build/android:build_java",
"//chrome/android:chrome_java",
"//chrome/browser/flags:java",
+ "//chrome/browser/preferences:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/safe_browsing/android:java",
"//chrome/browser/settings:test_support_java",
@@ -122,10 +134,13 @@ android_library("javatests") {
"//components/content_settings/android:content_settings_enums_java",
"//components/embedder_support/android:util_java",
"//components/prefs/android:java",
+ "//components/signin/public/android:java",
"//components/sync/android:sync_java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/hamcrest:hamcrest_core_java",
"//third_party/hamcrest:hamcrest_library_java",
@@ -142,6 +157,7 @@ android_resources("java_resources") {
"java/res/drawable-night/privacy_guide_history_sync_image.xml",
"java/res/drawable-night/privacy_guide_msbb_image.xml",
"java/res/drawable-night/privacy_guide_sb_image.xml",
+ "java/res/drawable-night/privacy_guide_search_suggestions_image.xml",
"java/res/drawable-night/privacy_guide_welcome_image.xml",
"java/res/drawable/active_tab_circle.xml",
"java/res/drawable/inactive_tab_circle.xml",
@@ -150,6 +166,7 @@ android_resources("java_resources") {
"java/res/drawable/privacy_guide_history_sync_image.xml",
"java/res/drawable/privacy_guide_msbb_image.xml",
"java/res/drawable/privacy_guide_sb_image.xml",
+ "java/res/drawable/privacy_guide_search_suggestions_image.xml",
"java/res/drawable/privacy_guide_welcome_image.xml",
"java/res/drawable/tab_selector.xml",
"java/res/layout/privacy_guide_cookies_step.xml",
@@ -158,9 +175,12 @@ android_resources("java_resources") {
"java/res/layout/privacy_guide_explanation_item.xml",
"java/res/layout/privacy_guide_history_sync_step.xml",
"java/res/layout/privacy_guide_msbb_step.xml",
+ "java/res/layout/privacy_guide_msbb_v3_step.xml",
"java/res/layout/privacy_guide_sb_enhanced_explanation.xml",
+ "java/res/layout/privacy_guide_sb_enhanced_explanation_updated.xml",
"java/res/layout/privacy_guide_sb_standard_explanation.xml",
"java/res/layout/privacy_guide_sb_step.xml",
+ "java/res/layout/privacy_guide_search_suggestions_step.xml",
"java/res/layout/privacy_guide_steps.xml",
"java/res/layout/privacy_guide_welcome.xml",
"java/res/menu/privacy_guide_toolbar_menu.xml",
diff --git a/chromium/chrome/browser/privacy_sandbox/android/BUILD.gn b/chromium/chrome/browser/privacy_sandbox/android/BUILD.gn
index fffb46fe22a..c330e57f50a 100644
--- a/chromium/chrome/browser/privacy_sandbox/android/BUILD.gn
+++ b/chromium/chrome/browser/privacy_sandbox/android/BUILD.gn
@@ -62,6 +62,7 @@ android_library("java") {
"//components/favicon/android:java",
"//components/prefs/android:java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_browser_browser_java",
@@ -101,6 +102,7 @@ android_library("javatests") {
":java_resources",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//base/test:test_support_java",
"//chrome/android:chrome_java",
"//chrome/browser/flags:java",
@@ -109,10 +111,12 @@ android_library("javatests") {
"//chrome/browser/settings:test_support_java",
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/browser_ui/settings/android:java",
"//components/policy/android:policy_java_test_support",
"//components/prefs/android:java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/chrome/browser/promos/BUILD.gn b/chromium/chrome/browser/promos/BUILD.gn
index d6860ff5b1e..c6bb10815a9 100644
--- a/chromium/chrome/browser/promos/BUILD.gn
+++ b/chromium/chrome/browser/promos/BUILD.gn
@@ -6,7 +6,6 @@ source_set("utils") {
sources = [
"promos_features.cc",
"promos_features.h",
- "promos_pref_names.cc",
"promos_pref_names.h",
"promos_utils.cc",
"promos_utils.h",
diff --git a/chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 403b23c3fd6..292e336f0ca 100644
--- a/chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -257,17 +257,16 @@ class PushMessagingBrowserTestBase : public InProcessBrowserTest {
void LoadTestPageWithoutManifest() { LoadTestPage(GetNoManifestTestURL()); }
- bool RunScript(const std::string& script, std::string* result) {
- return RunScript(script, result, nullptr);
+ content::EvalJsResult RunScript(const std::string& script) {
+ return RunScript(script, nullptr);
}
- bool RunScript(const std::string& script, std::string* result,
- content::WebContents* web_contents) {
- if (!web_contents)
+ content::EvalJsResult RunScript(const std::string& script,
+ content::WebContents* web_contents) {
+ if (!web_contents) {
web_contents = GetBrowser()->tab_strip_model()->GetActiveWebContents();
- *result = content::EvalJs(web_contents->GetPrimaryMainFrame(), script)
- .ExtractString();
- return true;
+ }
+ return content::EvalJs(web_contents->GetPrimaryMainFrame(), script);
}
gcm::GCMAppHandler* GetAppHandler() {
@@ -404,52 +403,48 @@ class PushMessagingBrowserTestBase : public InProcessBrowserTest {
};
void PushMessagingBrowserTestBase::RequestAndAcceptPermission() {
- std::string script_result;
GetPermissionRequestManager()->set_auto_response_for_test(
permissions::PermissionRequestManager::ACCEPT_ALL);
- ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result));
- ASSERT_EQ("permission status - granted", script_result);
+ ASSERT_EQ("permission status - granted",
+ RunScript("requestNotificationPermission();"));
}
void PushMessagingBrowserTestBase::RequestAndDenyPermission() {
- std::string script_result;
GetPermissionRequestManager()->set_auto_response_for_test(
permissions::PermissionRequestManager::DENY_ALL);
- ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result));
- ASSERT_EQ("permission status - denied", script_result);
+ ASSERT_EQ("permission status - denied",
+ RunScript("requestNotificationPermission();"));
}
void PushMessagingBrowserTestBase::SubscribeSuccessfully(
PushSubscriptionKeyFormat key_format,
std::string* out_token) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
switch (key_format) {
case PushSubscriptionKeyFormat::kBinary:
- ASSERT_TRUE(RunScript("removeManifest()", &script_result));
- ASSERT_EQ("manifest removed", script_result);
+ ASSERT_EQ("manifest removed", RunScript("removeManifest()"));
- ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
- ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result, true, out_token));
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(RunScript("documentSubscribePush()").ExtractString(),
+ true, out_token));
break;
case PushSubscriptionKeyFormat::kBase64UrlEncoded:
- ASSERT_TRUE(RunScript("removeManifest()", &script_result));
- ASSERT_EQ("manifest removed", script_result);
+ ASSERT_EQ("manifest removed", RunScript("removeManifest()"));
- ASSERT_TRUE(RunScript("documentSubscribePushWithBase64URLEncodedString()",
- &script_result));
- ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result, true, out_token));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(
+ RunScript("documentSubscribePushWithBase64URLEncodedString()")
+ .ExtractString(),
+ true, out_token));
break;
case PushSubscriptionKeyFormat::kOmitKey:
// Test backwards compatibility with old ID based subscriptions.
- ASSERT_TRUE(
- RunScript("documentSubscribePushWithoutKey()", &script_result));
- ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result, false, out_token));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(
+ RunScript("documentSubscribePushWithoutKey()").ExtractString(), false,
+ out_token));
break;
default:
NOTREACHED();
@@ -492,9 +487,8 @@ void PushMessagingBrowserTestBase::LegacySubscribeSuccessfully(
// Create a non-InstanceID GCM registration. Have to directly access
// GCMDriver, since this codepath has been deleted from Push.
- std::string script_result;
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
@@ -663,82 +657,66 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
SubscribeSuccessNotificationsPrompt) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
GetPermissionRequestManager()->set_auto_response_for_test(
permissions::PermissionRequestManager::ACCEPT_ALL);
- ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
// Both of these methods EXPECT that they succeed.
- ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result));
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(RunScript("documentSubscribePush()").ExtractString()));
GetAppIdentifierForServiceWorkerRegistration(0LL);
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
SubscribeFailureNotificationsBlocked) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndDenyPermission());
- ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
- script_result);
+ RunScript("documentSubscribePush()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureNoManifest) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
- ASSERT_TRUE(RunScript("removeManifest()", &script_result));
- ASSERT_EQ("manifest removed", script_result);
+ ASSERT_EQ("manifest removed", RunScript("removeManifest()"));
- ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, and "
"manifest empty or missing",
- script_result);
+ RunScript("documentSubscribePushWithoutKey()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureNoSenderId) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
- ASSERT_TRUE(RunScript("swapManifestNoSenderId()", &script_result));
- ASSERT_EQ("sender id removed from manifest", script_result);
+ ASSERT_EQ("sender id removed from manifest",
+ RunScript("swapManifestNoSenderId()"));
- ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, and "
"gcm_sender_id not found in manifest",
- script_result);
+ RunScript("documentSubscribePushWithoutKey()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
RegisterFailureEmptyPushSubscriptionOptions) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
- ASSERT_TRUE(
- RunScript("documentSubscribePushWithEmptyOptions()", &script_result));
EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
- script_result);
+ RunScript("documentSubscribePushWithEmptyOptions()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWithInvalidation) {
@@ -777,245 +755,210 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWithInvalidation) {
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWorker) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Try to subscribe from a worker without a key. This should fail.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, and "
"gcm_sender_id not found in manifest",
- script_result);
+ RunScript("workerSubscribePushNoKey()"));
// Now run the subscribe with a key. This should succeed.
- ASSERT_TRUE(RunScript("workerSubscribePush()", &script_result));
ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, true /* standard_protocol */));
+ EndpointToToken(RunScript("workerSubscribePush()").ExtractString(),
+ true /* standard_protocol */));
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
SubscribeWorkerWithBase64URLEncodedString) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Try to subscribe from a worker without a key. This should fail.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, and "
"gcm_sender_id not found in manifest",
- script_result);
+ RunScript("workerSubscribePushNoKey()"));
// Now run the subscribe with a key. This should succeed.
- ASSERT_TRUE(RunScript("workerSubscribePushWithBase64URLEncodedString()",
- &script_result));
- ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, true /* standard_protocol */));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(
+ RunScript("workerSubscribePushWithBase64URLEncodedString()")
+ .ExtractString(),
+ true /* standard_protocol */));
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
ResubscribeWithoutKeyAfterSubscribingWithKeyInManifest) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Run the subscription from the document without a key, this will trigger
// the code to read sender id from the manifest and will write it to the
// datastore.
- ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
std::string token1;
- ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token1));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(
+ RunScript("documentSubscribePushWithoutKey()").ExtractString(),
+ false /* standard_protocol */, &token1));
- ASSERT_TRUE(RunScript("removeManifest()", &script_result));
- ASSERT_EQ("manifest removed", script_result);
+ ASSERT_EQ("manifest removed", RunScript("removeManifest()"));
// Try to resubscribe from the document without a key or manifest.
// This should fail.
- ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, "
"and manifest empty or missing",
- script_result);
+ RunScript("documentSubscribePushWithoutKey()"));
// Now run the subscribe from the service worker without a key.
// In this case, the sender id should be read from the datastore.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
std::string token2;
ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token2));
+ EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
+ false /* standard_protocol */, &token2));
EXPECT_EQ(token1, token2);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
// After unsubscribing, subscribe again from the worker with no key.
// The sender id should again be read from the datastore, so the
// subcribe should succeed, and we should get a new subscription token.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
std::string token3;
ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token3));
+ EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
+ false /* standard_protocol */, &token3));
EXPECT_NE(token1, token3);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}
IN_PROC_BROWSER_TEST_F(
PushMessagingBrowserTest,
ResubscribeWithoutKeyAfterSubscribingFromDocumentWithP256Key) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPageWithoutManifest(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Run the subscription from the document with a key.
- ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
- ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result));
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(RunScript("documentSubscribePush()").ExtractString()));
// Try to resubscribe from the document without a key - should fail.
- ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, "
"and manifest empty or missing",
- script_result);
+ RunScript("documentSubscribePushWithoutKey()"));
// Now try to resubscribe from the service worker without a key.
// This should also fail as the original key was not numeric.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, "
"and gcm_sender_id not found in manifest",
- script_result);
+ RunScript("workerSubscribePushNoKey()"));
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
// After unsubscribing, try to resubscribe again without a key.
// This should again fail.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, "
"and gcm_sender_id not found in manifest",
- script_result);
+ RunScript("workerSubscribePushNoKey()"));
}
IN_PROC_BROWSER_TEST_F(
PushMessagingBrowserTest,
ResubscribeWithoutKeyAfterSubscribingFromWorkerWithP256Key) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPageWithoutManifest(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Run the subscribe from the service worker with a key.
// This should succeed.
- ASSERT_TRUE(RunScript("workerSubscribePush()", &script_result));
ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, true /* standard_protocol */));
+ EndpointToToken(RunScript("workerSubscribePush()").ExtractString(),
+ true /* standard_protocol */));
// Try to resubscribe from the document without a key - should fail.
- ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, "
"and manifest empty or missing",
- script_result);
+ RunScript("documentSubscribePushWithoutKey()"));
// Now try to resubscribe from the service worker without a key.
// This should also fail as the original key was not numeric.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, and "
"gcm_sender_id not found in manifest",
- script_result);
+ RunScript("workerSubscribePushNoKey()"));
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
// After unsubscribing, try to resubscribe again without a key.
// This should again fail.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, "
"and gcm_sender_id not found in manifest",
- script_result);
+ RunScript("workerSubscribePushNoKey()"));
}
IN_PROC_BROWSER_TEST_F(
PushMessagingBrowserTest,
ResubscribeWithoutKeyAfterSubscribingFromDocumentWithNumber) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPageWithoutManifest(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Run the subscribe from the document with a numeric key.
// This should succeed.
- ASSERT_TRUE(
- RunScript("documentSubscribePushWithNumericKey()", &script_result));
+
std::string token1;
- ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token1));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(
+ RunScript("documentSubscribePushWithNumericKey()").ExtractString(),
+ false /* standard_protocol */, &token1));
// Try to resubscribe from the document without a key - should fail.
- ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, "
"and manifest empty or missing",
- script_result);
+ RunScript("documentSubscribePushWithoutKey()"));
// Now run the subscribe from the service worker without a key.
// In this case, the sender id should be read from the datastore.
@@ -1023,56 +966,50 @@ IN_PROC_BROWSER_TEST_F(
// no-key subscribes after subscribing with a numeric gcm sender id in the
// manifest, not a numeric applicationServerKey, but for code simplicity
// this case is allowed.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
std::string token2;
ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token2));
+ EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
+ false /* standard_protocol */, &token2));
EXPECT_EQ(token1, token2);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
// After unsubscribing, subscribe again from the worker with no key.
// The sender id should again be read from the datastore, so the
// subcribe should succeed, and we should get a new subscription token.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
std::string token3;
ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token3));
+ EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
+ false /* standard_protocol */, &token3));
EXPECT_NE(token1, token3);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}
IN_PROC_BROWSER_TEST_F(
PushMessagingBrowserTest,
ResubscribeWithoutKeyAfterSubscribingFromWorkerWithNumber) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPageWithoutManifest(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Run the subscribe from the service worker with a numeric key.
// This should succeed.
- ASSERT_TRUE(RunScript("workerSubscribePushWithNumericKey()", &script_result));
std::string token1;
- ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token1));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(
+ RunScript("workerSubscribePushWithNumericKey()").ExtractString(),
+ false /* standard_protocol */, &token1));
// Try to resubscribe from the document without a key - should fail.
- ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - missing applicationServerKey, "
"and manifest empty or missing",
- script_result);
+ RunScript("documentSubscribePushWithoutKey()"));
// Now run the subscribe from the service worker without a key.
// In this case, the sender id should be read from the datastore.
@@ -1080,85 +1017,71 @@ IN_PROC_BROWSER_TEST_F(
// no-key subscribes after subscribing with a numeric gcm sender id in the
// manifest, not a numeric applicationServerKey, but for code simplicity
// this case is allowed.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
std::string token2;
ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token2));
+ EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
+ false /* standard_protocol */, &token2));
EXPECT_EQ(token1, token2);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
// After unsubscribing, subscribe again from the worker with no key.
// The sender id should again be read from the datastore, so the
// subcribe should succeed, and we should get a new subscription token.
- ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
std::string token3;
ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token3));
+ EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
+ false /* standard_protocol */, &token3));
EXPECT_NE(token1, token3);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, ResubscribeWithMismatchedKey) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Run the subscribe from the service worker with a key.
// This should succeed.
- ASSERT_TRUE(
- RunScript("workerSubscribePushWithNumericKey('11111')", &script_result));
std::string token1;
- ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token1));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(
+ RunScript("workerSubscribePushWithNumericKey('11111')").ExtractString(),
+ false /* standard_protocol */, &token1));
// Try to resubscribe with a different key - should fail.
- ASSERT_TRUE(
- RunScript("workerSubscribePushWithNumericKey('22222')", &script_result));
EXPECT_EQ(
"InvalidStateError - Registration failed - A subscription with a "
"different applicationServerKey (or gcm_sender_id) already exists; to "
"change the applicationServerKey, unsubscribe then resubscribe.",
- script_result);
+ RunScript("workerSubscribePushWithNumericKey('22222')"));
// Try to resubscribe with the original key - should succeed.
- ASSERT_TRUE(
- RunScript("workerSubscribePushWithNumericKey('11111')", &script_result));
std::string token2;
- ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token2));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(
+ RunScript("workerSubscribePushWithNumericKey('11111')").ExtractString(),
+ false /* standard_protocol */, &token2));
EXPECT_EQ(token1, token2);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
// Resubscribe with a different key after unsubscribing.
// Should succeed, and we should get a new subscription token.
- ASSERT_TRUE(
- RunScript("workerSubscribePushWithNumericKey('22222')", &script_result));
std::string token3;
- ASSERT_NO_FATAL_FAILURE(
- EndpointToToken(script_result, false /* standard_protocol */, &token3));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(
+ RunScript("workerSubscribePushWithNumericKey('22222')").ExtractString(),
+ false /* standard_protocol */, &token3));
EXPECT_NE(token1, token3);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribePersisted) {
- std::string script_result;
-
// First, test that Service Worker registration IDs are assigned in order of
// registering the Service Workers, and the (fake) push subscription ids are
// assigned in order of push subscription (even when these orders are
@@ -1172,12 +1095,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribePersisted) {
EXPECT_EQ(sw0_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
LoadTestPage("/push_messaging/subscope1/test.html");
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
LoadTestPage("/push_messaging/subscope2/test.html");
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
// Note that we need to reload the page after registering, otherwise
// navigator.serviceWorker.ready is going to be resolved with the parent
@@ -1240,13 +1163,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, AppHandlerOnlyIfSubscribed) {
ASSERT_NO_FATAL_FAILURE(RestartPushService());
EXPECT_EQ(push_service(), GetAppHandler());
- std::string script_result;
-
// Unsubscribe.
base::RunLoop run_loop;
push_service()->SetUnsubscribeCallbackForTesting(run_loop.QuitClosure());
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
// The app handler is only guaranteed to be unregistered once the unsubscribe
// callback for testing has been run (PushSubscription.unsubscribe() usually
// resolves before that, in order to avoid blocking on network retries etc).
@@ -1258,17 +1178,13 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, AppHandlerOnlyIfSubscribed) {
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
gcm::IncomingMessage message;
@@ -1277,8 +1193,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) {
message.decrypted = true;
push_service()->OnMessage(app_identifier.app_id(), message);
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(true));
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_EQ("testdata", script_result);
+ EXPECT_EQ("testdata", RunScript("resultQueue.pop()"));
// Check that we record this case in UMA.
histogram_tester_.ExpectUniqueSample(
@@ -1287,17 +1202,13 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) {
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventOnShutdown) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
gcm::IncomingMessage message;
@@ -1310,43 +1221,35 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventOnShutdown) {
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPayload) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
gcm::IncomingMessage message;
message.sender_id = GetTestApplicationServerKey();
message.decrypted = false;
push_service()->OnMessage(app_identifier.app_id(), message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_EQ("[NULL]", script_result);
+ EXPECT_EQ("[NULL]", RunScript("resultQueue.pop()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, LegacyPushEvent) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
gcm::IncomingMessage message;
message.sender_id = kManifestSenderId;
message.decrypted = false;
push_service()->OnMessage(app_identifier.app_id(), message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_EQ("[NULL]", script_result);
+ EXPECT_EQ("[NULL]", RunScript("resultQueue.pop()"));
}
// Some users may have gotten into a state in the past where they still have
@@ -1371,10 +1274,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) {
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
// No push data should have been received.
- std::string script_result;
- ASSERT_TRUE(
- RunScript("String(resultQueue.popImmediately())", &script_result));
- EXPECT_EQ("null", script_result);
+ EXPECT_EQ("null", RunScript("String(resultQueue.popImmediately())"));
// Check that we record this case in UMA.
histogram_tester_.ExpectUniqueSample(
@@ -1397,18 +1297,14 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) {
// Tests receiving messages for a subscription that no longer exists.
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, NoSubscription) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
@@ -1421,9 +1317,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, NoSubscription) {
SendMessageAndWaitUntilHandled(app_identifier, message);
// No push data should have been received.
- ASSERT_TRUE(
- RunScript("String(resultQueue.popImmediately())", &script_result));
- EXPECT_EQ("null", script_result);
+ EXPECT_EQ("null", RunScript("String(resultQueue.popImmediately())"));
// Check that we record this case in UMA.
histogram_tester_.ExpectUniqueSample(
@@ -1442,15 +1336,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, NoSubscription) {
// Tests receiving messages for an origin that does not have permission, but
// somehow still has a subscription (as happened in https://crbug.com/633310).
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPermission) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Revoke notifications permission, but first disable the
// PushMessagingServiceImpl's OnContentSettingChanged handler so that it
@@ -1469,9 +1360,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPermission) {
SendMessageAndWaitUntilHandled(app_identifier, message);
// No push data should have been received.
- ASSERT_TRUE(
- RunScript("String(resultQueue.popImmediately())", &script_result));
- EXPECT_EQ("null", script_result);
+ EXPECT_EQ("null", RunScript("String(resultQueue.popImmediately())"));
// Check that we record this case in UMA.
histogram_tester_.ExpectUniqueSample(
@@ -1480,8 +1369,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPermission) {
// Missing permission should trigger an automatic unsubscription attempt.
EXPECT_EQ(app_identifier.app_id(), gcm_driver_->last_deletetoken_app_id());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
PushMessagingAppIdentifier app_identifier_afterwards =
PushMessagingAppIdentifier::FindByServiceWorker(GetBrowser()->profile(),
@@ -1497,19 +1385,15 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPermission) {
// https://crbug.com/458160 test is flaky on all platforms; but mostly linux.
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
DISABLED_PushEventEnforcesUserVisibleNotification) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
RemoveAllNotifications();
ASSERT_EQ(0u, GetNotificationCount());
@@ -1532,8 +1416,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
for (int n = 0; n < 2; n++) {
message.raw_data = "testdata";
SendMessageAndWaitUntilHandled(app_identifier, message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_EQ("testdata", script_result);
+ EXPECT_EQ("testdata", RunScript("resultQueue.pop()"));
EXPECT_EQ(0u, GetNotificationCount());
}
@@ -1547,8 +1430,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
// should not show a forced one.
message.raw_data = "shownotification";
SendMessageAndWaitUntilHandled(app_identifier, message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
- EXPECT_EQ("shownotification", script_result);
+ EXPECT_EQ("shownotification", RunScript("resultQueue.pop()", web_contents));
EXPECT_EQ(1u, GetNotificationCount());
EXPECT_TRUE(TagEquals(GetDisplayedNotifications()[0], "push_test_tag"));
RemoveAllNotifications();
@@ -1559,8 +1441,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
for (int n = 0; n < 2; n++) {
// First two missed notifications shouldn't force a default one.
SendMessageAndWaitUntilHandled(app_identifier, message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
- EXPECT_EQ("testdata", script_result);
+ EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));
EXPECT_EQ(0u, GetNotificationCount());
}
@@ -1568,8 +1449,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
// origin will be out of budget.
message.raw_data = "testdata";
SendMessageAndWaitUntilHandled(app_identifier, message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
- EXPECT_EQ("testdata", script_result);
+ EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));
{
std::vector<message_center::Notification> notifications =
@@ -1585,8 +1465,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
// a new notification themselves at a later point in time.
message.raw_data = "shownotification";
SendMessageAndWaitUntilHandled(app_identifier, message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
- EXPECT_EQ("shownotification", script_result);
+ EXPECT_EQ("shownotification", RunScript("resultQueue.pop()", web_contents));
{
std::vector<message_center::Notification> notifications =
@@ -1600,8 +1479,6 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
PushEventAllowSilentPushCommandLineFlag) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
@@ -1609,13 +1486,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_driver_->last_gettoken_authorized_entity());
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
RemoveAllNotifications();
ASSERT_EQ(0u, GetNotificationCount());
@@ -1639,16 +1514,14 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
message.decrypted = true;
SendMessageAndWaitUntilHandled(app_identifier, message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
- EXPECT_EQ("testdata", script_result);
+ EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));
EXPECT_EQ(0u, GetNotificationCount());
// If the Service Worker push event handler does not show a notification, we
// should show a forced one providing there is no foreground tab and the
// origin ran out of budget.
SendMessageAndWaitUntilHandled(app_identifier, message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
- EXPECT_EQ("testdata", script_result);
+ EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));
// Because the --allow-silent-push command line flag has not been passed,
// this should have shown a default notification.
@@ -1670,8 +1543,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
switches::kAllowSilentPush);
SendMessageAndWaitUntilHandled(app_identifier, message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
- EXPECT_EQ("testdata", script_result);
+ EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));
ASSERT_EQ(0u, GetNotificationCount());
}
@@ -1732,9 +1604,7 @@ IN_PROC_BROWSER_TEST_F(
GetAppIdentifierForServiceWorkerRegistration(0LL);
LoadTestPage(); // Reload to become controlled.
- std::string script_result;
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Add an origin to blocking lists after service worker is registered.
AddToPreloadDataBlocklist(
@@ -1750,9 +1620,7 @@ IN_PROC_BROWSER_TEST_F(
SendMessageAndWaitUntilHandled(app_identifier, message);
// No push data should have been received.
- ASSERT_TRUE(
- RunScript("String(resultQueue.popImmediately())", &script_result));
- EXPECT_EQ("null", script_result);
+ EXPECT_EQ("null", RunScript("String(resultQueue.popImmediately())"));
// Check that we record this case in UMA.
histogram_tester_.ExpectUniqueSample(
@@ -1763,8 +1631,7 @@ IN_PROC_BROWSER_TEST_F(
// Missing permission should trigger an automatic unsubscription attempt.
EXPECT_EQ(app_identifier.app_id(), gcm_driver_->last_deletetoken_app_id());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
PushMessagingAppIdentifier app_identifier_afterwards =
PushMessagingAppIdentifier::FindByServiceWorker(GetBrowser()->profile(),
@@ -1789,8 +1656,6 @@ IN_PROC_BROWSER_TEST_F(
IN_PROC_BROWSER_TEST_F(
PushMessagingBrowserTestWithAbusiveOriginPermissionRevocation,
OriginIsNotOnSafeBrowsingBlockingList) {
- std::string script_result;
-
// The origin should be marked as |ABUSIVE_CONTENT| on |CrowdDenyPreloadData|
// otherwise the permission revocation logic will not be triggered.
AddToPreloadDataBlocklist(
@@ -1801,11 +1666,9 @@ IN_PROC_BROWSER_TEST_F(
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
gcm::IncomingMessage message;
@@ -1814,8 +1677,7 @@ IN_PROC_BROWSER_TEST_F(
message.decrypted = true;
push_service()->OnMessage(app_identifier.app_id(), message);
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(true));
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_EQ("testdata", script_result);
+ EXPECT_EQ("testdata", RunScript("resultQueue.pop()"));
// Check that we record this case in UMA.
histogram_tester_.ExpectUniqueSample(
@@ -1836,8 +1698,6 @@ class PushMessagingBrowserTestWithNotificationTriggersEnabled
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTestWithNotificationTriggersEnabled,
PushEventIgnoresScheduledNotificationsForEnforcement) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
@@ -1868,8 +1728,8 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTestWithNotificationTriggersEnabled,
// should show a forced one providing there is no foreground tab and the
// origin ran out of budget.
SendMessageAndWaitUntilHandled(app_identifier, message);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
- EXPECT_EQ("shownotification-with-showtrigger", script_result);
+ EXPECT_EQ("shownotification-with-showtrigger",
+ RunScript("resultQueue.pop()", web_contents));
// Because scheduled notifications do not count as displayed notifications,
// this should have shown a default notification.
@@ -1883,19 +1743,15 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTestWithNotificationTriggersEnabled,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
PushEventEnforcesUserVisibleNotificationAfterQueue) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Fire off two push messages in sequence, only the second one of which will
// display a notification. The additional round-trip and I/O required by the
@@ -1931,7 +1787,6 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
PushEventNotificationWithoutEventWaitUntil) {
- std::string script_result;
content::WebContents* web_contents =
GetBrowser()->tab_strip_model()->GetActiveWebContents();
@@ -1939,13 +1794,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
base::RunLoop run_loop;
base::RepeatingClosure quit_barrier =
@@ -1960,8 +1813,8 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
push_service()->OnMessage(app_identifier.app_id(), message);
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(true));
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
- EXPECT_EQ("immediate:shownotification-without-waituntil", script_result);
+ EXPECT_EQ("immediate:shownotification-without-waituntil",
+ RunScript("resultQueue.pop()", web_contents));
run_loop.Run();
@@ -1970,49 +1823,42 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
EXPECT_TRUE(TagEquals(GetDisplayedNotifications()[0], "push_test_tag"));
// Verify that the renderer process hasn't crashed.
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysPrompt) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- ASSERT_EQ("permission status - prompt", script_result);
+ ASSERT_EQ("permission status - prompt",
+ RunScript("pushManagerPermissionState()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysGranted) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
- ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
- ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result));
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(RunScript("documentSubscribePush()").ExtractString()));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysDenied) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndDenyPermission());
- ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
- script_result);
+ RunScript("documentSubscribePush()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - denied", script_result);
+ EXPECT_EQ("permission status - denied",
+ RunScript("pushManagerPermissionState()"));
}
IN_PROC_BROWSER_TEST_P(PushMessagingPartitionedBrowserTest, CrossOriginFrame) {
@@ -2084,25 +1930,21 @@ IN_PROC_BROWSER_TEST_P(PushMessagingPartitionedBrowserTest, CrossOriginFrame) {
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
- std::string script_result;
-
std::string token1;
ASSERT_NO_FATAL_FAILURE(
SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey, &token1));
- ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
- EXPECT_EQ("ok - stored", script_result);
+ EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));
// Resolves true if there was a subscription.
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
1);
// Resolves false if there was no longer a subscription.
- ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
- EXPECT_EQ("unsubscribe result: false", script_result);
+ EXPECT_EQ("unsubscribe result: false",
+ RunScript("unsubscribeStoredPushSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
@@ -2120,12 +1962,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
ASSERT_NO_FATAL_FAILURE(
SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey, &token2));
EXPECT_NE(token1, token2);
- ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
- EXPECT_EQ("ok - stored", script_result);
- ASSERT_TRUE(RunScript("replaceServiceWorker()", &script_result));
- EXPECT_EQ("ok - service worker replaced", script_result);
- ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));
+ EXPECT_EQ("ok - service worker replaced",
+ RunScript("replaceServiceWorker()"));
+ EXPECT_EQ("unsubscribe result: true",
+ RunScript("unsubscribeStoredPushSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
@@ -2138,15 +1979,14 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey, &token3));
EXPECT_NE(token1, token3);
EXPECT_NE(token2, token3);
- ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
- EXPECT_EQ("ok - stored", script_result);
+ EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));
// Unregister service worker and wait for callback.
base::RunLoop run_loop;
push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
run_loop.QuitClosure());
- ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result));
- EXPECT_EQ("service worker unregistration status: true", script_result);
+ EXPECT_EQ("service worker unregistration status: true",
+ RunScript("unregisterServiceWorker()"));
run_loop.Run();
// Unregistering should have triggered an automatic unsubscribe.
@@ -2158,8 +1998,8 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 4);
// Now manual unsubscribe should return false.
- ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
- EXPECT_EQ("unsubscribe result: false", script_result);
+ EXPECT_EQ("unsubscribe result: false",
+ RunScript("unsubscribeStoredPushSubscription()"));
}
// Push subscriptions used to be non-InstanceID GCM registrations. Still need
@@ -2172,25 +2012,21 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
#endif
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
MAYBE_LegacyUnsubscribeSuccess) {
- std::string script_result;
-
std::string subscription_id1;
ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id1));
- ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
- EXPECT_EQ("ok - stored", script_result);
+ EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));
// Resolves true if there was a subscription.
gcm_service_->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
1);
// Resolves false if there was no longer a subscription.
- ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
- EXPECT_EQ("unsubscribe result: false", script_result);
+ EXPECT_EQ("unsubscribe result: false",
+ RunScript("unsubscribeStoredPushSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
@@ -2202,14 +2038,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id2));
EXPECT_NE(subscription_id1, subscription_id2);
gcm_service_->AddExpectedUnregisterResponse(gcm::GCMClient::NETWORK_ERROR);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
3);
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
// Doesn't reject if there were other push service errors (deactivates
// subscription locally anyway).
@@ -2219,8 +2053,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
EXPECT_NE(subscription_id2, subscription_id3);
gcm_service_->AddExpectedUnregisterResponse(
gcm::GCMClient::INVALID_PARAMETER);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
@@ -2234,12 +2067,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
EXPECT_NE(subscription_id1, subscription_id4);
EXPECT_NE(subscription_id2, subscription_id4);
EXPECT_NE(subscription_id3, subscription_id4);
- ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
- EXPECT_EQ("ok - stored", script_result);
- ASSERT_TRUE(RunScript("replaceServiceWorker()", &script_result));
- EXPECT_EQ("ok - service worker replaced", script_result);
- ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));
+ EXPECT_EQ("ok - service worker replaced",
+ RunScript("replaceServiceWorker()"));
+ EXPECT_EQ("unsubscribe result: true",
+ RunScript("unsubscribeStoredPushSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
@@ -2253,15 +2085,14 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
EXPECT_NE(subscription_id2, subscription_id5);
EXPECT_NE(subscription_id3, subscription_id5);
EXPECT_NE(subscription_id4, subscription_id5);
- ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
- EXPECT_EQ("ok - stored", script_result);
+ EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));
// Unregister service worker and wait for callback.
base::RunLoop run_loop;
push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
run_loop.QuitClosure());
- ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result));
- EXPECT_EQ("service worker unregistration status: true", script_result);
+ EXPECT_EQ("service worker unregistration status: true",
+ RunScript("unregisterServiceWorker()"));
run_loop.Run();
// Unregistering should have triggered an automatic unsubscribe.
@@ -2273,13 +2104,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 6);
// Now manual unsubscribe should return false.
- ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
- EXPECT_EQ("unsubscribe result: false", script_result);
+ EXPECT_EQ("unsubscribe result: false",
+ RunScript("unsubscribeStoredPushSubscription()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeOffline) {
- std::string script_result;
-
EXPECT_NE(push_service(), GetAppHandler());
std::string token;
@@ -2290,8 +2119,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeOffline) {
// Should quickly resolve true after deleting local state (rather than waiting
// until unsubscribing over the network exceeds the maximum backoff duration).
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
@@ -2308,20 +2136,17 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeOffline) {
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
UnregisteringServiceWorkerUnsubscribes) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Unregister the worker, and wait for callback to complete.
base::RunLoop run_loop;
push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
run_loop.QuitClosure());
- ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result));
- ASSERT_EQ("service worker unregistration status: true", script_result);
+ ASSERT_EQ("service worker unregistration status: true",
+ RunScript("unregisterServiceWorker()"));
run_loop.Run();
// This should have unregistered the push subscription.
@@ -2342,13 +2167,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
ServiceWorkerDatabaseDeletionUnsubscribes) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Pretend as if the Service Worker database went away, and wait for callback
// to complete.
@@ -2371,8 +2193,6 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
InvalidGetSubscriptionUnsubscribes) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
@@ -2396,8 +2216,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
EXPECT_EQ(app_identifier1.app_id(), app_identifier2.app_id());
// Now call PushManager.getSubscription(). It should return null.
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
// This should have unsubscribed the push subscription.
histogram_tester_.ExpectUniqueSample(
@@ -2415,15 +2234,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
GlobalResetPushPermissionUnsubscribes) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
scoped_refptr<content::MessageLoopRunner> message_loop_runner =
new content::MessageLoopRunner;
@@ -2435,11 +2251,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
message_loop_runner->Run();
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - prompt", script_result);
+ EXPECT_EQ("permission status - prompt",
+ RunScript("pushManagerPermissionState()"));
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
@@ -2450,15 +2265,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
LocalResetPushPermissionUnsubscribes) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
scoped_refptr<content::MessageLoopRunner> message_loop_runner =
new content::MessageLoopRunner;
@@ -2473,11 +2285,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
message_loop_runner->Run();
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - prompt", script_result);
+ EXPECT_EQ("permission status - prompt",
+ RunScript("pushManagerPermissionState()"));
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
@@ -2488,15 +2299,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
DenyPushPermissionUnsubscribes) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
scoped_refptr<content::MessageLoopRunner> message_loop_runner =
new content::MessageLoopRunner;
@@ -2511,11 +2319,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
message_loop_runner->Run();
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - denied", script_result);
+ EXPECT_EQ("permission status - denied",
+ RunScript("pushManagerPermissionState()"));
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
@@ -2526,15 +2333,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
GlobalResetNotificationsPermissionUnsubscribes) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
scoped_refptr<content::MessageLoopRunner> message_loop_runner =
new content::MessageLoopRunner;
@@ -2546,11 +2350,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
message_loop_runner->Run();
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - prompt", script_result);
+ EXPECT_EQ("permission status - prompt",
+ RunScript("pushManagerPermissionState()"));
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
@@ -2561,15 +2364,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
LocalResetNotificationsPermissionUnsubscribes) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
scoped_refptr<content::MessageLoopRunner> message_loop_runner =
new content::MessageLoopRunner;
@@ -2584,11 +2384,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
message_loop_runner->Run();
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - prompt", script_result);
+ EXPECT_EQ("permission status - prompt",
+ RunScript("pushManagerPermissionState()"));
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
@@ -2599,15 +2398,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
DenyNotificationsPermissionUnsubscribes) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
scoped_refptr<content::MessageLoopRunner> message_loop_runner =
new content::MessageLoopRunner;
@@ -2622,11 +2418,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
message_loop_runner->Run();
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - denied", script_result);
+ EXPECT_EQ("permission status - denied",
+ RunScript("pushManagerPermissionState()"));
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("false - not subscribed", script_result);
+ EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
histogram_tester_.ExpectUniqueSample(
"PushMessaging.UnregistrationReason",
@@ -2637,15 +2432,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
GrantAlreadyGrantedPermissionDoesNotUnsubscribe) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
scoped_refptr<content::MessageLoopRunner> message_loop_runner =
new content::MessageLoopRunner;
@@ -2660,11 +2452,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
message_loop_runner->Run();
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 0);
}
@@ -2675,15 +2466,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
// that have push permission with some non-common rules.
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
AutomaticUnsubscriptionFollowsContentSettingRules) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
scoped_refptr<content::MessageLoopRunner> message_loop_runner =
new content::MessageLoopRunner;
@@ -2706,11 +2494,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
// The Push service should not unsubscribe |origin| because at no point it was
// left without permission to use Push.
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 0);
}
@@ -2768,21 +2555,17 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, EncryptionKeyUniqueness) {
ASSERT_NO_FATAL_FAILURE(
SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey, &token1));
- std::string first_public_key;
- ASSERT_TRUE(RunScript("GetP256dh()", &first_public_key));
+ std::string first_public_key = RunScript("GetP256dh()").ExtractString();
EXPECT_GE(first_public_key.size(), 32u);
- std::string script_result;
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
std::string token2;
ASSERT_NO_FATAL_FAILURE(
SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token2));
EXPECT_NE(token1, token2);
- std::string second_public_key;
- ASSERT_TRUE(RunScript("GetP256dh()", &second_public_key));
+ std::string second_public_key = RunScript("GetP256dh()").ExtractString();
EXPECT_GE(second_public_key.size(), 32u);
EXPECT_NE(first_public_key, second_public_key);
@@ -2801,7 +2584,7 @@ class PushMessagingIncognitoBrowserTest : public PushMessagingBrowserTestBase {
incognito_browser_ = CreateIncognitoBrowser();
// We SetUp here rather than in SetUp since the https_server isn't yet
// created at that time.
- prerender_helper_.SetUp(https_server());
+ prerender_helper_.RegisterServerRequestMonitor(https_server());
PushMessagingBrowserTestBase::SetUpOnMainThread();
}
Browser* GetBrowser() const override { return incognito_browser_; }
@@ -2812,7 +2595,7 @@ class PushMessagingIncognitoBrowserTest : public PushMessagingBrowserTestBase {
protected:
content::test::PrerenderTestHelper prerender_helper_;
- raw_ptr<Browser, DanglingUntriaged> incognito_browser_ = nullptr;
+ raw_ptr<Browser, AcrossTasksDanglingUntriaged> incognito_browser_ = nullptr;
};
// Regression test for https://crbug.com/476474
@@ -2820,15 +2603,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest,
IncognitoGetSubscriptionDoesNotHang) {
ASSERT_TRUE(GetBrowser()->profile()->IsOffTheRecord());
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
// In Incognito mode the promise returned by getSubscription should not hang,
// it should just fulfill with null.
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- ASSERT_EQ("false - not subscribed", script_result);
+ ASSERT_EQ("false - not subscribed", RunScript("hasSubscription()"));
}
IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest, WarningToCorrectRFH) {
@@ -2843,14 +2623,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest, WarningToCorrectRFH) {
return message.source_frame->IsInPrimaryMainFrame();
}));
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
- ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
ASSERT_EQ("AbortError - Registration failed - permission denied",
- script_result);
+ RunScript("documentSubscribePush()"));
ASSERT_TRUE(console_observer.Wait());
EXPECT_EQ(1u, console_observer.messages().size());
@@ -2929,27 +2706,22 @@ class PushMessagingDisallowSenderIdsBrowserTest
IN_PROC_BROWSER_TEST_F(PushMessagingDisallowSenderIdsBrowserTest,
SubscriptionWithSenderIdFails) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Attempt to create a subscription with a GCM Sender ID ("numeric key"),
// which should fail because the kPushMessagingDisallowSenderIDs feature has
// been enabled for this test.
- ASSERT_TRUE(
- RunScript("documentSubscribePushWithNumericKey()", &script_result));
EXPECT_EQ(
"AbortError - Registration failed - GCM Sender IDs are no longer "
"supported, please upgrade to VAPID authentication instead",
- script_result);
+ RunScript("documentSubscribePushWithNumericKey()"));
}
class PushSubscriptionWithExpirationTimeTest
@@ -2980,29 +2752,24 @@ bool PushSubscriptionWithExpirationTimeTest::IsExpirationTimeValid(
IN_PROC_BROWSER_TEST_F(PushSubscriptionWithExpirationTimeTest,
SubscribeGetSubscriptionWithExpirationTime) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// Subscribe with expiration time enabled, should get a subscription with
// expiration time in the future back
- std::string subscription_expiration_time;
- ASSERT_TRUE(RunScript("documentSubscribePushGetExpirationTime()",
- &subscription_expiration_time));
+ std::string subscription_expiration_time =
+ RunScript("documentSubscribePushGetExpirationTime()").ExtractString();
EXPECT_TRUE(IsExpirationTimeValid(subscription_expiration_time));
- std::string get_subscription_expiration_time;
// Get subscription should also yield a subscription with expiration time
- ASSERT_TRUE(RunScript("GetSubscriptionExpirationTime()",
- &get_subscription_expiration_time));
+ std::string get_subscription_expiration_time =
+ RunScript("GetSubscriptionExpirationTime()").ExtractString();
EXPECT_TRUE(IsExpirationTimeValid(get_subscription_expiration_time));
// Both methods should return the same expiration time
ASSERT_EQ(subscription_expiration_time, get_subscription_expiration_time);
@@ -3010,16 +2777,13 @@ IN_PROC_BROWSER_TEST_F(PushSubscriptionWithExpirationTimeTest,
IN_PROC_BROWSER_TEST_F(PushSubscriptionWithExpirationTimeTest,
GetSubscriptionWithExpirationTime) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
// Get subscription should also yield a subscription with expiration time
- ASSERT_TRUE(RunScript("GetSubscriptionExpirationTime()", &script_result));
- EXPECT_TRUE(IsExpirationTimeValid(script_result));
+ EXPECT_TRUE(IsExpirationTimeValid(
+ RunScript("GetSubscriptionExpirationTime()").ExtractString()));
}
class PushSubscriptionWithoutExpirationTimeTest
@@ -3040,23 +2804,18 @@ class PushSubscriptionWithoutExpirationTimeTest
IN_PROC_BROWSER_TEST_F(PushSubscriptionWithoutExpirationTimeTest,
SubscribeDocumentExpirationTimeNull) {
- std::string script_result;
-
- ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
- ASSERT_EQ("ok - service worker registered", script_result);
+ ASSERT_EQ("ok - service worker registered",
+ RunScript("registerServiceWorker()"));
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
// When |features::kPushSubscriptionWithExpirationTime| is disabled,
// expiration time should be null
- ASSERT_TRUE(
- RunScript("documentSubscribePushGetExpirationTime()", &script_result));
- EXPECT_EQ("null", script_result);
+ EXPECT_EQ("null", RunScript("documentSubscribePushGetExpirationTime()"));
}
class PushSubscriptionChangeEventTest : public PushMessagingBrowserTestBase {
@@ -3076,8 +2835,6 @@ class PushSubscriptionChangeEventTest : public PushMessagingBrowserTestBase {
IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
PushSubscriptionChangeEventSuccess) {
- std::string script_result;
-
// Create the |old_subscription| by subscribing and unsubscribing again
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
@@ -3086,8 +2843,7 @@ IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
blink::mojom::PushSubscriptionPtr old_subscription =
GetSubscriptionForAppIdentifier(app_identifier);
- ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
- EXPECT_EQ("unsubscribe result: true", script_result);
+ EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
// There should be no subscription since we unsubscribed
EXPECT_EQ(PushMessagingAppIdentifier::GetCount(GetBrowser()->profile()), 0u);
@@ -3103,11 +2859,9 @@ IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
GURL old_endpoint = old_subscription->endpoint;
GURL new_endpoint = new_subscription->endpoint;
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
base::RunLoop run_loop;
push_service()->FirePushSubscriptionChange(
@@ -3116,11 +2870,9 @@ IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
run_loop.Run();
// Compare old subscription
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_EQ(old_endpoint.spec(), script_result);
+ EXPECT_EQ(old_endpoint.spec(), RunScript("resultQueue.pop()"));
// Compare new subscription
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_EQ(new_endpoint.spec(), script_result);
+ EXPECT_EQ(new_endpoint.spec(), RunScript("resultQueue.pop()"));
// Check that we record this case in UMA.
histogram_tester_.ExpectUniqueSample(
@@ -3130,21 +2882,16 @@ IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
FiredAfterPermissionRevoked) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - granted", script_result);
+ EXPECT_EQ("permission status - granted",
+ RunScript("pushManagerPermissionState()"));
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
@@ -3159,16 +2906,14 @@ IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
CONTENT_SETTING_BLOCK);
run_loop.Run();
- ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
- EXPECT_EQ("permission status - denied", script_result);
+ EXPECT_EQ("permission status - denied",
+ RunScript("pushManagerPermissionState()"));
// Check if the pushsubscriptionchangeevent arrived in the document and
// whether the |old_subscription| has the expected endpoint and
// |new_subscription| is null
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_EQ(old_subscription->endpoint.spec(), script_result);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_EQ("null", script_result);
+ EXPECT_EQ(old_subscription->endpoint.spec(), RunScript("resultQueue.pop()"));
+ EXPECT_EQ("null", RunScript("resultQueue.pop()"));
// Check that we record this case in UMA.
histogram_tester_.ExpectUniqueSample(
@@ -3177,18 +2922,13 @@ IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
}
IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest, OnInvalidation) {
- std::string script_result;
-
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
- ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
- EXPECT_EQ("true - subscribed", script_result);
+ EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("false - is not controlled", script_result);
+ ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
LoadTestPage(); // Reload to become controlled.
- ASSERT_TRUE(RunScript("isControlled()", &script_result));
- ASSERT_EQ("true - is controlled", script_result);
+ ASSERT_EQ("true - is controlled", RunScript("isControlled()"));
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
@@ -3215,10 +2955,8 @@ IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest, OnInvalidation) {
base::RunLoop().RunUntilIdle();
// Expect `pushsubscriptionchange` event that is not null
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_NE("null", script_result);
- ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
- EXPECT_NE("null", script_result);
+ EXPECT_NE("null", RunScript("resultQueue.pop()"));
+ EXPECT_NE("null", RunScript("resultQueue.pop()"));
// Check that we record this case in UMA.
histogram_tester_.ExpectUniqueSample(
diff --git a/chromium/chrome/browser/push_messaging/push_messaging_service_factory.cc b/chromium/chrome/browser/push_messaging/push_messaging_service_factory.cc
index 7bf7391293c..ab9113f738c 100644
--- a/chromium/chrome/browser/push_messaging/push_messaging_service_factory.cc
+++ b/chromium/chrome/browser/push_messaging/push_messaging_service_factory.cc
@@ -66,16 +66,16 @@ PushMessagingServiceFactory::~PushMessagingServiceFactory() = default;
void PushMessagingServiceFactory::RestoreFactoryForTests(
content::BrowserContext* context) {
- SetTestingFactory(context,
- base::BindRepeating([](content::BrowserContext* context) {
- return base::WrapUnique(
- GetInstance()->BuildServiceInstanceFor(context));
- }));
+ SetTestingFactory(
+ context, base::BindRepeating([](content::BrowserContext* context) {
+ return GetInstance()->BuildServiceInstanceForBrowserContext(context);
+ }));
}
-KeyedService* PushMessagingServiceFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+PushMessagingServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
CHECK(!profile->IsOffTheRecord());
- return new PushMessagingServiceImpl(profile);
+ return std::make_unique<PushMessagingServiceImpl>(profile);
}
diff --git a/chromium/chrome/browser/push_messaging/push_messaging_service_factory.h b/chromium/chrome/browser/push_messaging/push_messaging_service_factory.h
index ab903acbca2..9e4298eac3a 100644
--- a/chromium/chrome/browser/push_messaging/push_messaging_service_factory.h
+++ b/chromium/chrome/browser/push_messaging/push_messaging_service_factory.h
@@ -31,7 +31,7 @@ class PushMessagingServiceFactory : public ProfileKeyedServiceFactory {
~PushMessagingServiceFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chromium/chrome/browser/push_messaging/push_messaging_service_impl.cc
index 634ffdc6f6f..109fdd20fa9 100644
--- a/chromium/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chromium/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -15,7 +15,6 @@
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
-#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_runner.h"
@@ -48,7 +47,6 @@
#include "components/gcm_driver/instance_id/instance_id_driver.h"
#include "components/gcm_driver/instance_id/instance_id_profile_service.h"
#include "components/permissions/permission_manager.h"
-#include "components/permissions/permission_result.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
@@ -155,14 +153,6 @@ void RecordUnsubscribeReason(blink::mojom::PushUnregistrationReason reason) {
UMA_HISTOGRAM_ENUMERATION("PushMessaging.UnregistrationReason", reason);
}
-void RecordUnsubscribeGCMResult(gcm::GCMClient::Result result) {
- UMA_HISTOGRAM_ENUMERATION("PushMessaging.UnregistrationGCMResult", result);
-}
-
-void RecordUnsubscribeIIDResult(InstanceID::Result result) {
- UMA_HISTOGRAM_ENUMERATION("PushMessaging.UnregistrationIIDResult", result);
-}
-
void UnregisterCallbackToClosure(
base::OnceClosure closure,
blink::mojom::PushUnregistrationStatus status) {
@@ -356,12 +346,6 @@ void PushMessagingServiceImpl::OnMessage(const std::string& app_id,
return;
#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
- if (g_browser_process->background_mode_manager()) {
- UMA_HISTOGRAM_BOOLEAN("PushMessaging.ReceivedMessageInBackground",
- g_browser_process->background_mode_manager()
- ->IsBackgroundWithoutWindows());
- }
-
if (!in_flight_keep_alive_) {
in_flight_keep_alive_ = std::make_unique<ScopedKeepAlive>(
KeepAliveOrigin::IN_FLIGHT_PUSH_MESSAGE,
@@ -439,9 +423,6 @@ void PushMessagingServiceImpl::OnCheckedOrigin(
PermissionRevocationRequest::Outcome outcome) {
origin_revocation_request_.reset();
- base::UmaHistogramLongTimes("PushMessaging.CheckOriginForAbuseTime",
- base::Time::Now() - message.received_time);
-
PushMessagingAppIdentifier app_identifier =
PushMessagingAppIdentifier::FindByAppId(profile_, message.app_id);
@@ -533,9 +514,6 @@ void PushMessagingServiceImpl::
if (message.decrypted)
payload = message.raw_data;
- base::UmaHistogramLongTimes("PushMessaging.DeliverQueuedMessageTime",
- base::Time::Now() - next_message.received_time);
-
// Inform tests observing message dispatching about the event.
if (message_dispatched_callback_for_testing_) {
message_dispatched_callback_for_testing_.Run(
@@ -682,10 +660,6 @@ void PushMessagingServiceImpl::DidHandleEnqueuedMessage(
std::queue<PendingMessage>& delivery_queue = iter->second;
CHECK(!delivery_queue.empty());
- base::UmaHistogramLongTimes(
- "PushMessaging.MessageHandledTime",
- base::Time::Now() - delivery_queue.front().received_time);
-
delivery_queue.pop();
if (delivery_queue.empty())
message_delivery_queue_.erase(key);
@@ -839,7 +813,9 @@ void PushMessagingServiceImpl::SubscribeFromDocument(
// `render_frame_host` and we always use `requesting_origin` for
// NOTIFICATIONS.
profile_->GetPermissionController()->RequestPermissionFromCurrentDocument(
- blink::PermissionType::NOTIFICATIONS, render_frame_host, user_gesture,
+ render_frame_host,
+ content::PermissionRequestDescription(
+ blink::PermissionType::NOTIFICATIONS, user_gesture),
base::BindOnce(&PushMessagingServiceImpl::DoSubscribe,
weak_factory_.GetWeakPtr(), std::move(app_identifier),
std::move(options), std::move(callback), render_process_id,
@@ -1321,14 +1297,12 @@ void PushMessagingServiceImpl::DidClearPushSubscriptionId(
void PushMessagingServiceImpl::DidUnregister(bool was_subscribed,
gcm::GCMClient::Result result) {
- RecordUnsubscribeGCMResult(result);
DidUnsubscribe(std::string() /* app_id_when_instance_id */, was_subscribed);
}
void PushMessagingServiceImpl::DidDeleteID(const std::string& app_id,
bool was_subscribed,
InstanceID::Result result) {
- RecordUnsubscribeIIDResult(result);
// DidUnsubscribe must be run asynchronously when passing a non-empty
// |app_id_when_instance_id|, since it calls
// InstanceIDDriver::RemoveInstanceID which deletes the InstanceID itself.
diff --git a/chromium/chrome/browser/push_messaging/push_messaging_service_unittest.cc b/chromium/chrome/browser/push_messaging/push_messaging_service_unittest.cc
index 9e937709f0f..4df2daa1883 100644
--- a/chromium/chrome/browser/push_messaging/push_messaging_service_unittest.cc
+++ b/chromium/chrome/browser/push_messaging/push_messaging_service_unittest.cc
@@ -11,7 +11,6 @@
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
-#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -97,8 +96,6 @@ std::unique_ptr<KeyedService> BuildFakeGCMProfileService(
return gcm::FakeGCMProfileService::Build(static_cast<Profile*>(context));
}
-constexpr base::TimeDelta kPushEventHandleTime = base::Seconds(10);
-
} // namespace
class PushMessagingServiceTest : public ::testing::Test {
@@ -391,105 +388,6 @@ TEST_F(PushMessagingServiceTest, MAYBE_RemoveExpiredSubscriptions) {
EXPECT_TRUE(deleted_identifier.is_null());
}
-TEST_F(PushMessagingServiceTest, TestMultipleIncomingPushMessages) {
- base::HistogramTester histograms;
-
- const GURL origin(kTestOrigin);
- SetPermission(origin, CONTENT_SETTING_ALLOW);
-
- PushMessagingServiceImpl* push_service = profile()->GetPushMessagingService();
- ASSERT_TRUE(push_service);
-
- // Subscribe |origin| to push service.
- Subscribe(push_service, origin);
- PushMessagingAppIdentifier app_identifier =
- PushMessagingAppIdentifier::FindByServiceWorker(profile(), origin,
- kTestServiceWorkerId);
- ASSERT_FALSE(app_identifier.is_null());
-
- // Setup decrypted test message.
- gcm::IncomingMessage message;
- message.sender_id = kTestSenderId;
- message.raw_data = "testdata";
- message.decrypted = true;
-
- // Setup callbacks for dispatch and handled push events.
- auto dispatched_run_loop = std::make_unique<base::RunLoop>();
- auto handled_run_loop = std::make_unique<base::RunLoop>();
- PushMessagingServiceImpl::PushEventCallback handle_push_event;
-
- push_service->SetMessageDispatchedCallbackForTesting(
- base::BindLambdaForTesting(
- [&](const std::string& app_id, const GURL& origin,
- int64_t service_worker_registration_id,
- absl::optional<std::string> payload,
- PushMessagingServiceImpl::PushEventCallback callback) {
- handle_push_event = std::move(callback);
- dispatched_run_loop->Quit();
- }));
-
- push_service->SetMessageCallbackForTesting(
- base::BindLambdaForTesting([&]() { handled_run_loop->Quit(); }));
-
- // Simulate two incoming push messages at the same time.
- push_service->OnMessage(app_identifier.app_id(), message);
- push_service->OnMessage(app_identifier.app_id(), message);
-
- // First wait until we dispatched the first push message.
- dispatched_run_loop->Run();
- dispatched_run_loop = std::make_unique<base::RunLoop>();
- auto handled_first = std::move(handle_push_event);
- handle_push_event = PushMessagingServiceImpl::PushEventCallback();
-
- histograms.ExpectUniqueTimeSample("PushMessaging.CheckOriginForAbuseTime",
- base::Seconds(0),
- /*expected_bucket_count=*/1);
- histograms.ExpectUniqueTimeSample("PushMessaging.DeliverQueuedMessageTime",
- base::Seconds(0),
- /*expected_bucket_count=*/1);
-
- // Run all tasks until idle so we can verify that we don't dispatch the second
- // push message until the first one is handled.
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(handle_push_event);
-
- // Simulate handling the first push event takes some time.
- task_environment().FastForwardBy(kPushEventHandleTime);
-
- // Now signal that the first push event has been handled and wait until we
- // checked for visibility requirements.
- std::move(handled_first).Run(blink::mojom::PushEventStatus::SUCCESS);
- handled_run_loop->Run();
- handled_run_loop = std::make_unique<base::RunLoop>();
-
- histograms.ExpectUniqueTimeSample("PushMessaging.MessageHandledTime",
- kPushEventHandleTime,
- /*expected_bucket_count=*/1);
-
- // Simulate handling the second push event takes some time.
- task_environment().FastForwardBy(kPushEventHandleTime);
-
- // Now wait until we dispatched the second push message and handle it too.
- dispatched_run_loop->Run();
- std::move(handle_push_event).Run(blink::mojom::PushEventStatus::SUCCESS);
- handled_run_loop->Run();
-
- // Checking origins for abuse happens immediately on receiving a push message
- // one at a time. Both messages do that instantly in this test.
- histograms.ExpectTimeBucketCount("PushMessaging.CheckOriginForAbuseTime",
- base::Seconds(0),
- /*count=*/2);
- // Delivering messages should be done in series so the second message should
- // have waited for the first one to be handled.
- histograms.ExpectTimeBucketCount("PushMessaging.DeliverQueuedMessageTime",
- kPushEventHandleTime,
- /*count=*/1);
- // The total time from receiving until handling of the second message.
- histograms.ExpectTimeBucketCount("PushMessaging.MessageHandledTime",
- kPushEventHandleTime * 2,
- /*count=*/1);
-}
-
#if BUILDFLAG(IS_ANDROID)
class FCMRevocationTest : public PushMessagingServiceTest {
public:
diff --git a/chromium/chrome/browser/quick_delete/BUILD.gn b/chromium/chrome/browser/quick_delete/BUILD.gn
index 9aaaacba50f..e2427433cd2 100644
--- a/chromium/chrome/browser/quick_delete/BUILD.gn
+++ b/chromium/chrome/browser/quick_delete/BUILD.gn
@@ -5,36 +5,43 @@
import("//build/config/android/rules.gni")
android_library("java") {
+ srcjar_deps = [ ":jni_headers" ]
sources = [
"android/java/src/org/chromium/chrome/browser/quick_delete/QuickDeleteBridge.java",
"android/java/src/org/chromium/chrome/browser/quick_delete/QuickDeleteController.java",
"android/java/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDelegate.java",
"android/java/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegate.java",
+ "android/java/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMediator.java",
"android/java/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMetricsDelegate.java",
+ "android/java/src/org/chromium/chrome/browser/quick_delete/QuickDeleteProperties.java",
"android/java/src/org/chromium/chrome/browser/quick_delete/QuickDeleteTabsFilter.java",
+ "android/java/src/org/chromium/chrome/browser/quick_delete/QuickDeleteViewBinder.java",
]
deps = [
":java_resources",
"//base:base_java",
"//base:jni_java",
+ "//chrome/browser/browsing_data/android:java",
"//chrome/browser/flags:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/signin/services/android:java",
+ "//chrome/browser/sync/android:java",
"//chrome/browser/tab:java",
"//chrome/browser/tabmodel:java",
- "//chrome/browser/tabmodel:java",
"//chrome/browser/ui/android/layouts:java",
"//chrome/browser/ui/messages/android:java",
+ "//components/browser_ui/settings/android:java",
"//components/browser_ui/widget/android:java",
"//components/browsing_data/core:java",
"//components/embedder_support/android:util_java",
"//components/signin/public/android:java",
+ "//components/sync/android:sync_java",
"//content/public/android:content_full_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_annotation_annotation_jvm_java",
"//ui/android:ui_no_recycler_view_java",
]
- annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+
resources_package = "org.chromium.chrome.browser.quick_delete"
}
@@ -44,17 +51,19 @@ generate_jni("jni_headers") {
android_resources("java_resources") {
sources = [
- "android/java/res/drawable/quick_delete_animation.xml",
- "android/java/res/drawable/quick_delete_cookies_24dp.xml",
- "android/java/res/drawable/quick_delete_dialog_tab_icon_24dp.xml",
+ "android/java/res/drawable/quick_delete_cookies_40dp.xml",
+ "android/java/res/drawable/quick_delete_dialog_history_icon_40dp.xml",
+ "android/java/res/drawable/quick_delete_dialog_tab_icon_40dp.xml",
"android/java/res/drawable/quick_delete_trash_bin_icon_24dp.xml",
- "android/java/res/layout/quick_delete_animation_view.xml",
"android/java/res/layout/quick_delete_dialog.xml",
+ "android/java/res/values/dimens.xml",
+ "android/java/res/values/styles.xml",
]
deps = [
"//chrome/browser/ui/android/strings:ui_strings_grd",
"//components/browser_ui/settings/android:java_resources",
"//components/browser_ui/strings/android:browser_ui_strings_grd",
+ "//ui/android:ui_java_resources",
]
}
@@ -62,8 +71,10 @@ robolectric_library("junit") {
resources_package = "org.chromium.chrome.browser.quick_delete"
sources = [
"android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateUnitTest.java",
+ "android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMediatorTest.java",
"android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMetricsDelegateTest.java",
"android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteTabsFilterTest.java",
+ "android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteViewBinderTest.java",
]
deps = [
":java",
@@ -71,20 +82,25 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//chrome/browser/browsing_data/android:java",
"//chrome/browser/flags:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/signin/services/android:java",
+ "//chrome/browser/sync/android:java",
"//chrome/browser/tab:java",
"//chrome/browser/tabmodel:java",
"//chrome/browser/ui/messages/android:java",
"//chrome/test/android:chrome_java_integration_test_support",
"//chrome/test/android:chrome_java_test_support_common",
+ "//components/browser_ui/settings/android:java",
"//components/browser_ui/widget/android:java",
"//components/browsing_data/core:java",
"//components/embedder_support/android:util_java",
"//components/signin/public/android:java",
"//components/signin/public/android:signin_java_test_support",
+ "//components/sync/android:sync_java",
"//content/public/android:content_full_java",
+ "//content/public/test/android:content_java_test_support",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
@@ -98,6 +114,7 @@ android_library("javatests") {
resources_package = "org.chromium.chrome.browser.quick_delete"
sources = [
+ "android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteBridgeTest.java",
"android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteControllerTest.java",
"android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateTest.java",
]
@@ -107,21 +124,30 @@ android_library("javatests") {
":java_resources",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_app_java_resources",
"//chrome/android:chrome_java",
+ "//chrome/browser/browsing_data/android:java",
"//chrome/browser/flags:java",
+ "//chrome/browser/profiles/android:java",
"//chrome/browser/tab:java",
"//chrome/browser/tabmodel:java",
+ "//chrome/browser/ui/android/appmenu:java",
"//chrome/browser/ui/android/appmenu/test:test_support_java",
"//chrome/browser/ui/android/layouts:java",
"//chrome/browser/ui/android/layouts/test:java",
"//chrome/test/android:chrome_java_integration_test_support",
"//chrome/test/android:chrome_java_test_support_common",
"//components/browsing_data/core:java",
+ "//components/signin/public/android:java",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//net/android:net_java_test_support",
"//third_party/android_deps:espresso_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
+ "//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_core_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_java_test_support",
diff --git a/chromium/chrome/browser/readaloud/android/BUILD.gn b/chromium/chrome/browser/readaloud/android/BUILD.gn
index 60ca0633f15..c1d902500dd 100644
--- a/chromium/chrome/browser/readaloud/android/BUILD.gn
+++ b/chromium/chrome/browser/readaloud/android/BUILD.gn
@@ -6,12 +6,161 @@ import("//build/config/android/rules.gni")
android_library("java") {
sources = [
+ "java/src/org/chromium/chrome/browser/readaloud/PlayerController.java",
+ "java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java",
+ "java/src/org/chromium/chrome/browser/readaloud/ReadAloudFeatures.java",
+ "java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonController.java",
+ "java/src/org/chromium/chrome/browser/readaloud/miniplayer/MiniPlayerCoordinator.java",
+ "java/src/org/chromium/chrome/browser/readaloud/miniplayer/MiniPlayerMediator.java",
+ "java/src/org/chromium/chrome/browser/readaloud/miniplayer/MiniPlayerProperties.java",
+ "java/src/org/chromium/chrome/browser/readaloud/miniplayer/MiniPlayerViewBinder.java",
+ ]
+ deps = [
+ ":expanded_player_java",
+ ":hooks_java",
+ ":java_resources",
+ ":player_state_java",
+ "//base:base_java",
+ "//chrome/android:chrome_app_java_resources",
+ "//chrome/android/modules/readaloud/public:java",
+ "//chrome/browser/flags:java",
+ "//chrome/browser/language/android:java",
+ "//chrome/browser/profiles/android:java",
+ "//chrome/browser/signin/services/android:java",
+ "//chrome/browser/tab:java",
+ "//chrome/browser/tabmodel:java",
+ "//chrome/browser/ui/android/strings:ui_strings_grd",
+ "//chrome/browser/ui/android/toolbar:java",
+ "//chrome/browser/user_education:java",
+ "//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/bottomsheet/android:java",
+ "//components/embedder_support/android:util_java",
+ "//components/feature_engagement/public:public_java",
+ "//third_party/android_deps:guava_android_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ "//ui/android:ui_java",
+ "//ui/android:ui_java_resources",
+ "//url:gurl_java",
+ ]
+ resources_package = "org.chromium.chrome.browser.readaloud"
+}
+
+android_resources("java_resources") {
+ sources = [
+ "java/res/drawable/baseline_close_24.xml",
+ "java/res/drawable/baseline_forward_30_24.xml",
+ "java/res/drawable/baseline_more_horiz_24.xml",
+ "java/res/drawable/baseline_replay_10_24.xml",
+ "java/res/drawable/forward_30_button.xml",
+ "java/res/drawable/mini_play_button.xml",
+ "java/res/drawable/pause_button.xml",
+ "java/res/drawable/play_button.xml",
+ "java/res/drawable/primary_gray_rounded.xml",
+ "java/res/drawable/replay_10_button.xml",
+ "java/res/drawable/secondary_gray_rounded.xml",
+ "java/res/layout/readaloud_expanded_player_layout.xml",
+ "java/res/layout/readaloud_mini_player_layout.xml",
+ "java/res/values/colors.xml",
+ "java/res/values/dimens.xml",
+ ]
+ deps = [
+ "//chrome/browser/ui/android/strings:ui_strings_grd",
+ "//components/browser_ui/styles/android:java_resources",
+ "//components/browser_ui/widget/android:java_resources",
+ "//ui/android:ui_java_resources",
+ ]
+}
+
+android_library("hooks_java") {
+ sources = [
"java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooks.java",
"java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksImpl.java",
]
deps = [
- "//third_party/android_deps:com_google_guava_listenablefuture_java",
+ "//base:base_java",
"//third_party/android_deps:guava_android_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ "//url:gurl_java",
]
jar_excluded_patterns = [ "*/ReadAloudReadabilityHooksImpl*.class" ]
}
+
+android_library("hooks_public_impl_java") {
+ sources = [ "java/src/org/chromium/chrome/browser/readaloud/ReadAloudReadabilityHooksImpl.java" ]
+
+ deps = [
+ ":hooks_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ ]
+}
+
+robolectric_library("junit") {
+ testonly = true
+ sources = [
+ "java/src/org/chromium/chrome/browser/readaloud/PlayerControllerUnitTest.java",
+ "java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java",
+ "java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonControllerUnitTest.java",
+ "java/src/org/chromium/chrome/browser/readaloud/expandedplayer/ExpandedPlayerCoordinatorUnitTest.java",
+ "java/src/org/chromium/chrome/browser/readaloud/expandedplayer/ExpandedPlayerMediatorUnitTest.java",
+ "java/src/org/chromium/chrome/browser/readaloud/expandedplayer/ExpandedPlayerSheetContentUnitTest.java",
+ "java/src/org/chromium/chrome/browser/readaloud/miniplayer/MiniPlayerCoordinatorUnitTest.java",
+ "java/src/org/chromium/chrome/browser/readaloud/miniplayer/MiniPlayerMediatorUnitTest.java",
+ ]
+ deps = [
+ ":expanded_player_java",
+ ":hooks_java",
+ ":java",
+ ":java_resources",
+ ":player_state_java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//base:base_junit_test_support",
+ "//chrome/android/modules/readaloud/public:java",
+ "//chrome/browser/flags:java",
+ "//chrome/browser/profiles/android:java",
+ "//chrome/browser/signin/services/android:java",
+ "//chrome/browser/tab:java",
+ "//chrome/browser/tabmodel:java",
+ "//chrome/browser/ui/android/toolbar:java",
+ "//chrome/test/android:chrome_java_unit_test_support",
+ "//components/browser_ui/bottomsheet/android:java",
+ "//components/feature_engagement/public:public_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_test_core_java",
+ "//third_party/androidx:androidx_test_ext_junit_java",
+ "//third_party/junit",
+ "//third_party/mockito:mockito_java",
+ "//ui/android:ui_java",
+ "//ui/android:ui_java_test_support",
+ "//url:gurl_java",
+ "//url:gurl_junit_test_support",
+ ]
+}
+
+android_library("expanded_player_java") {
+ sources = [
+ "java/src/org/chromium/chrome/browser/readaloud/expandedplayer/ExpandedPlayerCoordinator.java",
+ "java/src/org/chromium/chrome/browser/readaloud/expandedplayer/ExpandedPlayerMediator.java",
+ "java/src/org/chromium/chrome/browser/readaloud/expandedplayer/ExpandedPlayerProperties.java",
+ "java/src/org/chromium/chrome/browser/readaloud/expandedplayer/ExpandedPlayerSheetContent.java",
+ "java/src/org/chromium/chrome/browser/readaloud/expandedplayer/ExpandedPlayerViewBinder.java",
+ ]
+ deps = [
+ ":player_state_java",
+ "//base:base_java",
+ "//chrome/android:chrome_app_java_resources",
+ "//chrome/android/modules/readaloud/public:java",
+ "//chrome/browser/ui/android/strings:ui_strings_grd",
+ "//components/browser_ui/bottomsheet/android:java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ "//ui/android:ui_java",
+ "//ui/android:ui_java_resources",
+ ]
+ resources_package = "org.chromium.chrome.browser.readaloud.expandedplayer"
+}
+
+android_library("player_state_java") {
+ sources =
+ [ "java/src/org/chromium/chrome/browser/readaloud/PlayerState.java" ]
+ deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
+}
diff --git a/chromium/chrome/browser/recent_tabs/BUILD.gn b/chromium/chrome/browser/recent_tabs/BUILD.gn
index 745dbc1d07b..daa8caf02d2 100644
--- a/chromium/chrome/browser/recent_tabs/BUILD.gn
+++ b/chromium/chrome/browser/recent_tabs/BUILD.gn
@@ -69,6 +69,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//base/test:test_support_java",
"//chrome/browser/feature_engagement:java",
"//chrome/browser/profiles/android:java",
@@ -84,6 +85,7 @@ robolectric_library("junit") {
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_java",
+ "//url:gurl_java",
"//url:gurl_junit_test_support",
]
}
diff --git a/chromium/chrome/browser/recent_tabs/internal/BUILD.gn b/chromium/chrome/browser/recent_tabs/internal/BUILD.gn
index 7ce070a0c8e..2493c7b54b3 100644
--- a/chromium/chrome/browser/recent_tabs/internal/BUILD.gn
+++ b/chromium/chrome/browser/recent_tabs/internal/BUILD.gn
@@ -115,6 +115,7 @@ robolectric_library("junit") {
":java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//base/test:test_support_java",
"//chrome/browser/feature_engagement:java",
"//chrome/browser/flags:java",
@@ -130,22 +131,26 @@ robolectric_library("junit") {
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_java",
+ "//url:gurl_java",
"//url:gurl_junit_test_support",
]
}
-android_library("javatests") {
+android_library("recent_tabs_javatests") {
testonly = true
sources = [ "android/java/src/org/chromium/chrome/browser/recent_tabs/RestoreTabsTest.java" ]
deps = [
":java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
+ "//chrome/android:chrome_test_java",
"//chrome/android/features/tab_ui:test_support_javalib",
"//chrome/browser/feature_engagement:java",
"//chrome/browser/flags:java",
@@ -154,14 +159,18 @@ android_library("javatests") {
"//chrome/browser/recent_tabs:java",
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/feature_engagement/public:public_java",
"//components/sync_device_info:sync_device_info_java",
"//third_party/android_deps:espresso_java",
+ "//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_java_test_support",
+ "//url:gurl_java",
"//url:gurl_junit_test_support",
]
}
@@ -174,8 +183,10 @@ android_library("unit_device_javatests") {
deps = [
":java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/recent_tabs:java",
+ "//chrome/browser/tabmodel:java",
"//chrome/browser/ui/android/favicon:java",
"//chrome/test/android:chrome_java_test_support_common",
"//components/browser_ui/bottomsheet/android:java",
@@ -185,10 +196,12 @@ android_library("unit_device_javatests") {
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_full_java",
"//ui/android:ui_java_test_support",
+ "//url:gurl_java",
"//url:gurl_junit_test_support",
]
}
diff --git a/chromium/chrome/browser/renderer_host/BUILD.gn b/chromium/chrome/browser/renderer_host/BUILD.gn
index e7d32781970..e37237b0042 100644
--- a/chromium/chrome/browser/renderer_host/BUILD.gn
+++ b/chromium/chrome/browser/renderer_host/BUILD.gn
@@ -13,8 +13,6 @@ source_set("history_swiper") {
"chrome_render_widget_host_view_mac_history_swiper.mm",
]
- configs += [ "//build/config/compiler:enable_arc" ]
-
frameworks = [ "Cocoa.framework" ]
deps = [
diff --git a/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.mm b/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.mm
index 4ce94104c95..9880a97b8af 100644
--- a/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.mm
+++ b/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.mm
@@ -30,10 +30,6 @@
#include "mojo/public/cpp/bindings/remote.h"
#include "services/service_manager/public/cpp/interface_provider.h"
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
@interface ChromeRenderWidgetHostViewMacDelegate () <HistorySwiperDelegate>
@property(readonly) content::WebContents* webContents;
diff --git a/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm b/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm
index 31eb4ebfd4f..d1bb9c4a0c6 100644
--- a/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm
+++ b/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm
@@ -9,10 +9,6 @@
#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
#include "ui/events/blink/did_overscroll_params.h"
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
namespace {
// The horizontal distance required to cause the browser to perform a history
diff --git a/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm b/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm
index b0a49340a52..688573dd558 100644
--- a/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm
+++ b/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm
@@ -4,9 +4,9 @@
#include <Cocoa/Cocoa.h>
+#include "base/apple/scoped_cftyperef.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
-#include "base/mac/scoped_cftyperef.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/test/simple_test_tick_clock.h"
@@ -26,10 +26,6 @@
#include "ui/events/base_event_utils.h"
#include "url/gurl.h"
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
namespace {
// Refers to how the event is going to be sent to the NSView. There are 3
@@ -150,8 +146,9 @@ class ChromeRenderWidgetHostViewMacHistorySwiperTest
// Creates a mock scroll wheel event that is backed by a real CGEvent.
id MockScrollWheelEvent(NSPoint delta, NSEventType type) {
- base::ScopedCFTypeRef<CGEventRef> cg_event(CGEventCreateScrollWheelEvent(
- nullptr, kCGScrollEventUnitLine, 2, 0, 0));
+ base::apple::ScopedCFTypeRef<CGEventRef> cg_event(
+ CGEventCreateScrollWheelEvent(nullptr, kCGScrollEventUnitLine, 2, 0,
+ 0));
CGEventSetIntegerValueField(cg_event, kCGScrollWheelEventIsContinuous, 1);
CGEventSetIntegerValueField(
cg_event, kCGScrollWheelEventPointDeltaAxis2, delta.x);
diff --git a/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm b/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm
index 12449ba9026..395a4c3754d 100644
--- a/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm
+++ b/chromium/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm
@@ -12,10 +12,6 @@
#import "third_party/ocmock/ocmock_extensions.h"
#include "ui/events/blink/did_overscroll_params.h"
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
@interface HistorySwiper (MacHistorySwiperTest)
- (BOOL)browserCanNavigateInDirection:
(history_swiper::NavigationDirection)forward
diff --git a/chromium/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc b/chromium/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
index 25691195a1c..1132262c856 100644
--- a/chromium/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
+++ b/chromium/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
@@ -26,9 +26,6 @@
#include "content/public/browser/child_process_launcher_utils.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/render_widget_host.h"
-#include "content/public/browser/render_widget_host_iterator.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"
@@ -46,8 +43,6 @@
#include "content/public/browser/browser_child_process_host.h"
#endif // BUILDFLAG(IS_MAC)
-using content::RenderViewHost;
-using content::RenderWidgetHost;
using content::WebContents;
namespace {
@@ -57,18 +52,13 @@ int RenderProcessHostCount() {
}
WebContents* FindFirstDevToolsContents() {
- std::unique_ptr<content::RenderWidgetHostIterator> widgets(
- RenderWidgetHost::GetRenderWidgetHosts());
- while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
- if (!widget->GetProcess()->IsInitializedAndNotDead())
- continue;
- RenderViewHost* view_host = RenderViewHost::From(widget);
- if (!view_host)
- continue;
- WebContents* contents = WebContents::FromRenderViewHost(view_host);
- GURL url = contents->GetURL();
- if (url.SchemeIs(content::kChromeDevToolsScheme))
- return contents;
+ for (content::WebContents* web_contents : content::GetAllWebContents()) {
+ if (web_contents->GetURL().SchemeIs(content::kChromeDevToolsScheme) &&
+ web_contents->GetPrimaryMainFrame()
+ ->GetProcess()
+ ->IsInitializedAndNotDead()) {
+ return web_contents;
+ }
}
return nullptr;
}
@@ -89,6 +79,17 @@ base::Process ProcessFromHandle(base::ProcessHandle handle) {
return base::Process(handle);
}
+// Returns true if the priority of `process` is kBestEffort.
+bool IsProcessBackgrounded(const base::Process& process) {
+#if BUILDFLAG(IS_MAC)
+ return process.GetPriority(
+ content::BrowserChildProcessHost::GetPortProvider()) ==
+ base::Process::Priority::kBestEffort;
+#else
+ return process.GetPriority() == base::Process::Priority::kBestEffort;
+#endif
+}
+
} // namespace
class ChromeRenderProcessHostTest : public extensions::ExtensionBrowserTest {
@@ -383,17 +384,10 @@ class ChromeRenderProcessHostBackgroundingTest
EXPECT_TRUE(process->IsInitializedAndNotDead());
EXPECT_EQ(expected_is_backgrounded, process->IsProcessBackgrounded());
- if (base::Process::CanBackgroundProcesses()) {
+ if (base::Process::CanSetPriority()) {
base::Process p = ProcessFromHandle(process->GetProcess().Handle());
ASSERT_TRUE(p.IsValid());
-#if BUILDFLAG(IS_MAC)
- base::PortProvider* port_provider =
- content::BrowserChildProcessHost::GetPortProvider();
- EXPECT_EQ(expected_is_backgrounded,
- p.IsProcessBackgrounded(port_provider));
-#else
- EXPECT_EQ(expected_is_backgrounded, p.IsProcessBackgrounded());
-#endif
+ EXPECT_EQ(expected_is_backgrounded, IsProcessBackgrounded(p));
}
}
};
@@ -642,8 +636,17 @@ class ChromeRenderProcessHostBackgroundingTestWithAudio
: public ChromeRenderProcessHostTest {
public:
ChromeRenderProcessHostBackgroundingTestWithAudio() {
- // Tests require that each tab has a different process.
- feature_list_.InitAndEnableFeature(features::kDisableProcessReuse);
+ feature_list_.InitWithFeatures(
+ /*enabled_features=*/
+ {
+ // Tests require that each tab has a different process.
+ features::kDisableProcessReuse,
+#if BUILDFLAG(IS_MAC)
+ // Tests require that backgrounding processes is possible.
+ features::kMacAllowBackgroundingRenderProcesses,
+#endif
+ },
+ /*disabled_features=*/{});
}
ChromeRenderProcessHostBackgroundingTestWithAudio(
@@ -691,9 +694,6 @@ class ChromeRenderProcessHostBackgroundingTestWithAudio
ASSERT_NE(audio_process_.Pid(), no_audio_process_.Pid());
ASSERT_TRUE(no_audio_process_.IsValid());
ASSERT_TRUE(audio_process_.IsValid());
-#if BUILDFLAG(IS_MAC)
- port_provider_ = content::BrowserChildProcessHost::GetPortProvider();
-#endif // BUILDFLAG(IS_MAC)
}
protected:
@@ -714,31 +714,21 @@ class ChromeRenderProcessHostBackgroundingTestWithAudio
base::Process audio_process_;
base::Process no_audio_process_;
- raw_ptr<content::WebContents, DanglingUntriaged> audio_tab_web_contents_;
+ raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged>
+ audio_tab_web_contents_;
private:
- bool IsProcessBackgrounded(const base::Process& process) {
-#if BUILDFLAG(IS_MAC)
- return process.IsProcessBackgrounded(port_provider_);
-#else
- return process.IsProcessBackgrounded();
-#endif
- }
-
base::test::ScopedFeatureList feature_list_;
-
-#if BUILDFLAG(IS_MAC)
- raw_ptr<base::PortProvider> port_provider_;
-#endif
};
// Test to make sure that a process is backgrounded when the audio stops playing
// from the active tab and there is an immediate tab switch.
IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostBackgroundingTestWithAudio,
ProcessPriorityAfterStoppedAudio) {
- // This test is invalid on platforms that can't background.
- if (!base::Process::CanBackgroundProcesses())
+ // This test is invalid on platforms that can't set priority.
+ if (!base::Process::CanSetPriority()) {
return;
+ }
ShowSingletonTab(audio_url_);
@@ -760,9 +750,10 @@ IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostBackgroundingTestWithAudio,
// stops playing from a hidden tab.
IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostBackgroundingTestWithAudio,
ProcessPriorityAfterAudioStopsOnNotVisibleTab) {
- // This test is invalid on platforms that can't background.
- if (!base::Process::CanBackgroundProcesses())
+ // This test is invalid on platforms that can't set priority.
+ if (!base::Process::CanSetPriority()) {
return;
+ }
// Wait until the two pages are not backgrounded.
WaitUntilBackgrounded(audio_process_, false, no_audio_process_, false);
@@ -782,8 +773,9 @@ IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostBackgroundingTestWithAudio,
IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostBackgroundingTestWithAudio,
ProcessPriorityAfterAudioStartsFromBackgroundTab) {
// This test is invalid on platforms that can't background.
- if (!base::Process::CanBackgroundProcesses())
+ if (!base::Process::CanSetPriority()) {
return;
+ }
// Stop the audio.
ASSERT_TRUE(
diff --git a/chromium/chrome/browser/resources/BUILD.gn b/chromium/chrome/browser/resources/BUILD.gn
index c8d86508fb8..fdb7af66903 100644
--- a/chromium/chrome/browser/resources/BUILD.gn
+++ b/chromium/chrome/browser/resources/BUILD.gn
@@ -7,7 +7,6 @@ import("//build/config/chromeos/ui_mode.gni")
import("//chrome/browser/buildflags.gni")
import("//chrome/common/features.gni")
import("//chrome/test/base/js2gtest.gni")
-import("//chrome/test/include_js_tests.gni")
import("//components/signin/features.gni")
import("//pdf/features.gni")
import("//printing/buildflags/buildflags.gni")
@@ -33,6 +32,7 @@ group("resources") {
"feed:resources",
"feedback:resources",
"gaia_auth_host:resources",
+ "hats:resources",
"history:resources",
"identity_internals:resources",
"inline_login:resources",
@@ -74,10 +74,10 @@ group("resources") {
if (is_chromeos_ash) {
public_deps += [
+ "ash/settings:resources",
"chromeos:resources",
"nearby_internals:resources",
"nearby_share:resources",
- "settings/chromeos:resources",
]
}
@@ -101,7 +101,6 @@ group("resources") {
public_deps += [
"app_home:resources",
"app_settings:resources",
- "ntp4:resources",
]
}
@@ -117,8 +116,8 @@ group("resources") {
]
}
- if (enable_waffle_desktop) {
- public_deps += [ "waffle:resources" ]
+ if (enable_search_engine_choice) {
+ public_deps += [ "search_engine_choice:resources" ]
}
if (enable_webui_tab_strip) {
@@ -248,6 +247,10 @@ group("dev_ui_resources") {
"//content/browser/webrtc/resources",
]
+ if (enable_vr) {
+ public_deps += [ "//content/browser/resources/webxr_internals:resources" ]
+ }
+
if (is_android) {
public_deps += [
"//chrome/browser/resources/feed_internals:resources",
@@ -295,6 +298,10 @@ repack("dev_ui_paks") {
"$root_gen_dir/content/service_worker_resources.pak",
]
+ if (enable_vr) {
+ sources += [ "$root_gen_dir/content/webxr_internals_resources.pak" ]
+ }
+
if (is_android) {
sources += [
"$root_gen_dir/chrome/feed_internals_resources.pak",
diff --git a/chromium/chrome/browser/resources/access_code_cast/index.html b/chromium/chrome/browser/resources/access_code_cast/index.html
index ede792cba8f..0137b02e57b 100644
--- a/chromium/chrome/browser/resources/access_code_cast/index.html
+++ b/chromium/chrome/browser/resources/access_code_cast/index.html
@@ -1,5 +1,5 @@
<!DOCTYPE html>
-<html lang="en">
+<html lang="en" $i18n{chromeRefresh2023Attribute}>
<head>
<meta charset="utf-8">
diff --git a/chromium/chrome/browser/resources/accessibility/BUILD.gn b/chromium/chrome/browser/resources/accessibility/BUILD.gn
index d803aa5c565..2e4afbc6833 100644
--- a/chromium/chrome/browser/resources/accessibility/BUILD.gn
+++ b/chromium/chrome/browser/resources/accessibility/BUILD.gn
@@ -6,6 +6,7 @@ import("//build/config/chromeos/ui_mode.gni")
import("//ui/webui/resources/tools/build_webui.gni")
if (is_chromeos_lacros) {
import("//chrome/browser/resources/chromeos/accessibility/manifest.gni")
+ import("strings/extension_strings.gni")
}
build_webui("build") {
@@ -27,6 +28,7 @@ if (is_chromeos_lacros) {
deps = [
":chromevox_helper_manifest",
":embedded_a11y_helper_manifest",
+ ":extension_strings",
"chromevox_helper:build",
"embedded_a11y_helper:build",
]
@@ -53,4 +55,8 @@ if (is_chromeos_lacros) {
# Providing the key in manifest.json will keep the extension ID stable.
key = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDG3J14i99Qtu/QZ5zsJl4Z7EHaG+ndaVIajg91rxleFu3YpuJqS3u3aOE/FCjIOUp6uqn/e5l+8As3RK6NBNiQzGVXZn/5orW/iaRUk2kflUNDCrBjdgvgE5TGgjtfCuHJITc/L184ss2GKBkWrcUWw0ZR9YSTzZ1AeEfFfLT0e0Ic3u4BnM82gAJKpDV6dmvR9rcWs8yhume6kXB7hLMY9pAtBOKlx8vhHo0QAiDHK/9hxDyNIHcgKsCN4jpUn2odIBCA+dfHPlHshztqAW6FkuC91dUP5PH3QS0Z+ZB8uCm0viEXa/twH3KEE+BSkDtThTNX1lDTAT+rilQlSECpAgMBAAECggEADRma6QZD0Ygfj6nHsVnMsKX655CXrR2DSlsJ4y2D/QoDJJbHywtKbNhkoDhmgFwfzyptSPZ+M9nBz2P/oKwNTSqd0W5f51TD5sh9MhIc5nb5fZjeakrsONhpYFNTcG0h1xNQVaH8AprqxZfuISuU1G1MPFlxYKA2p/jUd4rBy+oWBHZrxaThVUdIFENkkDQQsGaCZlegF+B4ma0Mym1i9tqKrCDJuFiCXl6tBQ8iQSW/z7+ekpanVLtAdeb98W/0DDsHB9VFLgfkEynNh8PGro+TrSjmBZRQIxZu9aTFjL1bMbj2pf3oU0r5RqRRRH9Q1w84I9vHxfXkhoqL9yh5gQKBgQDhs+tnYNQAg2ecz9sYvsOA8GQMutfT8HeFslQDsWhs7GDkMbkW5e+HKbONtZKOTlYd6hUh7R0X7aDvaaJWhTfVyesEp65a/V7t5SrrVGE24po+x+VWDT9vgvw3EcGifEgq5kVxvY5Gco2Iw/tEwyQf29ryE7sawcAVhGcEXIO96QKBgQDhjlOtSS3KpKZRQbbBWc6G0cnDpJmyMCr5B9xEz1Fs7OJuI70uRzxkvni/AIhPYCciiAJ04KnDmBReK56600emGm0FoYIZZ1x8gBi3s7VUiervkQLxCi/hko0X06ro8irUTPywELR92ORRKFHk2PXg8t2rHxR87Yh4Ws+dENT0wQKBgH+1yZ6+SFdeA1PUvTmcP42G+GXHl3ZCG69X/3fze44elpiolf2h784AR6wIsLu7EbltibWfsJWojSbLE+N3D+f1j/kbVGoB8iQURxvPrA5dXD/n5hmx91IGwOX8Mx/YyREVKPg59PZhvTpFArme3aL1SUcu6PEz7pd+SrhKDPV5AoGBAJOXkmGdtP0Y6EvbeD7lCSNnO2Nt3bSfmD8ESbos/tcL2s6/TBdwPbCeglxZeNiXzoPmA9V+/Rcj+I/2cxNFFWk3eYdpu7vryrUdDQ+H5GvBI55HgWlAhTxRrUmeFAQCEsyA5AhBphmDR0Nj4tKgtaPQyAQlfj6RH+/BXIEmdnMBAoGBANlUyyA0mcyX/FgcYB27ul2dpcicrqjIQi3lk4LDzdXJp5Qm/P+PUl7PbLrf1UizIv4q/89uyp3zuX3sFw6ISdYExC5Xh4mC4naSEnysQgA/c/329kXdSjCNVHEKkm3ngoDTh1ZT+kU4x/PAEBIrW5Q1lQguYLCBMdsLivuvqnh8"
}
+
+ extension_strings("extension_strings") {
+ out_dir = accessibility_out_dir
+ }
}
diff --git a/chromium/chrome/browser/resources/accessibility/chromevox_helper/BUILD.gn b/chromium/chrome/browser/resources/accessibility/chromevox_helper/BUILD.gn
index 3d142e33d76..77aa5120c9a 100644
--- a/chromium/chrome/browser/resources/accessibility/chromevox_helper/BUILD.gn
+++ b/chromium/chrome/browser/resources/accessibility/chromevox_helper/BUILD.gn
@@ -14,19 +14,24 @@ group("build") {
}
ts_library("build_ts") {
- in_files = [ "content.ts" ]
+ in_files = [
+ "content.ts",
+ "service_worker.ts",
+ ]
out_dir = tsc_output_dir
definitions = [
"//tools/typescript/definitions/runtime.d.ts",
+ "//tools/typescript/definitions/scripting.d.ts",
+ "//tools/typescript/definitions/storage.d.ts",
"//tools/typescript/definitions/tabs.d.ts",
+ "//tools/typescript/definitions/windows.d.ts",
]
}
copy("chromevox_helper_copied_files") {
- sources = [
- "$tsc_output_dir/content.js",
- "../../chromeos/accessibility/chromevox/injected/cvox_gdocs_script.js",
- ]
+ sources =
+ filter_include(get_target_outputs(":build_ts"), [ "*.js" ]) +
+ [ "../../chromeos/accessibility/chromevox/injected/cvox_gdocs_script.js" ]
outputs = [ "$out_dir/{{source_file_part}}" ]
deps = [ ":build_ts" ]
}
diff --git a/chromium/chrome/browser/resources/accessibility/chromevox_helper/service_worker.ts b/chromium/chrome/browser/resources/accessibility/chromevox_helper/service_worker.ts
new file mode 100644
index 00000000000..a01e7044291
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/chromevox_helper/service_worker.ts
@@ -0,0 +1,56 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+const INITIALIZED_KEY: string = 'cvox_initialized';
+
+// Handles interacting with the Lacros browser via extension APIs
+// for ChromeVox in Ash.
+
+async function main() {
+ // Inject script into all gdocs pages if not already initialized.
+ // For any new tabs that are opened after the ChromeVox helper started
+ // running, the manifest will handle content script injection, so there's
+ // no need to listen for any changes: This only has to be done one-time.
+ // Use session storage to ensure this is done once even if the service
+ // worker wakes up again later.
+ const initialized = await chrome.storage.session.get([INITIALIZED_KEY]);
+ if (initialized && INITIALIZED_KEY in initialized) {
+ return;
+ }
+ const storageUpdate = {[INITIALIZED_KEY]: true};
+ chrome.storage.session.set(storageUpdate);
+
+ // Inject into already-active tabs.
+ let matches = [];
+ try {
+ matches = chrome.runtime.getManifest().content_scripts![0]!.matches!;
+ } catch (e) {
+ throw new Error('Unable to find content script matches entry in manifest.');
+ }
+
+ // Build one large regexp.
+ const docsRe = new RegExp(matches.join('|'));
+
+ const windows = await chrome.windows.getAll({'populate': true});
+ for (const window of windows) {
+ if (!window.tabs) {
+ continue;
+ }
+ const tabs = window.tabs.filter(tab => docsRe.test(tab.url!));
+ for (const tab of tabs) {
+ chrome.scripting
+ .executeScript({
+ target: {tabId: tab.id!, allFrames: true},
+ files: ['chromevox_helper/cvox_gdocs_script.js'],
+ })
+ .then(() => {
+ if (chrome.runtime.lastError) {
+ console.error('Could not inject into tab ', tab);
+ }
+ });
+ }
+ }
+}
+
+main();
diff --git a/chromium/chrome/browser/resources/accessibility/chromevox_helper_manifest.json.jinja2 b/chromium/chrome/browser/resources/accessibility/chromevox_helper_manifest.json.jinja2
index ca9f3ecfb1e..1e04bece0f7 100644
--- a/chromium/chrome/browser/resources/accessibility/chromevox_helper_manifest.json.jinja2
+++ b/chromium/chrome/browser/resources/accessibility/chromevox_helper_manifest.json.jinja2
@@ -6,8 +6,14 @@
"name": "ChromeVox Helper",
"version": "{{set_version}}",
"description": "Provides support for ChromeVox within non-Ash browsers",
+ "background": {
+ "service_worker": "chromevox_helper/service_worker.js",
+ "type":"module"
+ },
"permissions": [
- "scripting"
+ "tabs",
+ "scripting",
+ "storage"
],
"incognito": "split",
"content_scripts": [
diff --git a/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper/BUILD.gn b/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper/BUILD.gn
index c1289773a39..d45e8919c23 100644
--- a/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper/BUILD.gn
+++ b/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper/BUILD.gn
@@ -14,19 +14,30 @@ group("build") {
}
ts_library("build_ts") {
- in_files = [ "content.ts" ]
+ in_files = [
+ "content.ts",
+ "service_worker.ts",
+ ]
out_dir = tsc_output_dir
definitions = [
+ "//tools/typescript/definitions/chrome_event.d.ts",
"//tools/typescript/definitions/runtime.d.ts",
"//tools/typescript/definitions/tabs.d.ts",
+ "//tools/typescript/definitions/storage.d.ts",
+ "//tools/typescript/definitions/accessibility_features.d.ts",
+ "//tools/typescript/definitions/accessibility_service_private.d.ts",
+ "//tools/typescript/definitions/context_menus.d.ts",
+ "//tools/typescript/definitions/i18n.d.ts",
]
}
copy("embedded_a11y_helper_copied_files") {
- sources = [
- "$tsc_output_dir/content.js",
- "../../chromeos/accessibility/common/gdocs_script.js",
- ]
+ sources = filter_include(get_target_outputs(":build_ts"), [ "*.js" ]) + [
+ "../../chromeos/accessibility/common/gdocs_script.js",
+ "../../chromeos/accessibility/select_to_speak/sts-icon-128.png",
+ "../../chromeos/accessibility/select_to_speak/sts-icon-16.png",
+ "../../chromeos/accessibility/select_to_speak/sts-icon-48.png",
+ ]
outputs = [ "$out_dir/{{source_file_part}}" ]
deps = [ ":build_ts" ]
}
diff --git a/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper/service_worker.ts b/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper/service_worker.ts
new file mode 100644
index 00000000000..042da444b97
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper/service_worker.ts
@@ -0,0 +1,58 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+const INITIALIZED_KEY: string = 'sts_initialized';
+// During development, chrome.accessibilityServicePrivate is behind a feature
+// flag.
+const SHOW_CONTEXT_MENU = chrome.accessibilityServicePrivate !== undefined;
+
+async function selectToSpeakContextMenusCallback() {
+ // Inform Lacros of the context menu click.
+ if (SHOW_CONTEXT_MENU) {
+ chrome.accessibilityServicePrivate.speakSelectedText();
+ }
+}
+
+async function onSelectToSpeakChanged(
+ details: chrome.accessibilityFeatures.ChromeSettingsResponse) {
+ if (details.value) {
+ const initialized = await chrome.storage.session.get([INITIALIZED_KEY]);
+ if (initialized && initialized[INITIALIZED_KEY] === true) {
+ return;
+ }
+ const storageUpdate = {[INITIALIZED_KEY]: true};
+ chrome.storage.session.set(storageUpdate);
+
+ // Add a context menu item to selection contexts.
+ if (SHOW_CONTEXT_MENU) {
+ await chrome.contextMenus.create({
+ title: chrome.i18n.getMessage(
+ 'select_to_speak_listen_context_menu_option_text'),
+ contexts: [chrome.contextMenus.ContextType.SELECTION],
+ id: 'embedded_a11y_helper',
+ });
+ }
+ return;
+ }
+
+ // Clear the context menu if there was one.
+ chrome.contextMenus.removeAll();
+ const storageUpdate = {[INITIALIZED_KEY]: false};
+ chrome.storage.session.set(storageUpdate);
+}
+
+async function main() {
+ chrome.contextMenus.onClicked.addListener(selectToSpeakContextMenusCallback);
+
+ // Set up based on current state.
+ const currentDetails =
+ await chrome.accessibilityFeatures.selectToSpeak.get({});
+ await onSelectToSpeakChanged(currentDetails);
+
+ // Add a listener for future changes.
+ chrome.accessibilityFeatures.selectToSpeak.onChange.addListener(
+ details => onSelectToSpeakChanged(details));
+}
+
+main();
diff --git a/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper_manifest.json.jinja2 b/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper_manifest.json.jinja2
index 5106ce08f1f..30a41ed1971 100644
--- a/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper_manifest.json.jinja2
+++ b/chromium/chrome/browser/resources/accessibility/embedded_a11y_helper_manifest.json.jinja2
@@ -6,10 +6,24 @@
"name": "Accessibility Helper",
"version": "{{set_version}}",
"description": "Provides support for Ash accessibility features from non-Ash browsers",
+ "default_locale": "en",
+ "background": {
+ "service_worker": "embedded_a11y_helper/service_worker.js",
+ "type":"module"
+ },
"permissions": [
- "scripting"
+ "accessibilityFeatures.read",
+ "accessibilityServicePrivate",
+ "contextMenus",
+ "scripting",
+ "storage"
],
"incognito": "split",
+ "icons": {
+ "128": "embedded_a11y_helper/sts-icon-128.png",
+ "16": "embedded_a11y_helper/sts-icon-16.png",
+ "48": "embedded_a11y_helper/sts-icon-48.png"
+ },
"content_scripts": [
{
"matches": [ "https://docs.google.com/document*",
@@ -32,4 +46,4 @@
"https://docs.sandbox.google.com/*" ]
}
]
-} \ No newline at end of file
+}
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings.gni b/chromium/chrome/browser/resources/accessibility/strings/extension_strings.gni
new file mode 100644
index 00000000000..65367c1ab21
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings.gni
@@ -0,0 +1,33 @@
+# Copyright 2020 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/locales.gni")
+import("//chrome/common/features.gni")
+import("//tools/grit/grit_rule.gni")
+
+# Same as the locales list but in the format the extension system expects:
+# it uses underscores instead of hyphens, and "en" instead of "en-US".
+# Very similar to locales_as_apple_outputs, except pseudolocales are not included.
+accessibility_locales = []
+foreach(locale, locales_without_pseudolocales) {
+ if (locale == "en-US") {
+ accessibility_locales += [ "en" ]
+ } else {
+ accessibility_locales += [ string_replace(locale, "-", "_") ]
+ }
+}
+
+template("extension_strings") {
+ grit(target_name) {
+ source =
+ "//chrome/browser/resources/accessibility/strings/extension_strings.grd"
+ defines = chrome_grit_defines
+ outputs =
+ process_file_template(accessibility_locales,
+ "_locales/{{source_name_part}}/messages.json.gz")
+ output_dir = invoker.out_dir
+
+ resource_ids = ""
+ }
+}
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings.grd b/chromium/chrome/browser/resources/accessibility/strings/extension_strings.grd
new file mode 100644
index 00000000000..06356f2c17f
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings.grd
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<grit base_dir="." current_release="1" latest_public_release="0"
+ output_all_resource_defines="false" enc_check="möl" source_lang_id="en">
+ <outputs>
+ <output filename="_locales/af/messages.json.gz" type="chrome_messages_json_gzip" lang="af"/>
+ <output filename="_locales/am/messages.json.gz" type="chrome_messages_json_gzip" lang="am"/>
+ <output filename="_locales/ar/messages.json.gz" type="chrome_messages_json_gzip" lang="ar"/>
+ <output filename="_locales/bg/messages.json.gz" type="chrome_messages_json_gzip" lang="bg"/>
+ <output filename="_locales/bn/messages.json.gz" type="chrome_messages_json_gzip" lang="bn"/>
+ <output filename="_locales/ca/messages.json.gz" type="chrome_messages_json_gzip" lang="ca"/>
+ <output filename="_locales/cs/messages.json.gz" type="chrome_messages_json_gzip" lang="cs"/>
+ <output filename="_locales/cy/messages.json.gz" type="chrome_messages_json_gzip" lang="cy"/>
+ <output filename="_locales/da/messages.json.gz" type="chrome_messages_json_gzip" lang="da"/>
+ <output filename="_locales/de/messages.json.gz" type="chrome_messages_json_gzip" lang="de"/>
+ <output filename="_locales/el/messages.json.gz" type="chrome_messages_json_gzip" lang="el"/>
+ <output filename="_locales/en_GB/messages.json.gz" type="chrome_messages_json_gzip" lang="en-GB"/>
+ <output filename="_locales/en/messages.json.gz" type="chrome_messages_json_gzip" lang="en"/>
+ <output filename="_locales/es/messages.json.gz" type="chrome_messages_json_gzip" lang="es"/>
+ <output filename="_locales/es_419/messages.json.gz" type="chrome_messages_json_gzip" lang="es-419"/>
+ <output filename="_locales/et/messages.json.gz" type="chrome_messages_json_gzip" lang="et"/>
+ <output filename="_locales/eu/messages.json.gz" type="chrome_messages_json_gzip" lang="eu"/>
+ <output filename="_locales/fa/messages.json.gz" type="chrome_messages_json_gzip" lang="fa"/>
+ <output filename="_locales/fi/messages.json.gz" type="chrome_messages_json_gzip" lang="fi"/>
+ <output filename="_locales/fil/messages.json.gz" type="chrome_messages_json_gzip" lang="fil"/>
+ <output filename="_locales/fr/messages.json.gz" type="chrome_messages_json_gzip" lang="fr"/>
+ <output filename="_locales/gl/messages.json.gz" type="chrome_messages_json_gzip" lang="gl"/>
+ <output filename="_locales/gu/messages.json.gz" type="chrome_messages_json_gzip" lang="gu"/>
+ <output filename="_locales/he/messages.json.gz" type="chrome_messages_json_gzip" lang="he"/>
+ <output filename="_locales/hi/messages.json.gz" type="chrome_messages_json_gzip" lang="hi"/>
+ <output filename="_locales/hr/messages.json.gz" type="chrome_messages_json_gzip" lang="hr"/>
+ <output filename="_locales/hu/messages.json.gz" type="chrome_messages_json_gzip" lang="hu"/>
+ <output filename="_locales/id/messages.json.gz" type="chrome_messages_json_gzip" lang="id"/>
+ <output filename="_locales/is/messages.json.gz" type="chrome_messages_json_gzip" lang="is"/>
+ <output filename="_locales/it/messages.json.gz" type="chrome_messages_json_gzip" lang="it"/>
+ <output filename="_locales/ja/messages.json.gz" type="chrome_messages_json_gzip" lang="ja"/>
+ <output filename="_locales/kn/messages.json.gz" type="chrome_messages_json_gzip" lang="kn"/>
+ <output filename="_locales/ko/messages.json.gz" type="chrome_messages_json_gzip" lang="ko"/>
+ <output filename="_locales/lt/messages.json.gz" type="chrome_messages_json_gzip" lang="lt"/>
+ <output filename="_locales/lv/messages.json.gz" type="chrome_messages_json_gzip" lang="lv"/>
+ <output filename="_locales/ml/messages.json.gz" type="chrome_messages_json_gzip" lang="ml"/>
+ <output filename="_locales/mr/messages.json.gz" type="chrome_messages_json_gzip" lang="mr"/>
+ <output filename="_locales/ms/messages.json.gz" type="chrome_messages_json_gzip" lang="ms"/>
+ <output filename="_locales/nl/messages.json.gz" type="chrome_messages_json_gzip" lang="nl"/>
+ <output filename="_locales/nb/messages.json.gz" type="chrome_messages_json_gzip" lang="no"/>
+ <output filename="_locales/pl/messages.json.gz" type="chrome_messages_json_gzip" lang="pl"/>
+ <output filename="_locales/pt_BR/messages.json.gz" type="chrome_messages_json_gzip" lang="pt-BR"/>
+ <output filename="_locales/pt_PT/messages.json.gz" type="chrome_messages_json_gzip" lang="pt-PT"/>
+ <output filename="_locales/ro/messages.json.gz" type="chrome_messages_json_gzip" lang="ro"/>
+ <output filename="_locales/ru/messages.json.gz" type="chrome_messages_json_gzip" lang="ru"/>
+ <output filename="_locales/sk/messages.json.gz" type="chrome_messages_json_gzip" lang="sk"/>
+ <output filename="_locales/sl/messages.json.gz" type="chrome_messages_json_gzip" lang="sl"/>
+ <output filename="_locales/sr/messages.json.gz" type="chrome_messages_json_gzip" lang="sr"/>
+ <output filename="_locales/sv/messages.json.gz" type="chrome_messages_json_gzip" lang="sv"/>
+ <output filename="_locales/sw/messages.json.gz" type="chrome_messages_json_gzip" lang="sw"/>
+ <output filename="_locales/ta/messages.json.gz" type="chrome_messages_json_gzip" lang="ta"/>
+ <output filename="_locales/te/messages.json.gz" type="chrome_messages_json_gzip" lang="te"/>
+ <output filename="_locales/th/messages.json.gz" type="chrome_messages_json_gzip" lang="th"/>
+ <output filename="_locales/tr/messages.json.gz" type="chrome_messages_json_gzip" lang="tr"/>
+ <output filename="_locales/uk/messages.json.gz" type="chrome_messages_json_gzip" lang="uk"/>
+ <output filename="_locales/vi/messages.json.gz" type="chrome_messages_json_gzip" lang="vi"/>
+ <output filename="_locales/zh_CN/messages.json.gz" type="chrome_messages_json_gzip" lang="zh-CN"/>
+ <output filename="_locales/zh_TW/messages.json.gz" type="chrome_messages_json_gzip" lang="zh-TW"/>
+ <output filename="_locales/zu/messages.json.gz" type="chrome_messages_json_gzip" lang="zu"/>
+ </outputs>
+ <translations>
+ <file path="extension_strings_af.xtb" lang="af" />
+ <file path="extension_strings_am.xtb" lang="am" />
+ <file path="extension_strings_ar.xtb" lang="ar" />
+ <file path="extension_strings_as.xtb" lang="as" />
+ <file path="extension_strings_az.xtb" lang="az" />
+ <file path="extension_strings_be.xtb" lang="be" />
+ <file path="extension_strings_bg.xtb" lang="bg" />
+ <file path="extension_strings_bn.xtb" lang="bn" />
+ <file path="extension_strings_bs.xtb" lang="bs" />
+ <file path="extension_strings_ca.xtb" lang="ca" />
+ <file path="extension_strings_cs.xtb" lang="cs" />
+ <file path="extension_strings_cy.xtb" lang="cy" />
+ <file path="extension_strings_da.xtb" lang="da" />
+ <file path="extension_strings_de.xtb" lang="de" />
+ <file path="extension_strings_el.xtb" lang="el" />
+ <file path="extension_strings_en-GB.xtb" lang="en-GB" />
+ <file path="extension_strings_es.xtb" lang="es" />
+ <file path="extension_strings_es-419.xtb" lang="es-419" />
+ <file path="extension_strings_et.xtb" lang="et" />
+ <file path="extension_strings_eu.xtb" lang="eu" />
+ <file path="extension_strings_fa.xtb" lang="fa" />
+ <file path="extension_strings_fi.xtb" lang="fi" />
+ <file path="extension_strings_fil.xtb" lang="fil" />
+ <file path="extension_strings_fr.xtb" lang="fr" />
+ <file path="extension_strings_fr-CA.xtb" lang="fr-CA" />
+ <file path="extension_strings_gl.xtb" lang="gl" />
+ <file path="extension_strings_gu.xtb" lang="gu" />
+ <file path="extension_strings_hi.xtb" lang="hi" />
+ <file path="extension_strings_hr.xtb" lang="hr" />
+ <file path="extension_strings_hu.xtb" lang="hu" />
+ <file path="extension_strings_hy.xtb" lang="hy" />
+ <file path="extension_strings_id.xtb" lang="id" />
+ <file path="extension_strings_is.xtb" lang="is" />
+ <file path="extension_strings_it.xtb" lang="it" />
+ <!-- The translation console uses 'iw' for Hebrew, but we use 'he'. -->
+ <file path="extension_strings_iw.xtb" lang="he" />
+ <file path="extension_strings_ja.xtb" lang="ja" />
+ <file path="extension_strings_ka.xtb" lang="ka" />
+ <file path="extension_strings_kk.xtb" lang="kk" />
+ <file path="extension_strings_km.xtb" lang="km" />
+ <file path="extension_strings_kn.xtb" lang="kn" />
+ <file path="extension_strings_ko.xtb" lang="ko" />
+ <file path="extension_strings_ky.xtb" lang="ky" />
+ <file path="extension_strings_lo.xtb" lang="lo" />
+ <file path="extension_strings_lt.xtb" lang="lt" />
+ <file path="extension_strings_lv.xtb" lang="lv" />
+ <file path="extension_strings_mk.xtb" lang="mk" />
+ <file path="extension_strings_ml.xtb" lang="ml" />
+ <file path="extension_strings_mn.xtb" lang="mn" />
+ <file path="extension_strings_mr.xtb" lang="mr" />
+ <file path="extension_strings_ms.xtb" lang="ms" />
+ <file path="extension_strings_my.xtb" lang="my" />
+ <file path="extension_strings_ne.xtb" lang="ne" />
+ <file path="extension_strings_nl.xtb" lang="nl" />
+ <file path="extension_strings_no.xtb" lang="no" />
+ <file path="extension_strings_or.xtb" lang="or" />
+ <file path="extension_strings_pa.xtb" lang="pa" />
+ <file path="extension_strings_pl.xtb" lang="pl" />
+ <file path="extension_strings_pt-BR.xtb" lang="pt-BR" />
+ <file path="extension_strings_pt-PT.xtb" lang="pt-PT" />
+ <file path="extension_strings_ro.xtb" lang="ro" />
+ <file path="extension_strings_ru.xtb" lang="ru" />
+ <file path="extension_strings_si.xtb" lang="si" />
+ <file path="extension_strings_sk.xtb" lang="sk" />
+ <file path="extension_strings_sl.xtb" lang="sl" />
+ <file path="extension_strings_sq.xtb" lang="sq" />
+ <file path="extension_strings_sr.xtb" lang="sr" />
+ <file path="extension_strings_sr-Latn.xtb" lang="sr-Latn" />
+ <file path="extension_strings_sv.xtb" lang="sv" />
+ <file path="extension_strings_sw.xtb" lang="sw" />
+ <file path="extension_strings_ta.xtb" lang="ta" />
+ <file path="extension_strings_te.xtb" lang="te" />
+ <file path="extension_strings_th.xtb" lang="th" />
+ <file path="extension_strings_tr.xtb" lang="tr" />
+ <file path="extension_strings_uk.xtb" lang="uk" />
+ <file path="extension_strings_ur.xtb" lang="ur" />
+ <file path="extension_strings_uz.xtb" lang="uz" />
+ <file path="extension_strings_vi.xtb" lang="vi" />
+ <file path="extension_strings_zh-CN.xtb" lang="zh-CN" />
+ <file path="extension_strings_zh-HK.xtb" lang="zh-HK" />
+ <file path="extension_strings_zh-TW.xtb" lang="zh-TW" />
+ <file path="extension_strings_zu.xtb" lang="zu" />
+ </translations>
+ <release seq="1">
+ <messages fallback_to_english="true">
+ <message desc="Context menu option to speak the currently highlighted/selected text" name="IDS_SELECT_TO_SPEAK_LISTEN_CONTEXT_MENU_OPTION_TEXT">
+ Listen to selected text
+ </message>
+ </messages>
+ </release>
+</grit>
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_af.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_af.xtb
new file mode 100644
index 00000000000..2db13909709
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_af.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="af">
+<translation id="3407726812456125464">Luister na gekose teks</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_am.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_am.xtb
new file mode 100644
index 00000000000..83d947a0c6a
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_am.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="am">
+<translation id="3407726812456125464">የተመረጠውን ጽሁፍ ያዳምጡ</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ar.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ar.xtb
new file mode 100644
index 00000000000..658e77f9dc8
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ar.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ar">
+<translation id="3407726812456125464">الاستماع إلى النص المحدَّد</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_as.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_as.xtb
new file mode 100644
index 00000000000..cb27096ecaa
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_as.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="as">
+<translation id="3407726812456125464">বাছনি কৰা পাঠ শুনক</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_az.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_az.xtb
new file mode 100644
index 00000000000..2fe6bf112cb
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_az.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="az">
+<translation id="3407726812456125464">Seçilmiş mətni dinləyin</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_be.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_be.xtb
new file mode 100644
index 00000000000..4015a57a160
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_be.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="be">
+<translation id="3407726812456125464">Праслухаць вылучаны тэкст</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_bg.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_bg.xtb
new file mode 100644
index 00000000000..98166861b5e
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_bg.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="bg">
+<translation id="3407726812456125464">Слушане на избрания текст</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_bn.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_bn.xtb
new file mode 100644
index 00000000000..2ef7fbc8bef
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_bn.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="bn">
+<translation id="3407726812456125464">বেছে নেওয়া টেক্সট শুনুন</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_bs.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_bs.xtb
new file mode 100644
index 00000000000..a64632286e3
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_bs.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="bs">
+<translation id="3407726812456125464">Slušajte odabrani tekst</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ca.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ca.xtb
new file mode 100644
index 00000000000..4dff6837b39
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ca.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ca">
+<translation id="3407726812456125464">Escolta el text seleccionat</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_cs.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_cs.xtb
new file mode 100644
index 00000000000..82ff4c4d351
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_cs.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="cs">
+<translation id="3407726812456125464">Poslechnout si vybraný text</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_cy.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_cy.xtb
new file mode 100644
index 00000000000..3b8c80d5e6d
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_cy.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="cy">
+<translation id="3407726812456125464">Gwrando ar y testun a ddewisir</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_da.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_da.xtb
new file mode 100644
index 00000000000..b91277cedfb
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_da.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="da">
+<translation id="3407726812456125464">Få den markerede tekst læst op</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_de.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_de.xtb
new file mode 100644
index 00000000000..ab422a446cf
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_de.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="de">
+<translation id="3407726812456125464">Ausgewählten Text vorlesen lassen</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_el.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_el.xtb
new file mode 100644
index 00000000000..5014ac4bd7c
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_el.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="el">
+<translation id="3407726812456125464">Ακρόαση επιλεγμένου κειμένου</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_en-GB.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_en-GB.xtb
new file mode 100644
index 00000000000..4b7495ddcbc
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_en-GB.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="en-GB">
+<translation id="3407726812456125464">Listen to selected text</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_es-419.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_es-419.xtb
new file mode 100644
index 00000000000..3e96edb843b
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_es-419.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="es-419">
+<translation id="3407726812456125464">Escuchar el texto seleccionado</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_es.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_es.xtb
new file mode 100644
index 00000000000..f28a54819a7
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_es.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="es">
+<translation id="3407726812456125464">Escuchar texto seleccionado</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_et.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_et.xtb
new file mode 100644
index 00000000000..ad23a81f381
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_et.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="et">
+<translation id="3407726812456125464">Kuulake valitud teksti</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_eu.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_eu.xtb
new file mode 100644
index 00000000000..39375e7c3d5
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_eu.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="eu">
+<translation id="3407726812456125464">Entzun hautatutako testua</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fa.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fa.xtb
new file mode 100644
index 00000000000..4327c7152c4
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fa.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fa">
+<translation id="3407726812456125464">گوش دادن به نوشتار انتخاب‌شده</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fi.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fi.xtb
new file mode 100644
index 00000000000..ac52303ad1a
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fi.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fi">
+<translation id="3407726812456125464">Kuuntele valittu teksti</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fil.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fil.xtb
new file mode 100644
index 00000000000..84202d42659
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fil.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fil">
+<translation id="3407726812456125464">Pakinggan ang piniling text</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fr-CA.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fr-CA.xtb
new file mode 100644
index 00000000000..b90e6fbff3f
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fr-CA.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fr-CA">
+<translation id="3407726812456125464">Écouter le texte sélectionné</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fr.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fr.xtb
new file mode 100644
index 00000000000..ffee6d55edc
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_fr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fr">
+<translation id="3407726812456125464">Écouter le texte sélectionné</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_gl.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_gl.xtb
new file mode 100644
index 00000000000..2eae44197b9
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_gl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="gl">
+<translation id="3407726812456125464">Escoitar texto seleccionado</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_grd/IDS_SELECT_TO_SPEAK_LISTEN_CONTEXT_MENU_OPTION_TEXT.png.sha1 b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_grd/IDS_SELECT_TO_SPEAK_LISTEN_CONTEXT_MENU_OPTION_TEXT.png.sha1
new file mode 100644
index 00000000000..f029b8f4935
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_grd/IDS_SELECT_TO_SPEAK_LISTEN_CONTEXT_MENU_OPTION_TEXT.png.sha1
@@ -0,0 +1 @@
+acf45a93f6883a06943ff57dee9688d816af0eeb \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_gu.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_gu.xtb
new file mode 100644
index 00000000000..df4f4a4dd0c
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_gu.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="gu">
+<translation id="3407726812456125464">પસંદ કરેલી ટેક્સ્ટ સાંભળો</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hi.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hi.xtb
new file mode 100644
index 00000000000..58b4285c1fe
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hi.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hi">
+<translation id="3407726812456125464">चुने गए टेक्स्ट को सुनें</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hr.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hr.xtb
new file mode 100644
index 00000000000..23683ef6a23
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hr">
+<translation id="3407726812456125464">Poslušajte odabrani tekst</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hu.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hu.xtb
new file mode 100644
index 00000000000..6bc3a5ea2ca
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hu.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hu">
+<translation id="3407726812456125464">A kijelölt szöveg meghallgatása</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hy.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hy.xtb
new file mode 100644
index 00000000000..ca2be106b84
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_hy.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hy">
+<translation id="3407726812456125464">Հնչեցնել ընտրված տեքստը</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_id.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_id.xtb
new file mode 100644
index 00000000000..885862cbd6f
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_id.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="id">
+<translation id="3407726812456125464">Dengarkan teks yang dipilih</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_is.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_is.xtb
new file mode 100644
index 00000000000..45ac4a89f71
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_is.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="is">
+<translation id="3407726812456125464">Hlusta á valinn texta</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_it.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_it.xtb
new file mode 100644
index 00000000000..5502cf33233
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_it.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="it">
+<translation id="3407726812456125464">Ascolta il testo selezionato</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_iw.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_iw.xtb
new file mode 100644
index 00000000000..db0f91b0b28
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_iw.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="iw">
+<translation id="3407726812456125464">האזנה לטקסט שנבחר</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ja.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ja.xtb
new file mode 100644
index 00000000000..ad989aeb37c
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ja.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ja">
+<translation id="3407726812456125464">選択したテキストの音声を聞きます</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ka.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ka.xtb
new file mode 100644
index 00000000000..5be12bfea9a
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ka.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ka">
+<translation id="3407726812456125464">მონიშნული ტექსტის მოსმენა</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_kk.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_kk.xtb
new file mode 100644
index 00000000000..a653bdcfd5f
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_kk.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="kk">
+<translation id="3407726812456125464">Таңдалған мәтінді тыңдау</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_km.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_km.xtb
new file mode 100644
index 00000000000..b88aa05135e
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_km.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="km">
+<translation id="3407726812456125464">ស្ដាប់​ការអានអក្សរ​ដែល​បាន​ជ្រើសរើស</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_kn.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_kn.xtb
new file mode 100644
index 00000000000..1eb6330e0d8
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_kn.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="kn">
+<translation id="3407726812456125464">ಆಯ್ದ ಪಠ್ಯವನ್ನು ಆಲಿಸಿ</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ko.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ko.xtb
new file mode 100644
index 00000000000..78c1b8464d3
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ko.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ko">
+<translation id="3407726812456125464">선택한 텍스트 듣기</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ky.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ky.xtb
new file mode 100644
index 00000000000..37ec22499d0
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ky.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ky">
+<translation id="3407726812456125464">Тандалган текстти угуу</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_lo.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_lo.xtb
new file mode 100644
index 00000000000..732d0bb9bc8
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_lo.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="lo">
+<translation id="3407726812456125464">ຟັງຂໍ້ຄວາມທີ່ເລືອກ</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_lt.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_lt.xtb
new file mode 100644
index 00000000000..9456799a07c
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_lt.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="lt">
+<translation id="3407726812456125464">Klausyti pasirinkto teksto</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_lv.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_lv.xtb
new file mode 100644
index 00000000000..9f28bccfaa3
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_lv.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="lv">
+<translation id="3407726812456125464">Klausīties atlasīto tekstu</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_mk.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_mk.xtb
new file mode 100644
index 00000000000..830fa1cba93
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_mk.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="mk">
+<translation id="3407726812456125464">Изговори го означениот текст</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ml.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ml.xtb
new file mode 100644
index 00000000000..e5e1619cbae
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ml.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ml">
+<translation id="3407726812456125464">തിരഞ്ഞെടുത്ത ടെക്‌സ്‌റ്റ് കേൾക്കുക</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_mn.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_mn.xtb
new file mode 100644
index 00000000000..14a9d31dbc7
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_mn.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="mn">
+<translation id="3407726812456125464">Сонгосон текстийг сонсох</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_mr.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_mr.xtb
new file mode 100644
index 00000000000..a3744eccec4
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_mr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="mr">
+<translation id="3407726812456125464">निवडलेला मजकूर ऐका</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ms.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ms.xtb
new file mode 100644
index 00000000000..50b30448988
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ms.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ms">
+<translation id="3407726812456125464">Dengar teks yang dipilih</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_my.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_my.xtb
new file mode 100644
index 00000000000..d56f9631b9f
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_my.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="my">
+<translation id="3407726812456125464">ရွေးထားသောစာသားကို နားထောင်ရန်</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ne.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ne.xtb
new file mode 100644
index 00000000000..85f05d06cf6
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ne.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ne">
+<translation id="3407726812456125464">चयन गरिएको टेक्स्ट सुन्नुहोस्</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_nl.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_nl.xtb
new file mode 100644
index 00000000000..e558242f9d4
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_nl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="nl">
+<translation id="3407726812456125464">Geselecteerde tekst beluisteren</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_no.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_no.xtb
new file mode 100644
index 00000000000..5d9ed7546fc
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_no.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="no">
+<translation id="3407726812456125464">Hør på den markerte teksten</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_or.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_or.xtb
new file mode 100644
index 00000000000..bab0e65ec2c
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_or.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="or">
+<translation id="3407726812456125464">ଚୟନିତ ଟେକ୍ସଟ ଶୁଣନ୍ତୁ</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pa.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pa.xtb
new file mode 100644
index 00000000000..36ee38e2698
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pa.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pa">
+<translation id="3407726812456125464">ਚੁਣੀ ਹੋਈ ਲਿਖਤ ਨੂੰ ਸੁਣੋ</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pl.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pl.xtb
new file mode 100644
index 00000000000..3c341ace2b1
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pl">
+<translation id="3407726812456125464">Odsłuchaj zaznaczony tekst</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pt-BR.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pt-BR.xtb
new file mode 100644
index 00000000000..7fcba0bc0ea
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pt-BR.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pt-BR">
+<translation id="3407726812456125464">Ouvir o texto selecionado</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pt-PT.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pt-PT.xtb
new file mode 100644
index 00000000000..f7448fa7b9f
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_pt-PT.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pt-PT">
+<translation id="3407726812456125464">Ouvir texto selecionado</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ro.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ro.xtb
new file mode 100644
index 00000000000..d4d00a0f743
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ro.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ro">
+<translation id="3407726812456125464">Ascultă textul selectat</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ru.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ru.xtb
new file mode 100644
index 00000000000..88072d9f965
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ru.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ru">
+<translation id="3407726812456125464">Озвучить выбранный текст</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_si.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_si.xtb
new file mode 100644
index 00000000000..578937cb844
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_si.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="si">
+<translation id="3407726812456125464">තෝරා ගත් පෙළට සවන් දෙන්න</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sk.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sk.xtb
new file mode 100644
index 00000000000..da58f55cf59
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sk.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sk">
+<translation id="3407726812456125464">Vypočuť si vybraný text</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sl.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sl.xtb
new file mode 100644
index 00000000000..e0099689874
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sl.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sl">
+<translation id="3407726812456125464">Poslušanje izbranega besedila</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sq.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sq.xtb
new file mode 100644
index 00000000000..dd8d79dba2d
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sq.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sq">
+<translation id="3407726812456125464">Dëgjo tekstin e zgjedhur</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sr-Latn.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sr-Latn.xtb
new file mode 100644
index 00000000000..94ee6167101
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sr-Latn.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sr-Latn">
+<translation id="3407726812456125464">Slušajte izabrani tekst</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sr.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sr.xtb
new file mode 100644
index 00000000000..9cc6acfb16b
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sr">
+<translation id="3407726812456125464">Слушајте изабрани текст</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sv.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sv.xtb
new file mode 100644
index 00000000000..e289e0854ce
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sv.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sv">
+<translation id="3407726812456125464">Lyssna på markerad text</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sw.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sw.xtb
new file mode 100644
index 00000000000..0989fa8f33e
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_sw.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sw">
+<translation id="3407726812456125464">Sikiliza maandishi yaliyochaguliwa</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ta.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ta.xtb
new file mode 100644
index 00000000000..87e1785f557
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ta.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ta">
+<translation id="3407726812456125464">தேர்ந்தெடுத்த வார்த்தைகளை வாசி</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_te.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_te.xtb
new file mode 100644
index 00000000000..eb333b066c5
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_te.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="te">
+<translation id="3407726812456125464">ఎంచుకున్న టెక్స్ట్‌ను వినండి</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_th.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_th.xtb
new file mode 100644
index 00000000000..f06133c7914
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_th.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="th">
+<translation id="3407726812456125464">ฟังข้อความที่เลือก</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_tr.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_tr.xtb
new file mode 100644
index 00000000000..373f90e0bb3
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_tr.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="tr">
+<translation id="3407726812456125464">Seçilen metni sesli oku</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_uk.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_uk.xtb
new file mode 100644
index 00000000000..0df714e6701
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_uk.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="uk">
+<translation id="3407726812456125464">Прослухати виділений текст</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ur.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ur.xtb
new file mode 100644
index 00000000000..23ed32b4cf7
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_ur.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ur">
+<translation id="3407726812456125464">منتخب کردہ ٹیکسٹ سنیں</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_uz.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_uz.xtb
new file mode 100644
index 00000000000..68524187d3b
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_uz.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="uz">
+<translation id="3407726812456125464">Tanlangan matnni tinglash</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_vi.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_vi.xtb
new file mode 100644
index 00000000000..0e17339297f
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_vi.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="vi">
+<translation id="3407726812456125464">Đọc to phần văn bản đã chọn</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-CN.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-CN.xtb
new file mode 100644
index 00000000000..9262ff9d7b0
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-CN.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zh-CN">
+<translation id="3407726812456125464">朗读所选文字</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-HK.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-HK.xtb
new file mode 100644
index 00000000000..f48b3502f5e
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-HK.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zh-HK">
+<translation id="3407726812456125464">聆聽已選取的文字</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-TW.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-TW.xtb
new file mode 100644
index 00000000000..c200c8efaa2
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zh-TW.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zh-TW">
+<translation id="3407726812456125464">說出選取的文字</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zu.xtb b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zu.xtb
new file mode 100644
index 00000000000..5fb0bc85c80
--- /dev/null
+++ b/chromium/chrome/browser/resources/accessibility/strings/extension_strings_zu.xtb
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zu">
+<translation id="3407726812456125464">Lalela umbhalo okhethiwe</translation>
+</translationbundle> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/app_service_internals/app_service_internals.ts b/chromium/chrome/browser/resources/app_service_internals/app_service_internals.ts
index 7fa10bb1578..23ced859d8c 100644
--- a/chromium/chrome/browser/resources/app_service_internals/app_service_internals.ts
+++ b/chromium/chrome/browser/resources/app_service_internals/app_service_internals.ts
@@ -114,6 +114,14 @@ export class AppServiceInternalsElement extends PolymerElement {
fileParts.push(shortcut.debugInfo + '\n');
}
+ fileParts.push('Promise App List\n');
+ fileParts.push('================\n\n');
+ for (const promiseApp of this.promiseAppList_) {
+ fileParts.push(promiseApp.packageId + '\n');
+ fileParts.push('-----\n');
+ fileParts.push(promiseApp.debugInfo + '\n');
+ }
+
const file = new Blob(fileParts);
const a = document.createElement('a');
a.href = URL.createObjectURL(file);
diff --git a/chromium/chrome/browser/resources/app_settings/app.html b/chromium/chrome/browser/resources/app_settings/app.html
index aa345e851d0..1ab421fa2e4 100644
--- a/chromium/chrome/browser/resources/app_settings/app.html
+++ b/chromium/chrome/browser/resources/app_settings/app.html
@@ -87,5 +87,11 @@
class="permission-card-row separated-row" app="[[app_]]"
more-permissions-label="$i18n{appManagementMorePermissionsLabel}">
</app-management-more-permissions-item>
+ <app-management-supported-links-item
+ id="supportedLinksOption"
+ class="permission-card-row"
+ app="[[app_]]"
+ apps="[[apps_]]">
+ </app-management-supported-links-item>
</div>
</div>
diff --git a/chromium/chrome/browser/resources/app_settings/app.ts b/chromium/chrome/browser/resources/app_settings/app.ts
index fefe925613c..072455921db 100644
--- a/chromium/chrome/browser/resources/app_settings/app.ts
+++ b/chromium/chrome/browser/resources/app_settings/app.ts
@@ -14,9 +14,13 @@ import 'chrome://resources/cr_components/app_management/window_mode_item.js';
import 'chrome://resources/cr_components/app_management/icons.html.js';
import 'chrome://resources/cr_components/app_management/uninstall_button.js';
import 'chrome://resources/cr_components/localized_link/localized_link.js';
+import 'chrome://resources/cr_components/app_management/supported_links_item.js';
+import 'chrome://resources/cr_components/app_management/supported_links_overlapping_apps_dialog.js';
+import 'chrome://resources/cr_components/app_management/supported_links_dialog.js';
import {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
+import {AppMap} from 'chrome://resources/cr_components/app_management/constants.js';
import {getAppIcon} from 'chrome://resources/cr_components/app_management/util.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -43,14 +47,17 @@ export class WebAppSettingsAppElement extends PolymerElement {
},
iconUrl_: {type: String, computed: 'getAppIcon_(app_)'},
showSearch_: {type: Boolean, value: false, readonly: true},
+ apps_: Object,
};
}
private app_: App|null;
+ private apps_: AppMap;
private iconUrl_: string;
private showSearch_: boolean;
override connectedCallback() {
+ this.apps_ = {};
super.connectedCallback();
const urlPath = new URL(document.URL).pathname;
if (urlPath.length <= 1) {
@@ -67,15 +74,28 @@ export class WebAppSettingsAppElement extends PolymerElement {
this.app_ = result.app;
});
+ BrowserProxy.getInstance().handler.getApps().then((result) => {
+ for (const app of result.apps) {
+ this.apps_[app.id] = app;
+ }
+ });
+
// Listens to app update.
const callbackRouter = BrowserProxy.getInstance().callbackRouter;
callbackRouter.onAppChanged.addListener(this.onAppChanged_.bind(this));
+ callbackRouter.onAppRemoved.addListener(this.onAppRemoved_.bind(this));
}
private onAppChanged_(app: App) {
if (this.app_ && app.id === this.app_.id) {
this.app_ = app;
}
+ this.apps_ = {...this.apps_, [app.id]: app};
+ }
+
+ private onAppRemoved_(appId: string) {
+ delete this.apps_[appId];
+ this.apps_ = {...this.apps_};
}
private getAppIcon_(app: App|null): string {
diff --git a/chromium/chrome/browser/resources/app_settings/web_app_settings.ts b/chromium/chrome/browser/resources/app_settings/web_app_settings.ts
index 5da3241caf3..cb082d0570a 100644
--- a/chromium/chrome/browser/resources/app_settings/web_app_settings.ts
+++ b/chromium/chrome/browser/resources/app_settings/web_app_settings.ts
@@ -10,6 +10,8 @@ export {PermissionTypeIndex} from 'chrome://resources/cr_components/app_manageme
export {AppManagementPermissionItemElement} from 'chrome://resources/cr_components/app_management/permission_item.js';
export {createTriStatePermission} from 'chrome://resources/cr_components/app_management/permission_util.js';
export {AppManagementRunOnOsLoginItemElement} from 'chrome://resources/cr_components/app_management/run_on_os_login_item.js';
+export {AppManagementSupportedLinksItemElement} from 'chrome://resources/cr_components/app_management/supported_links_item.js';
+export {AppManagementSupportedLinksOverlappingAppsDialogElement} from 'chrome://resources/cr_components/app_management/supported_links_overlapping_apps_dialog.js';
export {AppManagementToggleRowElement} from 'chrome://resources/cr_components/app_management/toggle_row.js';
export {getPermissionValueBool} from 'chrome://resources/cr_components/app_management/util.js';
export {AppManagementWindowModeElement} from 'chrome://resources/cr_components/app_management/window_mode_item.js';
diff --git a/chromium/chrome/browser/resources/settings/chromeos/BUILD.gn b/chromium/chrome/browser/resources/ash/settings/BUILD.gn
index c8bc0b92f2d..6575c8bd2a4 100644
--- a/chromium/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chromium/chrome/browser/resources/ash/settings/BUILD.gn
@@ -62,6 +62,7 @@ build_webui("build") {
]
web_component_files = [
+ "common/lacros_extension_controlled_indicator.ts",
"crostini_page/bruschetta_subpage.ts",
"crostini_page/crostini_arc_adb.ts",
"crostini_page/crostini_arc_adb_confirmation_dialog.ts",
@@ -76,14 +77,21 @@ build_webui("build") {
"crostini_page/crostini_port_forwarding.ts",
"crostini_page/crostini_port_forwarding_add_port_dialog.ts",
"crostini_page/crostini_subpage.ts",
+ "date_time_page/date_time_settings_card.ts",
"date_time_page/date_time_page.ts",
"date_time_page/system_geolocation_dialog.ts",
"date_time_page/timezone_selector.ts",
"date_time_page/timezone_subpage.ts",
"device_page/audio.ts",
+ "device_page/customize_button_row.ts",
+ "device_page/customize_buttons_subsection.ts",
+ "device_page/customize_mouse_buttons_subpage.ts",
+ "device_page/customize_pen_buttons_subpage.ts",
+ "device_page/customize_tablet_buttons_subpage.ts",
"device_page/device_page.ts",
"device_page/display.ts",
"device_page/display_layout.ts",
+ "device_page/display_night_light.ts",
"device_page/display_overscan_dialog.ts",
"device_page/graphics_tablet_subpage.ts",
"device_page/keyboard.ts",
@@ -172,7 +180,6 @@ build_webui("build") {
"os_a11y_page/display_and_magnification_subpage.ts",
"os_a11y_page/keyboard_and_text_input_page.ts",
"os_a11y_page/live_caption_section.ts",
- "os_a11y_page/manage_a11y_subpage.ts",
"os_a11y_page/os_a11y_page.ts",
"os_a11y_page/select_to_speak_subpage.ts",
"os_a11y_page/switch_access_action_assignment_dialog.ts",
@@ -198,14 +205,12 @@ build_webui("build") {
"os_apps_page/app_management_page/chrome_app_detail_view.ts",
"os_apps_page/app_management_page/dom_switch.ts",
"os_apps_page/app_management_page/main_view.ts",
+ "os_apps_page/app_management_page/permission_heading.ts",
"os_apps_page/app_management_page/pin_to_shelf_item.ts",
"os_apps_page/app_management_page/pwa_detail_view.ts",
"os_apps_page/app_management_page/read_only_permission_item.ts",
"os_apps_page/app_management_page/resize_lock_item.ts",
"os_apps_page/app_management_page/sub_apps_item.ts",
- "os_apps_page/app_management_page/supported_links_dialog.ts",
- "os_apps_page/app_management_page/supported_links_item.ts",
- "os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.ts",
"os_apps_page/app_management_page/borealis_page/borealis_detail_view.ts",
"os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.ts",
"os_apps_page/app_notifications_page/app_notification_row.ts",
@@ -239,6 +244,7 @@ build_webui("build") {
"os_languages_page/cr_checkbox_with_policy.ts",
"os_languages_page/input_method_options_page.ts",
"os_languages_page/input_page.ts",
+ "os_languages_page/language_settings_card.ts",
"os_languages_page/os_add_languages_dialog.ts",
"os_languages_page/os_edit_dictionary_page.ts",
"os_languages_page/os_japanese_clear_ime_data_dialog.ts",
@@ -251,6 +257,8 @@ build_webui("build") {
"os_people_page/fingerprint_list_subpage.ts",
"os_people_page/local_data_recovery_dialog.ts",
"os_people_page/lock_screen_subpage.ts",
+ "os_people_page/password_settings.ts",
+ "os_people_page/pin_settings.ts",
"os_people_page/lock_screen_password_prompt_dialog.ts",
"os_people_page/os_people_page.ts",
"os_people_page/os_personalization_options.ts",
@@ -283,16 +291,18 @@ build_webui("build") {
"os_reset_page/os_powerwash_dialog.ts",
"os_reset_page/os_powerwash_dialog_esim_item.ts",
"os_reset_page/os_reset_page.ts",
+ "os_reset_page/reset_settings_card.ts",
"os_search_page/google_assistant_subpage.ts",
"os_search_page/os_search_page.ts",
"os_search_page/os_search_selection_dialog.ts",
+ "os_search_page/search_and_assistant_settings_card.ts",
"os_search_page/search_engine.ts",
"os_search_page/search_subpage.ts",
"os_settings_main/os_settings_main.ts",
"os_settings_menu/os_settings_menu.ts",
"os_settings_page/os_settings_animated_pages.ts",
- "os_settings_page/os_settings_section.ts",
"os_settings_page/os_settings_subpage.ts",
+ "os_settings_page/settings_card.ts",
"os_settings_page/settings_idle_load.ts",
"os_settings_search_box/os_search_result_row.ts",
"os_settings_search_box/os_settings_search_box.ts",
@@ -301,11 +311,14 @@ build_webui("build") {
"parental_controls_page/parental_controls_page.ts",
"personalization_page/personalization_page.ts",
"settings_scheduler_slider/settings_scheduler_slider.ts",
+ "system_preferences_page/storage_and_power_settings_card.ts",
+ "system_preferences_page/system_preferences_page.ts",
]
non_web_component_files = [
"assert_extras.ts",
"common/global_scroll_target_mixin.ts",
+ "common/lacros_extension_control_browser_proxy.ts",
"common/load_time_booleans.ts",
"common/setting_id_param_util.ts",
"common/types.ts",
@@ -348,7 +361,6 @@ build_webui("build") {
"os_a11y_page/audio_and_captions_page_browser_proxy.ts",
"os_a11y_page/cursor_and_touchpad_page_browser_proxy.ts",
"os_a11y_page/keyboard_and_text_input_page_browser_proxy.ts",
- "os_a11y_page/manage_a11y_subpage_browser_proxy.ts",
"os_a11y_page/os_a11y_page_browser_proxy.ts",
"os_a11y_page/switch_access_constants.ts",
"os_a11y_page/switch_access_subpage_browser_proxy.ts",
@@ -419,7 +431,6 @@ build_webui("build") {
css_files = [
"device_page/input_device_settings_shared.css",
"internet_page/internet_shared.css",
- "main_page_container/main_page_container_styles.css",
"os_apps_page/app_management_page/app_management_cros_shared_style.css",
"os_apps_page/app_management_page/app_management_cros_shared_vars.css",
"os_languages_page/shared_style.css",
@@ -432,27 +443,31 @@ build_webui("build") {
mojo_files_deps = [
":rename_personalization_search_mojom",
+ "//ash/public/mojom:accelerator_actions_ts__generator",
+ "//ash/public/mojom:accelerator_keys_ts__generator",
"//ash/public/mojom:input_device_settings_ts__generator",
+ "//ash/webui/settings/public/constants:mojom_ts__generator",
+ "//chrome/browser/ui/webui/ash/settings/search/mojom:mojom_ts__generator",
"//chrome/browser/ui/webui/settings/ash/files_page/mojom:mojom_ts__generator",
"//chrome/browser/ui/webui/settings/ash/input_device_settings:mojom_ts__generator",
"//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom:mojom_ts__generator",
- "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_ts__generator",
- "//chrome/browser/ui/webui/settings/chromeos/constants:mojom_ts__generator",
"//chromeos/ash/components/audio/public/mojom:mojom_ts__generator",
"//chromeos/ash/components/drivefs/mojom:pin_manager_types_ts__generator",
"//ui/events/ash/mojom:mojom_ts__generator",
]
mojo_files = [
+ "$root_gen_dir/ash/public/mojom/accelerator_actions.mojom-webui.ts",
+ "$root_gen_dir/ash/public/mojom/accelerator_keys.mojom-webui.ts",
"$root_gen_dir/ash/public/mojom/input_device_settings.mojom-webui.ts",
+ "$root_gen_dir/chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom-webui.ts",
+ "$root_gen_dir/chrome/browser/ui/webui/ash/settings/search/mojom/search_result_icon.mojom-webui.ts",
+ "$root_gen_dir/chrome/browser/ui/webui/ash/settings/search/mojom/user_action_recorder.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/settings/ash/files_page/mojom/google_drive_handler.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/settings/ash/files_page/mojom/one_drive_handler.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom-webui.ts",
- "$root_gen_dir/chrome/browser/ui/webui/settings/ash/search/search.mojom-webui.ts",
- "$root_gen_dir/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom-webui.ts",
- "$root_gen_dir/chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom-webui.ts",
- "$root_gen_dir/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-webui.ts",
- "$root_gen_dir/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom-webui.ts",
+ "$root_gen_dir/ash/webui/settings/public/constants/routes.mojom-webui.ts",
+ "$root_gen_dir/ash/webui/settings/public/constants/setting.mojom-webui.ts",
"$root_gen_dir/chromeos/ash/components/audio/public/mojom/cros_audio_config.mojom-webui.ts",
"$root_gen_dir/chromeos/ash/components/drivefs/mojom/pin_manager_types.mojom-webui.ts",
"$root_gen_dir/ui/events/ash/mojom/modifier_key.mojom-webui.ts",
@@ -468,6 +483,7 @@ build_webui("build") {
"//ash/webui/common/resources:build_ts",
"//chrome/browser/resources/nearby_share/shared:build_ts",
"//chrome/browser/resources/settings_shared:build_ts",
+ "//third_party/cros-components:cros_components_ts",
"//third_party/polymer/v3_0:library",
"//ui/webui/resources/cr_components/app_management:build_ts",
"//ui/webui/resources/cr_components/color_change_listener:build_ts",
@@ -546,9 +562,9 @@ build_webui("build") {
# Need to rename gen/ash/webui/personalization_app/search/search.mojom-webui.ts
# to personalization_search.mojom-webui.ts to avoid a naming collision with
-# gen/chrome/browser/ui/webui/settings/ash/search/search.mojom-webui.ts", before
-# passing it to build_webui(), since the latter places all Mojo files in the
-# same folder, and expects all Mojo files to have unique names.
+# gen/chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom-webui.ts",
+# before passing it to build_webui(), since the latter places all Mojo files in
+# the same folder, and expects all Mojo files to have unique names.
copy("rename_personalization_search_mojom") {
deps =
[ "//ash/webui/personalization_app/search:mojo_bindings_ts__generator" ]
diff --git a/chromium/chrome/browser/resources/settings/chromeos/os_settings.gni b/chromium/chrome/browser/resources/ash/settings/os_settings.gni
index 9af11b01105..9af11b01105 100644
--- a/chromium/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chromium/chrome/browser/resources/ash/settings/os_settings.gni
diff --git a/chromium/chrome/browser/resources/bluetooth_internals/main.js b/chromium/chrome/browser/resources/bluetooth_internals/main.js
index cc50e5c142e..4beffae6d35 100644
--- a/chromium/chrome/browser/resources/bluetooth_internals/main.js
+++ b/chromium/chrome/browser/resources/bluetooth_internals/main.js
@@ -4,13 +4,13 @@
import {checkSystemPermissions, initializeViews} from './bluetooth_internals.js';
import {BluetoothInternalsHandler} from './bluetooth_internals.mojom-webui.js';
-import {loadTestModule} from './test_loader_util.js';
document.addEventListener('DOMContentLoaded', async () => {
- // Using a query of "module" provides a hook for the test suite to perform
- // setup actions.
- const loaded = await loadTestModule();
- if (!loaded) {
+ // Using a query of "isTest" provides a hook for the test suite to perform
+ // setup actions before initializing the UI.
+ const params = new URLSearchParams(window.location.search);
+ const isTest = params.has('isTest');
+ if (!isTest) {
checkSystemPermissions(
BluetoothInternalsHandler.getRemote(), initializeViews);
}
diff --git a/chromium/chrome/browser/resources/bookmarks/COMMON_METADATA b/chromium/chrome/browser/resources/bookmarks/COMMON_METADATA
new file mode 100644
index 00000000000..087172e2e23
--- /dev/null
+++ b/chromium/chrome/browser/resources/bookmarks/COMMON_METADATA
@@ -0,0 +1,3 @@
+monorail {
+ component: "UI>Browser>Bookmarks"
+}
diff --git a/chromium/chrome/browser/resources/bookmarks/DIR_METADATA b/chromium/chrome/browser/resources/bookmarks/DIR_METADATA
index 087172e2e23..34aaf1a7a4d 100644
--- a/chromium/chrome/browser/resources/bookmarks/DIR_METADATA
+++ b/chromium/chrome/browser/resources/bookmarks/DIR_METADATA
@@ -1,3 +1 @@
-monorail {
- component: "UI>Browser>Bookmarks"
-}
+mixins: "//chrome/browser/resources/bookmarks/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/bookmarks/store_client_mixin.ts b/chromium/chrome/browser/resources/bookmarks/store_client_mixin.ts
index e62968523da..2c38dba1b18 100644
--- a/chromium/chrome/browser/resources/bookmarks/store_client_mixin.ts
+++ b/chromium/chrome/browser/resources/bookmarks/store_client_mixin.ts
@@ -2,124 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import {Action, DeferredAction} from 'chrome://resources/js/store_ts.js';
-import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {makeStoreClientMixin, StoreClientInterface} from 'chrome://resources/cr_elements/store_client/store_client.js';
+import {Action} from 'chrome://resources/js/store_ts.js';
import {Store} from './store.js';
import {BookmarksPageState} from './types.js';
-type Constructor<T> = new (...args: any[]) => T;
+// A Bookmarks specific specialization of `StoreClientInterface`.
+export interface StoreClientMixinInterface extends
+ StoreClientInterface<BookmarksPageState, Action> {}
-interface Watch {
- localProperty: string;
- valueGetter: (p: BookmarksPageState) => any;
-}
-
-export const StoreClientMixin = dedupingMixin(
- <T extends Constructor<PolymerElement>>(superClass: T): T&
- Constructor<StoreClientMixinInterface> => {
- class StoreClientMixin extends superClass implements
- StoreClientMixinInterface {
- private watches_: Watch[] = [];
-
- override connectedCallback() {
- super.connectedCallback();
- this.getStore().addObserver(this);
- }
-
- override disconnectedCallback() {
- super.disconnectedCallback();
- this.getStore().removeObserver(this);
- }
-
- /**
- * Watches a particular part of the state tree, updating |localProperty|
- * to the return value of |valueGetter| whenever the state changes. Eg,
- * to keep |this.item| updated with the value of a node: watch('item',
- * (state) => state.nodes[this.itemId]);
- *
- * Note that object identity is used to determine if the value has
- * changed before updating the UI, rather than Polymer-style deep
- * equality. If the getter function returns |undefined|, no changes will
- * propagate to the UI.
- */
- private watch_(
- localProperty: string,
- valueGetter: (p: BookmarksPageState) => any): void {
- this.watches_.push({
- localProperty: localProperty,
- valueGetter: valueGetter,
- });
- }
-
- dispatch(action: Action|null) {
- this.getStore().dispatch(action);
- }
-
- dispatchAsync(action: DeferredAction) {
- this.getStore().dispatchAsync(action);
- }
-
- onStateChanged(newState: BookmarksPageState) {
- this.watches_.forEach((watch) => {
- const oldValue = this.get(watch.localProperty);
- const newValue = watch.valueGetter(newState);
-
- // Avoid poking Polymer unless something has actually changed.
- // Reducers must return new objects rather than mutating existing
- // objects, so any real changes will pass through correctly.
- if (oldValue === newValue || newValue === undefined) {
- return;
- }
-
- this.set(watch.localProperty, newValue);
- });
- }
-
- updateFromStore() {
- if (this.getStore().isInitialized()) {
- this.onStateChanged(this.getStore().data);
- }
- }
-
- watch(
- localProperty: string,
- valueGetter: (p: BookmarksPageState) => any) {
- this.watch_(localProperty, valueGetter);
- }
-
- getState() {
- return this.getStore().data;
- }
-
- getStore() {
- return Store.getInstance();
- }
- }
- return StoreClientMixin;
- });
-
-export interface StoreClientMixinInterface {
- /**
- * Helper to dispatch an action to the store, which will update the store
- * data and then (possibly) flow through to the UI.
- */
- dispatch(action: Action|null): void;
-
- /**
- * Helper to dispatch a DeferredAction to the store, which will
- * asynchronously perform updates to the store data and UI.
- */
- dispatchAsync(action: DeferredAction): void;
-
- onStateChanged(newState: BookmarksPageState): void;
-
- updateFromStore(): void;
-
- watch(localProperty: string, valueGetter: (p: BookmarksPageState) => any):
- void;
-
- getState(): BookmarksPageState;
-
- getStore(): Store;
-}
+// `StoreClientMixin` binds Polymer elements to the Bookmarks store instance.
+export const StoreClientMixin = makeStoreClientMixin(Store.getInstance);
diff --git a/chromium/chrome/browser/resources/browsing_topics/browsing_topics_internals.html b/chromium/chrome/browser/resources/browsing_topics/browsing_topics_internals.html
index 78acc2b4d5e..b1ea6527cdc 100644
--- a/chromium/chrome/browser/resources/browsing_topics/browsing_topics_internals.html
+++ b/chromium/chrome/browser/resources/browsing_topics/browsing_topics_internals.html
@@ -116,7 +116,8 @@ youtube.com
<th>Topic ID</th>
<th>Topic Name</th>
<th>Real or Random</th>
- <th>Observed-by context domains (hashed)</th>
+ <th>Observed-by context domains
+ (hashed if the original value is unavailable)</th>
</tr>
</table>
<div>Calculation time: </div>
diff --git a/chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.css b/chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.css
index 97a7a45b12a..effddcba541 100644
--- a/chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.css
+++ b/chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.css
@@ -60,6 +60,11 @@ h3 {
white-space: pre-wrap;
}
+.hashvalue {
+ display: table-cell;
+ overflow-wrap: anywhere;
+}
+
/* Used so that 100% width within tabpanel will correspond to usable space. */
div[slot='panel'] {
height: 100%;
diff --git a/chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.html b/chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.html
index a0c8b8c7a4e..8967eee8d70 100644
--- a/chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.html
+++ b/chromium/chrome/browser/resources/certificate_viewer/certificate_viewer.html
@@ -80,11 +80,11 @@
</div>
<div>
<div class="attribute">$i18n{sha256}</div>
- <div id="sha256" class="value"></div>
+ <div id="sha256" class="hashvalue"></div>
</div>
<div>
- <div class="attribute">$i18n{sha1}</div>
- <div id="sha1" class="value"></div>
+ <div class="attribute">$i18n{spki}</div>
+ <div id="spki" class="hashvalue"></div>
</div>
</div>
</div>
diff --git a/chromium/chrome/browser/resources/chromeos/BUILD.gn b/chromium/chrome/browser/resources/chromeos/BUILD.gn
index 1380b6f9e0f..ddb12d98aa1 100644
--- a/chromium/chrome/browser/resources/chromeos/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/BUILD.gn
@@ -14,10 +14,12 @@ group("resources") {
":app_icon_resources",
":multidevice_setup_resources",
"accessibility:build",
+ "add_supervision:resources",
"arc_account_picker:resources",
"assistant_optin:resources",
"audio:resources",
"bluetooth_pairing_dialog:resources",
+ "borealis_installer:resources",
"cloud_upload:resources",
"desk_api:resources",
"emoji_picker:resources",
@@ -37,6 +39,8 @@ group("resources") {
"notification_tester:resources",
"password_change:resources",
"remote_maintenance_curtain:resources",
+ "sensor_info:resources",
+ "status_area_internals:resources",
"supervision:resources",
"vc_tray_tester:resources",
]
@@ -82,7 +86,6 @@ group("closure_compile") {
"accessibility/switch_access:closure_compile",
"account_manager:closure_compile",
"account_manager/components:closure_compile",
- "add_supervision:closure_compile",
"arc_account_picker:closure_compile",
"assistant_optin:closure_compile",
"bluetooth_pairing_dialog:closure_compile",
diff --git a/chromium/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn b/chromium/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
index 7445b7f587d..a304dc19c6c 100644
--- a/chromium/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
@@ -59,6 +59,7 @@ run_jsbundler("accessibility_common_copied_files") {
"dictation/parse/simple_parse_strategy.js",
"dictation/parse/speech_parser.js",
"dictation/ui_controller.js",
+ "gameface/gameface.js",
"magnifier/magnifier.js",
]
rewrite_rules = [
@@ -140,6 +141,7 @@ js_library("accessibility_common") {
deps = [
":autoclick",
":dictation",
+ ":gameface",
":magnifier",
"../common:automation_predicate",
"../common:chrome_event_handler",
@@ -289,6 +291,10 @@ js_library("dictation_context_checker") {
sources = [ "dictation/context_checker.js" ]
}
+js_library("gameface") {
+ sources = [ "gameface/gameface.js" ]
+}
+
action("pumpkin_test_files") {
testonly = true
diff --git a/chromium/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chromium/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 3272d5c4cde..f35637e7b0f 100644
--- a/chromium/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -77,6 +77,8 @@ chromevox_es6_modules = [
"background/editing/editable_text_base.js",
"background/editing/editing.js",
"background/editing/intent_handler.js",
+ "background/editing/text_edit_handler.js",
+ "background/editing/typing_echo.js",
"background/es6_loader.js",
"background/event/base_automation_handler.js",
"background/event/desktop_automation_handler.js",
@@ -113,7 +115,6 @@ chromevox_es6_modules = [
"background/panel/i_search_handler.js",
"background/panel/panel_background.js",
"background/panel/panel_node_menu_background.js",
- "background/panel/panel_tab_menu_background.js",
"background/phonetic_data.js",
"background/prefs.js",
"background/primary_tts.js",
@@ -129,6 +130,7 @@ chromevox_es6_modules = [
"common/bridge_callback_manager.js",
"common/bridge_constants.js",
"common/bridge_helper.js",
+ "common/command.js",
"common/command_store.js",
"common/custom_automation_event.js",
"common/earcon_id.js",
@@ -191,8 +193,6 @@ group("build") {
deps = [
":chromevox_background_script",
":chromevox_copied_files",
- ":chromevox_learn_mode_script",
- ":chromevox_log_script",
":chromevox_options_script",
":chromevox_panel_script",
":chromevox_phonetic_dictionaries_js",
@@ -203,8 +203,6 @@ group("build") {
}
chromevox_background_script_loader_file = "background/loader.js"
-chromevox_learn_mode_loader_file = "learn_mode/kbexplorer_loader.js"
-chromevox_log_loader_file = "log_page/log_loader.js"
chromevox_options_script_loader_file = "options/options_loader.js"
chromevox_panel_script_loader_file = "panel/panel_loader.js"
@@ -306,15 +304,6 @@ template("compress_js") {
}
# TODO once these loaders are all empty, we will need to re-work the entry points.
-compress_js("chromevox_learn_mode_script") {
- sources = [ chromevox_learn_mode_loader_file ]
- output_file = "$chromevox_out_dir/chromeVoxKbExplorerScript.js"
-}
-
-compress_js("chromevox_log_script") {
- sources = [ chromevox_log_loader_file ]
- output_file = "$chromevox_out_dir/chromeVoxLogScript.js"
-}
compress_js("chromevox_options_script") {
sources = [ chromevox_options_script_loader_file ]
@@ -426,6 +415,7 @@ if (is_chromeos_ash) {
"background/braille/braille_test.js",
"background/braille/braille_translator_manager_test.js",
"background/braille/liblouis_test.js",
+ "background/chromevox_range_test.js",
"background/color_test.js",
"background/download_handler_test.js",
"background/editing/editing_test.js",
diff --git a/chromium/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn b/chromium/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
index cb45a598a8c..e35df243257 100644
--- a/chromium/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
@@ -52,13 +52,10 @@ run_jsbundler("select_to_speak_copied_files") {
"earcons/null_selection.ogg",
"input_handler.js",
"metrics_utils.js",
- "options.css",
- "options.html",
"prefs_manager.js",
"select_to_speak-2x.svg",
"select_to_speak.js",
"select_to_speak_constants.js",
- "select_to_speak_options.js",
"sts-icon-128.png",
"sts-icon-16.png",
"sts-icon-48.png",
@@ -104,7 +101,6 @@ js2gtest("select_to_speak_extjs_tests") {
"select_to_speak_keystroke_selection_test.js",
"select_to_speak_mouse_selection_test.js",
"select_to_speak_navigation_control_test.js",
- "select_to_speak_options_test.js",
"select_to_speak_prefs_test.js",
]
@@ -145,7 +141,6 @@ js_type_check("closure_compile") {
":prefs_manager",
":select_to_speak",
":select_to_speak_constants",
- ":select_to_speak_options",
":tts_manager",
":ui_manager",
"../common:automation_predicate",
@@ -189,16 +184,6 @@ js_library("select_to_speak") {
]
}
-js_library("select_to_speak_options") {
- deps = [ ":prefs_manager" ]
- externs_list = [
- "$externs_path/accessibility_private.js",
- "$externs_path/automation.js",
- "$externs_path/metrics_private.js",
- "$externs_path/tts.js",
- ]
-}
-
js_library("input_handler") {
deps = [ "../common:rect_util" ]
}
diff --git a/chromium/chrome/browser/resources/chromeos/accessibility/strings/select_to_speak_strings.grdp b/chromium/chrome/browser/resources/chromeos/accessibility/strings/select_to_speak_strings.grdp
index 0e0afef9b92..a40fb163eea 100644
--- a/chromium/chrome/browser/resources/chromeos/accessibility/strings/select_to_speak_strings.grdp
+++ b/chromium/chrome/browser/resources/chromeos/accessibility/strings/select_to_speak_strings.grdp
@@ -6,99 +6,6 @@
<message desc="The description for Select-to-speak." name="IDS_SELECT_TO_SPEAK_DESCRIPTION">
</message>
-<message desc="The title of the Select-to-speak options page." name="IDS_SELECT_TO_SPEAK_OPTIONS_PAGE_TITLE">
- Select-to-speak Settings
-</message>
-<message desc="Section of the Select-to-speak options dialog where the user can adjust synthesized speech properties." name="IDS_SELECT_TO_SPEAK_OPTIONS_SPEECH">
- Speech
-</message>
-<message desc="Label for the control in the Select-to-speak options dialog where the user can choose a voice from a list." name="IDS_SELECT_TO_SPEAK_OPTIONS_VOICES_DESCRIPTION">
- Select a voice:
-</message>
-<message desc="Label for the control in the Select-to-speak options dialog where the user can choose a language from a list." name="IDS_SELECT_TO_SPEAK_OPTIONS_LANGUAGES_DESCRIPTION">
- Language
-</message>
-<message desc="The option in the language list where the default language of the device is used as the speech language." name="IDS_SELECT_TO_SPEAK_OPTIONS_DEVICE_LANGUAGE">
- Device language
-</message>
-<message desc="Label for the control in the Select-to-speak options dialog where the user can choose a preferred voice for the chosen language from a list." name="IDS_SELECT_TO_SPEAK_OPTIONS_PREFERRED_VOICE_DESCRIPTION">
- Preferred voice
-</message>
-<message desc="Label for the control in the Select-to-speak options dialog where the user can enable support for high-quality voices streamed from the interent when the device is online." name="IDS_SELECT_TO_SPEAK_OPTIONS_USE_NATURAL_VOICES">
- Use natural voice when device is online
-</message>
-<message desc="Informational text related to the Select-to-speak control that enables support for high-quality voices, mentioning that enabling these voices will send text to Google for processing and generating audio." name="IDS_SELECT_TO_SPEAK_OPTIONS_NATURAL_VOICES_EXPLANATION">
- Text will be sent to Google for processing.
-</message>
-<message desc="Label of link to more information about high-quality voices in Select-to-speak, mentioning that enabling these voices will send text to Google for processing and generating audio." name="IDS_SELECT_TO_SPEAK_OPTIONS_NATURAL_VOICES_EXPLANATION_MORE">
- Learn more
-</message>
-<message desc="Label for the control in the Select-to-speak options dialog where the user can choose a high-quality voice to be streamed from the internet from a list." name="IDS_SELECT_TO_SPEAK_OPTIONS_NATURAL_VOICE">
- Natural voice
-</message>
-<message desc="Label for the control in the Select-to-speak options dialog where the user can listen to a preview of the selected high-quality voice." name="IDS_SELECT_TO_SPEAK_OPTIONS_NATURAL_VOICE_PREVIEW">
- Voice preview
-</message>
-<message desc="Example text to preview the selected high-quality voice." name="IDS_SELECT_TO_SPEAK_OPTIONS_NATURAL_VOICES_EXAMPLE">
- Hi there! I'm your text-to-speech voice.
-</message>
-<message desc="Label for the button in the Select-to-speak options dialog to preview the selected high-quality voice." name="IDS_SELECT_TO_SPEAK_OPTIONS_PLAY_BUTTON">
- Play
-</message>
-<message desc="Group of options controlling highlighting." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT">
- Highlighting
-</message>
-<message desc="Label for option to highlight spoken words rather than spoken nodes." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_DESCRIPTION">
- Highlight each word as it is spoken
-</message>
-<message desc="Label for option to pick word highlight color." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_DESCRIPTION">
- Color for word highlights:
-</message>
-<message desc="Label for a blue highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_BLUE">
- Blue
-</message>
-<message desc="Label for a orange highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_ORANGE">
- Orange
-</message>
-<message desc="Label for a yellow highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_YELLOW">
- Yellow
-</message>
-<message desc="Label for a green highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_GREEN">
- Green
-</message>
-<message desc="Label for a pink highlight color in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_COLOR_PINK">
- Pink
-</message>
-<message desc="Example of a word highlight on a dark background in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_DARK">
- Dark background
-</message>
-<message desc="Example of a word highlight on a light background in the Select-to-speak options dialog." name="IDS_SELECT_TO_SPEAK_OPTIONS_HIGHLIGHT_LIGHT">
- Light background
-</message>
-<message desc="Label for option to fade the background outside of the focus ring to improve focus on what is being spoken." name="IDS_SELECT_TO_SPEAK_OPTIONS_BACKGROUND_SHADING_DESCRIPTION">
- Shade background content
-</message>
-<message desc="Label for option to show navigation controls, such as going to the previous or next paragraph, when Select-to-speak is activated." name="IDS_SELECT_TO_SPEAK_OPTIONS_NAVIGATION_CONTROLS_DESCRIPTION">
- Enable navigation controls
-</message>
-<message desc="Subtitle for option to show navigation controls, such as going to the previous or next paragraph, when Select-to-speak is activated." name="IDS_SELECT_TO_SPEAK_OPTIONS_NAVIGATION_CONTROLS_SUBTITLE">
- Provides controls to speed up, slow down, and pause the reading voice
-</message>
-<message desc="Link to the Text-to-Speech settings page." name="IDS_SELECT_TO_SPEAK_OPTIONS_TEXT_TO_SPEECH_SETTINGS">
- Personalize Text-to-Speech settings
-</message>
-<message desc="Link to the Text-to-Speech settings page." name="IDS_SELECT_TO_SPEAK_OPTIONS_TEXT_TO_SPEECH_SETTINGS_LINK">
- Text-to-Speech settings
-</message>
-<message desc="Voice name for the system default Text-to-Speech voice" name="IDS_SELECT_TO_SPEAK_SYSTEM_VOICE">
- System Text-to-Speech voice
-</message>
-<message desc="Label for the natural voices option that uses a default, server-supplied Text-to-Speech voice" name="IDS_SELECT_TO_SPEAK_DEFAULT_NETWORK_VOICE">
- Default natural voice
-</message>
-<message desc="Voice name of a natural voice, generated by combining a localized language name like English (US) with a serial number" name="IDS_SELECT_TO_SPEAK_NATURAL_VOICE_NAME">
- <ph name="DISPLAY_NAME">$1<ex>English (United States)</ex></ph> Voice <ph name="COUNT">$2<ex>2</ex></ph>
-</message>
<message desc="Description of a checkbox that is not checked" name="IDS_SELECT_TO_SPEAK_CHECKBOX_UNCHECKED">
unchecked
</message>
@@ -120,16 +27,13 @@
<message desc="Description of a radio button that is selected" name="IDS_SELECT_TO_SPEAK_RADIOBUTTON_MIXED">
partially selected
</message>
-<message desc="Sample text around which will be drawn a Select to Speak visual preview. This should be less than one line long." name="IDS_SELECT_TO_SPEAK_OPTIONS_SAMPLE_TEXT">
- The quick brown fox jumped over the lazy dog.
-</message>
<message desc="Title of the dialog shown to users the first time they use Select to speak after the natural voices update." name="IDS_SELECT_TO_SPEAK_NATURAL_VOICE_DIALOG_TITLE">
Use natural voice?
</message>
<message desc="Content of the dialog shown to users the first time they use Select to speak after the natural voices update." name="IDS_SELECT_TO_SPEAK_NATURAL_VOICE_DIALOG_DESCRIPTION">
You can use a natural, human-like voice when your device is online. Text will be sent to Google for processing. You can turn this off any time in Settings.
</message>
-<message desc="Label for the control in the Select-to-speak options dialog where the user can toggle automatic voice switching." name="IDS_SELECT_TO_SPEAK_OPTIONS_VOICE_SWITCHING_DESCRIPTION">
- Automatically change language to match selected content
+<message desc="Cancel button for the dialog shown to users the first time they use Select to speak. Clicking this button will result in using the system voices rather than natural voices." name="IDS_SELECT_TO_SPEAK_NATURAL_VOICE_DIALOG_CANCEL">
+ Use system voice
</message>
</grit-part>
diff --git a/chromium/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn b/chromium/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
index 2c50e2efe48..0af84b76f44 100644
--- a/chromium/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
@@ -29,7 +29,6 @@ run_jsbundler("switch_access_copied_files") {
"action_manager.js",
"auto_scan_manager.js",
"background.html",
- "background.js",
"cache.js",
"commands.js",
"focus_ring_manager.js",
@@ -81,6 +80,7 @@ run_jsbundler("switch_access_copied_files") {
"preference_manager.js",
"switch_access.js",
"switch_access_constants.js",
+ "switch_access_loader.js",
"switch_access_predicate.js",
"text_navigation_manager.js",
]
@@ -148,7 +148,6 @@ js_type_check("closure_compile") {
":action_manager",
":auto_scan_manager",
":back_button_node",
- ":background",
":basic_node",
":cache",
":combo_box_node",
@@ -170,6 +169,7 @@ js_type_check("closure_compile") {
":slider_node",
":switch_access",
":switch_access_constants",
+ ":switch_access_loader",
":switch_access_node",
":switch_access_predicate",
":tab_node",
@@ -200,7 +200,7 @@ js_library("auto_scan_manager") {
deps = [ ":switch_access_constants" ]
}
-js_library("background") {
+js_library("switch_access_loader") {
deps = [
":switch_access",
"../common:instance_checker",
diff --git a/chromium/chrome/browser/resources/chromeos/add_supervision/BUILD.gn b/chromium/chrome/browser/resources/chromeos/add_supervision/BUILD.gn
index 71b42a34c26..9645c6d609e 100644
--- a/chromium/chrome/browser/resources/chromeos/add_supervision/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/add_supervision/BUILD.gn
@@ -2,51 +2,36 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//third_party/closure_compiler/compile_js.gni")
-import("//tools/polymer/html_to_js.gni")
+import("//ui/webui/resources/tools/build_webui.gni")
-js_type_check("closure_compile") {
- is_polymer3 = true
- deps = [
- ":add_supervision_api_server",
- ":add_supervision_app",
- ":add_supervision_ui",
- "//ash/webui/common/resources/post_message_api:post_message_api_server",
- ]
-}
+build_webui("build") {
+ grd_prefix = "add_supervision"
-js_library("add_supervision_api_server") {
- deps = [
- "//ash/webui/common/resources/post_message_api:post_message_api_server",
- "//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings_js_library_for_compile",
- ]
-}
+ static_files = [ "add_supervision.html" ]
-js_library("add_supervision_app") {
- deps = [
- ":add_supervision_ui",
- "//chrome/browser/resources/chromeos/supervision:supervised_user_error",
- "//chrome/browser/resources/chromeos/supervision:supervised_user_offline",
- "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ web_component_files = [
+ "add_supervision_app.ts",
+ "add_supervision_ui.ts",
]
- externs_list = [ "//ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager_externs.js" ]
-}
-js_library("add_supervision_ui") {
- deps = [
- ":add_supervision_api_server",
- "//ash/webui/common/resources:load_time_data.m",
- "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ non_web_component_files = [ "add_supervision_api_server.ts" ]
+
+ mojo_files = [ "$root_gen_dir/chrome/browser/ui/webui/ash/add_supervision/add_supervision.mojom-webui.ts" ]
+ mojo_files_deps = [
+ "//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings_ts__generator",
]
- externs_list = [
- "$externs_path/chrome_extensions.js",
- "$externs_path/webview_tag.js",
+ ts_definitions = [
+ "//tools/typescript/definitions/webview_tag.d.ts",
+ "//tools/typescript/definitions/web_request.d.ts",
+ "//tools/typescript/definitions/context_menus.d.ts",
+ "//tools/typescript/definitions/tabs.d.ts",
+ "//tools/typescript/definitions/extension_types.d.ts",
]
-}
-html_to_js("web_components") {
- js_files = [
- "add_supervision_app.js",
- "add_supervision_ui.js",
+ ts_deps = [
+ "//ash/webui/common/resources:build_ts",
+ "//third_party/polymer/v3_0:library",
+ "//ui/webui/resources/cr_elements:build_ts",
+ "//ui/webui/resources/mojo:build_ts",
]
}
diff --git a/chromium/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn b/chromium/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
index 6be704c8480..acc84a5345f 100644
--- a/chromium/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
@@ -56,15 +56,8 @@ generate_grd("build_grd") {
input_files_base_dir = rebase_path(".", "//")
input_files = [
- # Vector resources
- "assistant_ready_dm.json",
- "assistant_ready_lm.json",
- "assistant_related_info_dm.json",
- "assistant_related_info_lm.json",
- "voice_laptop_dm.json",
- "voice_laptop_lm.json",
- "voice_tablet_dm.json",
- "voice_tablet_lm.json",
+ "voice_laptop.json",
+ "voice_tablet.json",
]
resource_path_prefix = "assistant_optin"
}
@@ -116,10 +109,7 @@ js_type_check("closure_compile") {
deps = [
":assistant_common_styles.css",
":assistant_icons.html",
-
- #"assistant_value_prop",
- #":assistant_voice_match",
- #":assistant_optin_flow",
+ ":browser_proxy",
":voice_match_entry",
]
}
@@ -140,42 +130,6 @@ js_library("assistant_icons.html") {
extra_deps = [ ":html_wrapper_files" ]
}
-js_library("assistant_loading") {
- sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js" ]
- deps = [
- "../login/components/behaviors:oobe_i18n_behavior",
- "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
- ]
- extra_deps = [ ":web_components" ]
-}
-
-js_library("assistant_optin_flow") {
- sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js" ]
- deps = [
- "../login/components/behaviors:oobe_i18n_behavior",
- "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
- ]
- extra_deps = [ ":web_components" ]
-}
-
-js_library("assistant_related_info") {
- sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/assistant_related_info.js" ]
- deps = [
- "../login/components/behaviors:oobe_i18n_behavior",
- "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
- ]
- extra_deps = [ ":web_components" ]
-}
-
-js_library("assistant_value_prop") {
- sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js" ]
- deps = [
- "../login/components/behaviors:oobe_i18n_behavior",
- "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
- ]
- extra_deps = [ ":web_components" ]
-}
-
js_library("voice_match_entry") {
sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/voice_match_entry.js" ]
deps = [
@@ -185,16 +139,8 @@ js_library("voice_match_entry") {
extra_deps = [ ":web_components" ]
}
-js_library("assistant_voice_match") {
- sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js" ]
- deps = [
- "../login/components/behaviors:multi_step_behavior",
- "../login/components/behaviors:oobe_i18n_behavior",
- "//ash/webui/common/resources:load_time_data.m",
- "//ash/webui/common/resources:util",
- "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
- ]
- extra_deps = [ ":web_components" ]
+js_library("browser_proxy") {
+ externs_list = [ "$externs_path/chrome_send.js" ]
}
##########################
diff --git a/chromium/chrome/browser/resources/chromeos/borealis_installer/BUILD.gn b/chromium/chrome/browser/resources/chromeos/borealis_installer/BUILD.gn
new file mode 100644
index 00000000000..49f8901a352
--- /dev/null
+++ b/chromium/chrome/browser/resources/chromeos/borealis_installer/BUILD.gn
@@ -0,0 +1,25 @@
+import("//ui/webui/resources/tools/build_webui.gni")
+
+build_webui("build") {
+ grd_prefix = "borealis_installer"
+
+ static_files = [
+ "borealis_installer.html",
+ "borealis_installer.css",
+ ]
+
+ web_component_files = [ "app.ts" ]
+
+ non_web_component_files = [ "browser_proxy.ts" ]
+
+ mojo_files = [ "$root_gen_dir/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer.mojom-webui.ts" ]
+
+ mojo_files_deps = [ "//chrome/browser/ui/webui/ash/borealis_installer:mojo_bindings_ts__generator" ]
+
+ ts_deps = [
+ "//ash/webui/common/resources:build_ts",
+ "//third_party/polymer/v3_0:library",
+ "//ui/webui/resources/js:build_ts",
+ "//ui/webui/resources/mojo:build_ts",
+ ]
+}
diff --git a/chromium/chrome/browser/resources/chromeos/cloud_upload/BUILD.gn b/chromium/chrome/browser/resources/chromeos/cloud_upload/BUILD.gn
index cd2cffb2bf3..8cd1fc2b4f6 100644
--- a/chromium/chrome/browser/resources/chromeos/cloud_upload/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/cloud_upload/BUILD.gn
@@ -8,10 +8,8 @@ build_webui("build") {
grd_prefix = "cloud_upload"
static_files = [
- "animations/move_confirmation_drive_dark.json",
- "animations/move_confirmation_drive_light.json",
- "animations/move_confirmation_onedrive_dark.json",
- "animations/move_confirmation_onedrive_light.json",
+ "animations/move_confirmation_drive.json",
+ "animations/move_confirmation_onedrive.json",
"icons/check.svg",
"icons/chevron.svg",
"icons/docs.svg",
@@ -25,8 +23,8 @@ build_webui("build") {
"icons/slides.svg",
"images/connect_one_drive_app_icon.svg",
"images/connect_one_drive.svg",
- "images/install_office_app_icon.svg",
- "images/install_office.svg",
+ "images/install_office_pwa_app_icon.svg",
+ "images/install_office_pwa.svg",
"images/install_onedrive_alone.svg",
"images/one_drive_success.svg",
"images/onedrive_app_icon.svg",
@@ -40,7 +38,7 @@ build_webui("build") {
"file_handler_page.ts",
"move_confirmation_page.ts",
"office_pwa_install_page.ts",
- "one_drive_upload_page.ts",
+ "office_setup_complete_page.ts",
"setup_cancel_dialog.ts",
"sign_in_page.ts",
"welcome_page.ts",
@@ -61,6 +59,7 @@ build_webui("build") {
ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
ts_deps = [
"//ash/webui/common/resources:build_ts",
+ "//third_party/cros-components:cros_components_ts",
"//ui/webui/resources/cr_components/color_change_listener:build_ts",
"//ui/webui/resources/cr_elements:build_ts",
"//ui/webui/resources/js:build_ts",
diff --git a/chromium/chrome/browser/resources/chromeos/contact_center_insights/BUILD.gn b/chromium/chrome/browser/resources/chromeos/contact_center_insights/BUILD.gn
index 18374b6f7cd..e4ba61c82d9 100644
--- a/chromium/chrome/browser/resources/chromeos/contact_center_insights/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/contact_center_insights/BUILD.gn
@@ -16,6 +16,7 @@ js_library("reporting_protos") {
"//components/reporting/proto:metric_data_proto",
"//components/reporting/proto:record_constants",
"//components/reporting/proto:record_proto",
+ "//components/reporting/proto:session_affiliated_user_proto",
"//components/reporting/proto:status_proto",
]
@@ -27,6 +28,7 @@ js_library("reporting_protos") {
"${reporting_proto_js_dir}/metric_data.js",
"${reporting_proto_js_dir}/record.js",
"${reporting_proto_js_dir}/record_constants.js",
+ "${reporting_proto_js_dir}/session_affiliated_user.js",
"${reporting_proto_js_dir}/status.js",
]
}
diff --git a/chromium/chrome/browser/resources/chromeos/desk_api/BUILD.gn b/chromium/chrome/browser/resources/chromeos/desk_api/BUILD.gn
index 9fa384eebec..ef3b5ac18e3 100644
--- a/chromium/chrome/browser/resources/chromeos/desk_api/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/desk_api/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2022 The Chromium Authors. All rights reserved.
+# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -14,6 +14,7 @@ resources_grd_file = "$target_gen_dir/resources.grd"
ts_files = [
"background.ts",
+ "background_main.ts",
"desk_api_impl.ts",
"message_type.ts",
"notification_api_impl.ts",
diff --git a/chromium/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn b/chromium/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
index 97a54486a7c..ba35be40127 100644
--- a/chromium/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/emoji_picker/BUILD.gn
@@ -18,12 +18,14 @@ build_webui("build") {
"network_error.svg",
"no_results_dark.svg",
"no_results.svg",
+ "nudge_icon.svg",
]
web_component_files = [
"emoji_button.ts",
"emoji_category_button.ts",
"emoji_error.ts",
+ "emoji_gif_nudge_overlay.ts",
"emoji_group.ts",
"emoji_group_button.ts",
"emoji_image.ts",
@@ -48,12 +50,16 @@ build_webui("build") {
mojo_files_deps =
[ "//chrome/browser/ui/webui/ash/emoji:mojo_bindings_ts__generator" ]
- mojo_files = [ "$root_gen_dir/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom-webui.ts" ]
+ mojo_files = [
+ "$root_gen_dir/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom-webui.ts",
+ "$root_gen_dir/chrome/browser/ui/webui/ash/emoji/new_window_proxy.mojom-webui.ts",
+ ]
ts_composite = true
ts_definitions = [ "$preprocess_folder/fuse.d.ts" ]
ts_deps = [
"//third_party/polymer/v3_0:library",
+ "//ui/webui/resources/cr_components/color_change_listener:build_ts",
"//ui/webui/resources/cr_elements:build_ts",
"//ui/webui/resources/js:build_ts",
"//ui/webui/resources/mojo:build_ts",
diff --git a/chromium/chrome/browser/resources/chromeos/enterprise_reporting/BUILD.gn b/chromium/chrome/browser/resources/chromeos/enterprise_reporting/BUILD.gn
index 0a40dc9587f..f17294ff572 100644
--- a/chromium/chrome/browser/resources/chromeos/enterprise_reporting/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/enterprise_reporting/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2023 The Chromium Authors. All rights reserved.
+# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/chromium/chrome/browser/resources/chromeos/login/BUILD.gn b/chromium/chrome/browser/resources/chromeos/login/BUILD.gn
index 93b72ef5a89..aa503166abb 100644
--- a/chromium/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -113,7 +113,7 @@ generate_grd("build_oobe_grd") {
":preprocess_unconditional_autogenerated",
":preprocess_unconditional_existing",
"../supervision:build_oobe_grdp",
- "//chrome/browser/resources/settings/chromeos:build_oobe_grdp",
+ "//chrome/browser/resources/ash/settings:build_oobe_grdp",
"//ui/webui/resources/cr_components/color_change_listener:build_ts",
]
manifest_files = [
@@ -123,15 +123,14 @@ generate_grd("build_oobe_grd") {
input_files_base_dir = rebase_path(".", "//")
input_files = [
# Lottie animation resources
- "animations/all_set_light.json",
- "animations/all_set_dark.json",
+ "animations/all_set.json",
"animations/checking_for_update.json",
"animations/downloading_apps.json",
+ "animations/gaia_info.json",
"animations/gesture_go_back.json",
"animations/gesture_go_home.json",
"animations/gesture_hotseat_overview.json",
"animations/spinner.json",
- "animations/spinner_dark.json",
"animations/welcome_screen_animation.json",
# Vector resources
@@ -144,24 +143,10 @@ generate_grd("build_oobe_grd") {
"images/arc_sideloading_illustration.svg",
"images/arc_vm_data_migration_icon.svg",
"images/browser_sync.svg",
- "images/data_loss_warning.svg",
- "images/encryption_migration.svg",
- "images/enrollment_complete.svg",
"images/error.svg",
- "images/parental_control.svg",
- "images/security_lock_dark.svg",
- "images/security_lock_light.svg",
"images/settings_gear.svg",
- "images/update_boot.svg",
- "images/update-antivirus.svg",
- "images/update-apps.svg",
- "images/update-charge.svg",
- "images/updating.svg",
- "images/google-accounts.svg",
- "images/welcome_animation_first_frame.svg",
"images/cfm/enrollment.svg",
"images/cfm/welcome.svg",
- "images/os-install-success.svg",
"images/1x/verify-account.svg",
"images/2x/verify-account.svg",
@@ -176,7 +161,7 @@ generate_grd("build_oobe_grd") {
grdp_files = [
"$root_gen_dir/chrome/browser/resources/chromeos/supervision/supervision_oobe_resources.grdp",
- "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_settings_oobe_resources.grdp",
+ "$root_gen_dir/chrome/browser/resources/ash/settings/os_settings_oobe_resources.grdp",
]
}
@@ -212,6 +197,7 @@ preprocess_if_expr("preprocess_unconditional_existing") {
"oobe_screen.css",
"oobe_trace.js",
"oobe_trace_start.js",
+ "priority_screens_common_flow.js",
"priority_screens_oobe_flow.js",
"screens.js",
]
@@ -269,6 +255,7 @@ preprocess_if_expr("preprocess_unconditional_autogenerated") {
"components/security_token_pin.js",
"components/throbber_notice.js",
"screens/common/adb_sideloading.js",
+ "screens/common/add_child.js",
"screens/common/app_downloading.js",
"screens/common/app_launch_splash.js",
"screens/common/assistant_optin.js",
@@ -276,6 +263,7 @@ preprocess_if_expr("preprocess_unconditional_autogenerated") {
"screens/common/choobe.js",
"screens/common/consolidated_consent.js",
"screens/common/cryptohome_recovery_setup.js",
+ "screens/common/password_selection.js",
"screens/common/device_disabled.js",
"screens/common/display_size.js",
"screens/common/drive_pinning.js",
@@ -288,6 +276,7 @@ preprocess_if_expr("preprocess_unconditional_autogenerated") {
"screens/common/gesture_navigation.js",
"screens/common/guest_tos.js",
"screens/common/hw_data_collection.js",
+ "screens/common/local_password_setup.js",
"screens/common/local_state_error.js",
"screens/common/managed_terms_of_service.js",
"screens/common/marketing_opt_in.js",
@@ -322,6 +311,7 @@ preprocess_if_expr("preprocess_unconditional_autogenerated") {
"screens/login/offline_login.js",
"screens/login/update_required_card.js",
"screens/oobe/auto_enrollment_check.js",
+ "screens/oobe/consumer_update.js",
"screens/oobe/demo_setup.js",
"screens/oobe/demo_preferences.js",
"screens/oobe/enable_debugging.js",
diff --git a/chromium/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chromium/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
index a214d6f3029..2a63b199780 100644
--- a/chromium/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
@@ -27,6 +27,7 @@ js_type_check("closure_compile_local") {
]
deps = [
":adb_sideloading",
+ ":add_child",
":app_downloading",
":app_launch_splash",
@@ -47,6 +48,7 @@ js_type_check("closure_compile_local") {
":gesture_navigation",
":guest_tos",
":hw_data_collection",
+ ":local_password_setup",
":local_state_error",
":managed_terms_of_service",
":marketing_opt_in",
@@ -55,6 +57,7 @@ js_type_check("closure_compile_local") {
":os_install",
":os_trial",
":parental_handoff",
+ ":password_selection",
":pin_setup",
":placeholder",
":recommend_apps",
@@ -93,6 +96,18 @@ js_library("adb_sideloading") {
extra_deps = [ ":web_components" ]
}
+js_library("add_child") {
+ sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/add_child.js" ]
+ deps = [
+ "../../components/behaviors:login_screen_behavior",
+ "../../components/behaviors:multi_step_behavior",
+ "../../components/behaviors:oobe_i18n_behavior",
+ "../../components/buttons:oobe_text_button",
+ "../../components/dialogs:oobe_adaptive_dialog",
+ ]
+ extra_deps = [ ":web_components" ]
+}
+
js_library("app_downloading") {
sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/app_downloading.js" ]
deps = [
@@ -272,7 +287,9 @@ js_library("fingerprint_setup") {
"//ash/webui/common/resources:load_time_data.m",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
- externs_list = [ "//ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc_externs.js" ]
+ externs_list = [
+ "//ash/webui/common/resources/quick_unlock/fingerprint_progress_externs.js",
+ ]
extra_deps = [ ":web_components" ]
}
@@ -348,6 +365,21 @@ js_library("hw_data_collection") {
extra_deps = [ ":web_components" ]
}
+js_library("local_password_setup") {
+ sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/local_password_setup.js" ]
+ deps = [
+ "../../components/behaviors:login_screen_behavior",
+ "../../components/behaviors:multi_step_behavior",
+ "../../components/behaviors:oobe_dialog_host_behavior",
+ "../../components/behaviors:oobe_i18n_behavior",
+ "../../components/buttons:oobe_back_button",
+ "../../components/buttons:oobe_next_button",
+ "../../components/dialogs:oobe_adaptive_dialog",
+ "../../components/dialogs:oobe_loading_dialog",
+ ]
+ extra_deps = [ ":web_components" ]
+}
+
js_library("local_state_error") {
sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/local_state_error.js" ]
deps = [
@@ -534,6 +566,19 @@ js_library("pin_setup") {
extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ]
}
+js_library("password_selection") {
+ sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/password_selection.js" ]
+ deps = [
+ "../../components/behaviors:login_screen_behavior",
+ "../../components/behaviors:oobe_dialog_host_behavior",
+ "../../components/behaviors:oobe_i18n_behavior",
+ "../../components/buttons:oobe_back_button",
+ "../../components/buttons:oobe_next_button",
+ "../../components/dialogs:oobe_adaptive_dialog",
+ ]
+ extra_deps = [ ":web_components" ]
+}
+
js_library("gaia_info") {
sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/gaia_info.js" ]
deps = [
@@ -721,6 +766,8 @@ js_library("error_message") {
html_to_js("web_components") {
js_files = [
"adb_sideloading.js",
+ "add_child.js",
+ "password_selection.js",
"cryptohome_recovery_setup.js",
"app_downloading.js",
"app_launch_splash.js",
@@ -740,6 +787,7 @@ html_to_js("web_components") {
"gesture_navigation.js",
"guest_tos.js",
"hw_data_collection.js",
+ "local_password_setup.js",
"local_state_error.js",
"managed_terms_of_service.js",
"marketing_opt_in.js",
diff --git a/chromium/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn b/chromium/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
index f27fc99341b..6edec72cd99 100644
--- a/chromium/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
@@ -17,6 +17,7 @@ js_type_check("closure_compile") {
]
deps = [
":auto_enrollment_check",
+ ":consumer_update",
":demo_preferences",
":demo_setup",
":enable_debugging",
@@ -45,6 +46,21 @@ js_library("auto_enrollment_check") {
extra_deps = [ ":web_components" ]
}
+js_library("consumer_update") {
+ sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/oobe/consumer_update.js" ]
+ deps = [
+ "../../components:oobe_slide",
+ "../../components/behaviors:login_screen_behavior",
+ "../../components/behaviors:multi_step_behavior",
+ "../../components/behaviors:oobe_i18n_behavior",
+ "../../components/buttons:oobe_back_button",
+ "../../components/buttons:oobe_next_button",
+ "../../components/dialogs:oobe_adaptive_dialog",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+ extra_deps = [ ":web_components" ]
+}
+
js_library("demo_preferences") {
sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/oobe/demo_preferences.js" ]
deps = [
@@ -100,6 +116,8 @@ js_library("enterprise_enrollment") {
"../../components/buttons:oobe_back_button",
"../../components/buttons:oobe_next_button",
"../../components/buttons:oobe_text_button",
+ "../../components/dialogs:oobe_adaptive_dialog",
+ "../../components/dialogs:oobe_loading_dialog",
"../../components/dialogs:oobe_modal_dialog",
"../common:offline_ad_login",
"//ash/webui/common/resources:cr.m",
@@ -218,6 +236,7 @@ js_library("welcome_dialog") {
html_to_js("web_components") {
js_files = [
"auto_enrollment_check.js",
+ "consumer_update.js",
"demo_setup.js",
"demo_preferences.js",
"enable_debugging.js",
diff --git a/chromium/chrome/browser/resources/chromeos/login/test_api/BUILD.gn b/chromium/chrome/browser/resources/chromeos/login/test_api/BUILD.gn
index d2ea18ebaf8..2ed2ab6a541 100644
--- a/chromium/chrome/browser/resources/chromeos/login/test_api/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/login/test_api/BUILD.gn
@@ -5,11 +5,13 @@
import("//third_party/closure_compiler/compile_js.gni")
js_type_check("closure_compile") {
+ is_polymer3 = true
deps = [ ":test_api" ]
}
js_library("test_api") {
deps = [
+ "../:cr_ui",
"//ash/webui/common/resources:load_time_data.m",
"//ash/webui/common/resources:util",
]
diff --git a/chromium/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn b/chromium/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
index af26ac9a113..96c46cb9b22 100644
--- a/chromium/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
@@ -34,6 +34,8 @@ if (optimize_webui) {
"//ash/webui/common/resources:css_wrapper_files",
"//ash/webui/common/resources:html_wrapper_files",
"//ash/webui/common/resources:preprocess",
+ "//third_party/cros-components:cros_components_ts",
+ "//third_party/material_web_components:library",
"//ui/webui/resources/cr_elements:build_ts",
]
}
@@ -86,12 +88,19 @@ js_type_check("closure_compile") {
}
js_library("multidevice_setup_post_oobe") {
+ extra_deps = [
+ "//third_party/cros-components:cros_components_ts",
+ "//third_party/material_web_components:library",
+ ]
deps = [
":post_oobe_delegate",
"//ash/webui/common/resources/multidevice_setup:multidevice_setup",
"//ash/webui/common/resources/multidevice_setup:multidevice_setup_delegate",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
+ externs_list = [
+ "//third_party/cros-components/externs/cros_lottie_renderer_externs.js",
+ ]
}
js_library("post_oobe_delegate") {
diff --git a/chromium/chrome/browser/resources/chromeos/parent_access/BUILD.gn b/chromium/chrome/browser/resources/chromeos/parent_access/BUILD.gn
index 2af8659013c..91553df50d6 100644
--- a/chromium/chrome/browser/resources/chromeos/parent_access/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/parent_access/BUILD.gn
@@ -23,7 +23,10 @@ js_type_check("closure_compile_local") {
":parent_access_before",
":parent_access_controller",
":parent_access_disabled",
+ ":parent_access_error",
+ ":parent_access_offline",
":parent_access_screen",
+ ":parent_access_template",
":parent_access_ui",
":parent_access_ui_handler",
":utils",
@@ -40,6 +43,7 @@ js_type_check("closure_compile_local") {
js_library("parent_access_after") {
deps = [
":parent_access_screen",
+ ":parent_access_template",
"flows:local_web_approvals_after",
"//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
@@ -51,6 +55,7 @@ js_library("parent_access_after") {
js_library("parent_access_before") {
deps = [
":parent_access_screen",
+ ":parent_access_template",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
externs_list =
@@ -60,21 +65,40 @@ js_library("parent_access_before") {
js_library("parent_access_disabled") {
deps = [
":parent_access_screen",
+ ":parent_access_template",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
externs_list =
[ "//ui/webui/resources/cr_elements/cr_button/cr_button_externs.js" ]
}
+js_library("parent_access_error") {
+ deps = [
+ ":parent_access_template",
+ "//chrome/browser/resources/chromeos/supervision:supervised_user_error",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+}
+
+js_library("parent_access_offline") {
+ deps = [
+ ":parent_access_template",
+ "//chrome/browser/resources/chromeos/supervision:supervised_user_offline",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+}
+
js_library("parent_access_app") {
+ extra_deps =
+ [ "//ui/webui/resources/cr_components/color_change_listener:build_ts" ]
deps = [
":parent_access_after",
":parent_access_before",
":parent_access_disabled",
+ ":parent_access_error",
+ ":parent_access_offline",
":parent_access_ui",
":parent_access_ui_handler",
- "//chrome/browser/resources/chromeos/supervision:supervised_user_error",
- "//chrome/browser/resources/chromeos/supervision:supervised_user_offline",
"//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
@@ -88,9 +112,18 @@ js_library("parent_access_controller") {
]
}
+js_library("parent_access_template") {
+ deps = [
+ ":parent_access_ui_handler",
+ "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+}
+
js_library("parent_access_ui") {
deps = [
":parent_access_controller",
+ ":parent_access_template",
":parent_access_ui_handler",
"//ash/webui/common/resources:load_time_data.m",
"//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js",
@@ -122,6 +155,9 @@ html_to_js("web_components") {
"parent_access_app.js",
"parent_access_before.js",
"parent_access_disabled.js",
+ "parent_access_error.js",
+ "parent_access_offline.js",
+ "parent_access_template.js",
"parent_access_ui.js",
]
}
diff --git a/chromium/chrome/browser/resources/chromeos/sensor_info/BUILD.gn b/chromium/chrome/browser/resources/chromeos/sensor_info/BUILD.gn
new file mode 100644
index 00000000000..7e6702425b3
--- /dev/null
+++ b/chromium/chrome/browser/resources/chromeos/sensor_info/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//ui/webui/resources/tools/build_webui.gni")
+
+build_webui("build") {
+ grd_prefix = "sensor_info"
+
+ static_files = [
+ "sensor_info.html",
+ "sensor_info.css",
+ ]
+
+ web_component_files = [ "app.ts" ]
+
+ non_web_component_files = [
+ # For example the BrowserProxy file would go here.
+ ]
+
+ ts_deps = [
+ "//third_party/polymer/v3_0:library",
+ "//ui/webui/resources/js:build_ts",
+ ]
+}
diff --git a/chromium/chrome/browser/resources/chromeos/status_area_internals/BUILD.gn b/chromium/chrome/browser/resources/chromeos/status_area_internals/BUILD.gn
new file mode 100644
index 00000000000..29550e5f915
--- /dev/null
+++ b/chromium/chrome/browser/resources/chromeos/status_area_internals/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//ui/webui/resources/tools/build_webui.gni")
+
+build_webui("build") {
+ grd_prefix = "status_area_internals"
+
+ static_files = [ "main.html" ]
+ web_component_files = [
+ "privacy_indicator_app.ts",
+ "privacy_indicator_app_manager.ts",
+ "status_area_internals.ts",
+ ]
+
+ ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+ ts_deps = [
+ "//third_party/polymer/v3_0:library",
+ "//ui/webui/resources/cr_elements:build_ts",
+ "//ui/webui/resources/js:build_ts",
+ ]
+}
diff --git a/chromium/chrome/browser/resources/component_extension_resources.grd b/chromium/chrome/browser/resources/component_extension_resources.grd
index 98a751057c1..b67ac01335f 100644
--- a/chromium/chrome/browser/resources/component_extension_resources.grd
+++ b/chromium/chrome/browser/resources/component_extension_resources.grd
@@ -40,11 +40,6 @@
<include name="IDR_DESK_API_DESK_API_BUNDLE_JS" file="${root_gen_dir}/chrome/browser/resources/chromeos/desk_api/desk_api_bundle.js" use_base_dir="false" resource_path="chromeos/desk_api/desk_api_bundle.js" type="BINDATA" />
</if>
- <include name="IDR_IDENTITY_API_SCOPE_APPROVAL_BACKGROUND_JS" file="identity_scope_approval_dialog/background.js" type="BINDATA" />
- <include name="IDR_IDENTITY_API_SCOPE_APPROVAL_DIALOG_CSS" file="identity_scope_approval_dialog/scope_approval_dialog.css" type="BINDATA" />
- <include name="IDR_IDENTITY_API_SCOPE_APPROVAL_DIALOG" file="identity_scope_approval_dialog/scope_approval_dialog.html" type="BINDATA" />
- <include name="IDR_IDENTITY_API_SCOPE_APPROVAL_DIALOG_JS" file="identity_scope_approval_dialog/scope_approval_dialog.js" type="BINDATA" />
- <include name="IDR_IDENTITY_API_SCOPE_APPROVAL_INJECT_JS" file="identity_scope_approval_dialog/inject.js" type="BINDATA" />
<if expr="chromeos_ash">
<include name="IDR_ARC_SUPPORT_BACKGROUND_JS" file="chromeos/arc_support/background.js" type="BINDATA" />
<include name="IDR_ARC_SUPPORT_BUBBLE_CSS" file="chromeos/arc_support/bubble.css" flattenhtml="true" type="BINDATA" />
@@ -83,6 +78,7 @@
<structures>
<structure type="lottie" name="IDS_ARC_INPUT_OVERLAY_ONBOARDING_ILLUSTRATION_DARK_JSON" file="chromeos/arc_input_overlay/onboarding_illustration_dark.json" compress="gzip" />
<structure type="lottie" name="IDS_ARC_INPUT_OVERLAY_ONBOARDING_ILLUSTRATION_LIGHT_JSON" file="chromeos/arc_input_overlay/onboarding_illustration_light.json" compress="gzip" />
+ <structure type="lottie" name="IDS_ARC_INPUT_OVERLAY_ZERO_STATE_ILLUSTRATION_JSON" file="chromeos/arc_input_overlay/zero_state_illustration.json" compress="gzip" />
</structures>
</release>
</grit>
diff --git a/chromium/chrome/browser/resources/device_log_ui/device_log_ui.html b/chromium/chrome/browser/resources/device_log_ui/device_log_ui.html
index 88ee49dbc52..06a44bbc891 100644
--- a/chromium/chrome/browser/resources/device_log_ui/device_log_ui.html
+++ b/chromium/chrome/browser/resources/device_log_ui/device_log_ui.html
@@ -92,6 +92,10 @@
<input id="log-type-extensions" type="checkbox">
<span>$i18n{logTypeExtensionsText}</span>
</label>
+ <label>
+ <input id="log-type-display" type="checkbox">
+ <span>$i18n{logTypeDisplayText}</span>
+ </label>
</div>
<div id="log-container"></div>
</body>
diff --git a/chromium/chrome/browser/resources/downloads/COMMON_METADATA b/chromium/chrome/browser/resources/downloads/COMMON_METADATA
new file mode 100644
index 00000000000..6bf7cbfb89f
--- /dev/null
+++ b/chromium/chrome/browser/resources/downloads/COMMON_METADATA
@@ -0,0 +1,3 @@
+monorail {
+ component: "UI>Browser>Downloads"
+}
diff --git a/chromium/chrome/browser/resources/downloads/DIR_METADATA b/chromium/chrome/browser/resources/downloads/DIR_METADATA
index 6bf7cbfb89f..55459419c70 100644
--- a/chromium/chrome/browser/resources/downloads/DIR_METADATA
+++ b/chromium/chrome/browser/resources/downloads/DIR_METADATA
@@ -1,3 +1 @@
-monorail {
- component: "UI>Browser>Downloads"
-}
+mixins: "//chrome/browser/resources/downloads/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/downloads/constants.ts b/chromium/chrome/browser/resources/downloads/constants.ts
index e0938ab33e1..52bd88ee275 100644
--- a/chromium/chrome/browser/resources/downloads/constants.ts
+++ b/chromium/chrome/browser/resources/downloads/constants.ts
@@ -6,20 +6,21 @@
* Explains why a download is in DANGEROUS state.
*/
export enum DangerType {
- NOT_DANGEROUS = 'NOT_DANGEROUS',
- DANGEROUS_FILE = 'DANGEROUS_FILE',
- DANGEROUS_URL = 'DANGEROUS_URL',
+ BLOCKED_PASSWORD_PROTECTED = 'BLOCKED_PASSWORD_PROTECTED',
+ BLOCKED_TOO_LARGE = 'BLOCKED_TOO_LARGE',
+ DANGEROUS_ACCOUNT_COMPROMISE = 'DANGEROUS_ACCOUNT_COMPROMISE',
DANGEROUS_CONTENT = 'DANGEROUS_CONTENT',
- UNCOMMON_CONTENT = 'UNCOMMON_CONTENT',
+ DANGEROUS_FILE = 'DANGEROUS_FILE',
DANGEROUS_HOST = 'DANGEROUS_HOST',
- POTENTIALLY_UNWANTED = 'POTENTIALLY_UNWANTED',
- DEEP_SCANNED_SAFE = 'DEEP_SCANNED_SAFE',
+ DANGEROUS_URL = 'DANGEROUS_URL',
+ DEEP_SCANNED_FAILED = 'DEEP_SCANNED_FAILED',
DEEP_SCANNED_OPENED_DANGEROUS = 'DEEP_SCANNED_OPENED_DANGEROUS',
- SENSITIVE_CONTENT_WARNING = 'SENSITIVE_CONTENT_WARNING',
+ DEEP_SCANNED_SAFE = 'DEEP_SCANNED_SAFE',
+ NOT_DANGEROUS = 'NOT_DANGEROUS',
+ POTENTIALLY_UNWANTED = 'POTENTIALLY_UNWANTED',
SENSITIVE_CONTENT_BLOCK = 'SENSITIVE_CONTENT_BLOCK',
- BLOCKED_TOO_LARGE = 'BLOCKED_TOO_LARGE',
- BLOCKED_PASSWORD_PROTECTED = 'BLOCKED_PASSWORD_PROTECTED',
- DANGEROUS_ACCOUNT_COMPROMISE = 'DANGEROUS_ACCOUNT_COMPROMISE',
+ SENSITIVE_CONTENT_WARNING = 'SENSITIVE_CONTENT_WARNING',
+ UNCOMMON_CONTENT = 'UNCOMMON_CONTENT',
}
/**
diff --git a/chromium/chrome/browser/resources/downloads/icons.html b/chromium/chrome/browser/resources/downloads/icons.html
index 5315618cea5..1232f1aabbd 100644
--- a/chromium/chrome/browser/resources/downloads/icons.html
+++ b/chromium/chrome/browser/resources/downloads/icons.html
@@ -5,7 +5,11 @@
These icons are copied from Polymer's iron-icons and kept in sorted order.
See http://goo.gl/Y1OdAq for instructions on adding additional icons.
-->
- <g id="remove-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"></path></g>
+ <g id="dangerous"><!-- Source: https://fonts.google.com/icons -->
+ <path
+ d="M 8.25 21 L 3 15.75 L 3 8.25 L 8.25 3 L 15.75 3 L 21 8.25 L 21 15.75 L 15.75 21 Z M 9.148438 16.25 L 12 13.398438 L 14.851562 16.25 L 16.25 14.851562 L 13.398438 12 L 16.25 9.148438 L 14.851562 7.75 L 12 10.601562 L 9.148438 7.75 L 7.75 9.148438 L 10.601562 12 L 7.75 14.851562 Z M 9.148438 16.25">
+ </path>
+ </g>
</defs>
</svg>
</iron-iconset-svg>
diff --git a/chromium/chrome/browser/resources/downloads/item.html b/chromium/chrome/browser/resources/downloads/item.html
index bf292d05098..886214e7814 100644
--- a/chromium/chrome/browser/resources/downloads/item.html
+++ b/chromium/chrome/browser/resources/downloads/item.html
@@ -43,6 +43,7 @@
margin: 6px auto;
min-height: 103px;
width: var(--downloads-card-width);
+ max-width: calc(100% - 2 * var(--downloads-card-margin));
}
#content.is-active {
@@ -122,13 +123,6 @@
overflow: hidden; /* Reduces file icon flicker on initial load. */
}
- #content:-webkit-any(.show-progress, .dangerous) #file-icon-wrapper {
- /* TODO(dbeam): animate from top-aligned to centered when items finish?
- */
- align-self: flex-start;
- padding-top: 16px;
- }
-
#content:not(.is-active) .icon {
-webkit-filter: grayscale(100%);
opacity: .5;
@@ -175,9 +169,13 @@
}
@media (prefers-color-scheme: light) {
- .is-active :-webkit-any(#name, #file-link, #show) {
+ .is-active :-webkit-any(#file-link, #show) {
color: var(--google-blue-600);
}
+
+ .is-active #name {
+ color: var(--cr-primary-text-color);
+ }
}
#name {
@@ -205,13 +203,13 @@
}
#progress,
- #description:not(:empty),
+ .description:not(:empty),
.controls {
margin-top: 16px;
}
@media (prefers-color-scheme: light) {
- .is-active #description {
+ .is-active .description {
color: #616161;
}
}
@@ -297,9 +295,9 @@
<div id="file-icon-wrapper" class="icon-wrapper" aria-hidden="true">
<img class="icon" id="file-icon" alt="" hidden="[[!useFileIcon_]]"
icon-color$="[[computeIconColor_(isDangerous_, data.dangerType,
- useFileIcon_)]]">
+ useFileIcon_, updateDeepScanningUx_)]]">
<iron-icon class="icon" icon$="[[computeIcon_(
- isDangerous_, data.dangerType, useFileIcon_)]]"
+ isDangerous_, data.dangerType, useFileIcon_, updateDeepScanningUx_)]]"
hidden="[[useFileIcon_]]" icon-color$="[[computeIconColor_(isDangerous_,
data.dangerType, useFileIcon_)]]"></iron-icon>
</div>
@@ -307,13 +305,13 @@
<div id="details">
<div id="title-area" role="gridcell"><!--
Can't have any line breaks.
- --><a is="action-link" id="file-link" href="[[data.url]]"
+ --><a is="action-link" id="file-link" href="[[data.url.url]]"
on-click="onFileLinkClick_" focus-row-control
focus-type="fileLink"
- hidden="[[!completelyOnDisk_]]">[[data.fileName]]</a><!--
+ hidden="[[!shouldLinkFilename_]]">[[data.fileName]]</a><!--
Before #name.
--><span id="name"
- hidden="[[completelyOnDisk_]]">[[data.fileName]]</span>
+ hidden="[[shouldLinkFilename_]]">[[data.fileName]]</span>
<span id="tag">[[computeTag_(data.state,
data.lastReasonText,
data.fileExternallyRemoved)]]</span>
@@ -321,23 +319,31 @@
<div role="gridcell">
<a id="url" target="_blank" on-click="onUrlClick_" focus-row-control
- focus-type="url">[[chopUrl_(data.url)]]</a>
+ focus-type="url">[[getDisplayUrlStr_(data.displayUrl)]]</a>
</div>
- <div id="description" role="gridcell"
+ <div class="description" role="gridcell"
hidden$="[[!computeDescriptionVisible_(data.*)]]">
[[computeDescription_(
data.state,
data.dangerType,
data.fileName,
- data.progressStatusText)]]
+ data.progressStatusText,
+ updateDeepScanningUx_)]]
+ </div>
+
+ <div class="description" role="gridcell"
+ hidden="[[!computeSecondLineVisible_(data.state,
+ updateDeepScanningUx_)]]">
+ $i18n{asyncScanningDownloadDescSecond}
</div>
<template is="dom-if" if="[[showProgress_]]">
<div role="gridcell">
<paper-progress id="progress"
- indeterminate="[[isIndeterminate_(data.percent)]]"
- value="[[data.percent]]"></paper-progress>
+ indeterminate="[[isIndeterminate_(data.percent,
+ updateDeepScanningUx_)]]" value="[[data.percent]]">
+ </paper-progress>
</div>
</template>
@@ -394,6 +400,14 @@
</cr-button>
</span>
</template>
+ <template is="dom-if" if="[[showOpenAnyway_]]">
+ <span role="gridcell">
+ <cr-button on-click="onOpenAnywayClick_" id="openAnyway"
+ focus-row-control focus-type="open">
+ $i18n{controlOpenAnyway}
+ </cr-button>
+ </span>
+ </template>
<span id="controlled-by"><!-- Text populated dynamically. --></span>
</div>
diff --git a/chromium/chrome/browser/resources/downloads/item.ts b/chromium/chrome/browser/resources/downloads/item.ts
index 48cbbd297f0..be7893058d3 100644
--- a/chromium/chrome/browser/resources/downloads/item.ts
+++ b/chromium/chrome/browser/resources/downloads/item.ts
@@ -21,8 +21,10 @@ import {FocusRowMixin} from 'chrome://resources/cr_elements/focus_row_mixin.js';
import {assert} from 'chrome://resources/js/assert_ts.js';
import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {mojoString16ToString} from 'chrome://resources/js/mojo_type_util.js';
import {sanitizeInnerHtml} from 'chrome://resources/js/parse_html_subset.js';
import {htmlEscape} from 'chrome://resources/js/util_ts.js';
+import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
import {beforeNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {BrowserProxy} from './browser_proxy.js';
@@ -64,9 +66,16 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
value: true,
},
+ shouldLinkFilename_: {
+ computed: 'computeShouldLinkFilename_(' +
+ 'data.dangerType, completelyOnDisk_)',
+ type: Boolean,
+ value: true,
+ },
+
hasShowInFolderLink_: {
computed: 'computeHasShowInFolderLink_(' +
- 'data.state, data.fileExternallyRemoved)',
+ 'data.state, data.fileExternallyRemoved, data.dangerType)',
type: Boolean,
value: true,
},
@@ -120,19 +129,20 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
},
showCancel_: {
- computed: 'computeShowCancel_(data.state)',
+ computed: 'computeShowCancel_(data.state, updateDeepScanningUx_)',
type: Boolean,
value: false,
},
showProgress_: {
- computed: 'computeShowProgress_(showCancel_, data.percent)',
+ computed: 'computeShowProgress_(showCancel_, data.percent,' +
+ 'updateDeepScanningUx_)',
type: Boolean,
value: false,
},
showOpenNow_: {
- computed: 'computeShowOpenNow_(data.state)',
+ computed: 'computeShowOpenNow_(data.state, updateDeepScanningUx_)',
type: Boolean,
value: false,
},
@@ -143,6 +153,22 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
value: false,
},
+ showOpenAnyway_: {
+ computed: 'computeShowOpenAnyway_(data.dangerType)',
+ type: Boolean,
+ value: false,
+ },
+
+ updateDeepScanningUx_: {
+ type: Boolean,
+ value: () => loadTimeData.getBoolean('updateDeepScanningUX'),
+ },
+
+ improvedDownloadWarningsUx_: {
+ type: Boolean,
+ value: () => loadTimeData.getBoolean('improvedDownloadWarningsUX'),
+ },
+
useFileIcon_: Boolean,
};
}
@@ -168,6 +194,9 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
private showProgress_: boolean;
private useFileIcon_: boolean;
private restoreFocusAfterCancel_: boolean = false;
+ private updateDeepScanningUx_: boolean;
+ private improvedDownloadWarningsUx_: boolean;
+ private completelyOnDisk_: boolean;
override overrideCustomEquivalent: boolean;
constructor() {
@@ -205,10 +234,10 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
}
/**
- * @return A reasonably long URL.
+ * @return A JS string of the display URL.
*/
- private chopUrl_(url: string): string {
- return url.slice(0, 300);
+ private getDisplayUrlStr_(displayUrl: String16): string {
+ return mojoString16ToString(displayUrl);
}
private computeClass_(): string {
@@ -234,8 +263,21 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
!this.data.fileExternallyRemoved;
}
+ private computeShouldLinkFilename_(): boolean {
+ if (this.data === undefined) {
+ return false;
+ }
+
+ return this.completelyOnDisk_ &&
+ this.data.dangerType !== DangerType.DEEP_SCANNED_FAILED;
+ }
+
private computeHasShowInFolderLink_(): boolean {
- return loadTimeData.getBoolean('hasShowInFolder') &&
+ if (this.data === undefined) {
+ return false;
+ }
+
+ return this.data.dangerType !== DangerType.DEEP_SCANNED_FAILED &&
this.computeCompletelyOnDisk_();
}
@@ -266,16 +308,29 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
return this.computeDescription_() !== '';
}
+ private computeSecondLineVisible_(): boolean {
+ return this.updateDeepScanningUx_ && this.data &&
+ this.data.state === States.ASYNC_SCANNING;
+ }
+
private computeDescription_(): string {
+ if (!this.data) {
+ return '';
+ }
+
const data = this.data;
switch (data.state) {
case States.COMPLETE:
switch (data.dangerType) {
case DangerType.DEEP_SCANNED_SAFE:
- return loadTimeData.getString('deepScannedSafeDesc');
+ return this.updateDeepScanningUx_ ?
+ '' :
+ loadTimeData.getString('deepScannedSafeDesc');
case DangerType.DEEP_SCANNED_OPENED_DANGEROUS:
return loadTimeData.getString('deepScannedOpenedDangerousDesc');
+ case DangerType.DEEP_SCANNED_FAILED:
+ return loadTimeData.getString('deepScannedFailedDesc');
}
break;
@@ -337,6 +392,10 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
return 'cr:warning';
}
+ if (dangerType === DangerType.DEEP_SCANNED_FAILED) {
+ return 'cr:info';
+ }
+
const ERROR_TYPES = [
DangerType.SENSITIVE_CONTENT_BLOCK,
DangerType.BLOCKED_TOO_LARGE,
@@ -347,7 +406,7 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
}
if (this.data.state === States.ASYNC_SCANNING) {
- return 'cr:info';
+ return this.updateDeepScanningUx_ ? 'cr:warning' : 'cr:info';
}
if (this.data.state === States.PROMPT_FOR_SCANNING) {
@@ -355,7 +414,8 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
}
}
if (this.isDangerous_) {
- return 'cr:error';
+ return this.improvedDownloadWarningsUx_ ? 'downloads:dangerous' :
+ 'cr:error';
}
if (!this.useFileIcon_) {
return 'cr:insert-drive-file';
@@ -368,7 +428,8 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
const dangerType = this.data.dangerType as DangerType;
if ((loadTimeData.getBoolean('requestsApVerdicts') &&
dangerType === DangerType.UNCOMMON_CONTENT) ||
- dangerType === DangerType.SENSITIVE_CONTENT_WARNING) {
+ dangerType === DangerType.SENSITIVE_CONTENT_WARNING ||
+ dangerType === DangerType.DEEP_SCANNED_FAILED) {
return 'yellow';
}
@@ -382,7 +443,7 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
}
if (this.data.state === States.ASYNC_SCANNING) {
- return 'grey';
+ return this.updateDeepScanningUx_ ? 'yellow' : 'grey';
}
if (this.data.state === States.PROMPT_FOR_SCANNING) {
@@ -463,26 +524,35 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
}
private computeShowCancel_(): boolean {
- return this.data.state === States.IN_PROGRESS ||
- this.data.state === States.PAUSED ||
- this.data.state === States.ASYNC_SCANNING;
+ return !!this.data &&
+ (this.data.state === States.IN_PROGRESS ||
+ this.data.state === States.PAUSED ||
+ (this.data.state === States.ASYNC_SCANNING &&
+ !this.updateDeepScanningUx_));
}
private computeShowProgress_(): boolean {
+ if (this.data && this.data.state === States.ASYNC_SCANNING) {
+ return true;
+ }
return this.showCancel_ && this.data.percent >= -1 &&
- this.data.state !== States.ASYNC_SCANNING &&
this.data.state !== States.PROMPT_FOR_SCANNING;
}
private computeShowOpenNow_(): boolean {
const allowOpenNow = loadTimeData.getBoolean('allowOpenNow');
- return this.data.state === States.ASYNC_SCANNING && allowOpenNow;
+ return !!this.data && this.data.state === States.ASYNC_SCANNING &&
+ allowOpenNow && !this.updateDeepScanningUx_;
}
private computeShowDeepScan_(): boolean {
return this.data.state === States.PROMPT_FOR_SCANNING;
}
+ private computeShowOpenAnyway_(): boolean {
+ return this.data.dangerType === DangerType.DEEP_SCANNED_FAILED;
+ }
+
private computeTag_(): string {
switch (this.data.state) {
case States.CANCELLED:
@@ -501,7 +571,8 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
}
private isIndeterminate_(): boolean {
- return this.data.percent === -1;
+ return this.data.state === States.ASYNC_SCANNING ||
+ this.data.percent === -1;
}
private observeControlledBy_() {
@@ -514,6 +585,11 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
}
private observeIsDangerous_() {
+ const removeFileUrlLinks = () => {
+ this.$.url.removeAttribute('href');
+ this.$['file-link'].removeAttribute('href');
+ };
+
if (!this.data) {
return;
}
@@ -522,30 +598,44 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
DangerType.SENSITIVE_CONTENT_BLOCK,
DangerType.BLOCKED_TOO_LARGE,
DangerType.BLOCKED_PASSWORD_PROTECTED,
+ DangerType.DEEP_SCANNED_FAILED,
];
+ // Handle various dangerous cases.
if (this.isDangerous_) {
- this.$.url.removeAttribute('href');
+ removeFileUrlLinks();
this.useFileIcon_ = false;
- } else if (OVERRIDDEN_ICON_TYPES.includes(
- this.data.dangerType as DangerType)) {
+ return;
+ }
+ if (OVERRIDDEN_ICON_TYPES.includes(this.data.dangerType as DangerType)) {
this.useFileIcon_ = false;
- } else if (this.data.state === States.ASYNC_SCANNING) {
+ return;
+ }
+ if (this.data.state === States.ASYNC_SCANNING) {
this.useFileIcon_ = false;
- } else if (this.data.state === States.PROMPT_FOR_SCANNING) {
+ return;
+ }
+ if (this.data.state === States.PROMPT_FOR_SCANNING) {
this.useFileIcon_ = false;
+ return;
+ }
+
+ // The file is not dangerous. Link the url if supplied.
+ if (this.data.url) {
+ this.$.url.href = this.data.url.url;
} else {
- this.$.url.href = this.data.url;
- const path = this.data.filePath;
- IconLoaderImpl.getInstance()
- .loadIcon(this.$['file-icon'], path)
- .then(success => {
- if (path === this.data.filePath &&
- this.data.state !== States.ASYNC_SCANNING) {
- this.useFileIcon_ = success;
- }
- });
+ removeFileUrlLinks();
}
+
+ const path = this.data.filePath;
+ IconLoaderImpl.getInstance()
+ .loadIcon(this.$['file-icon'], path)
+ .then(success => {
+ if (path === this.data.filePath &&
+ this.data.state !== States.ASYNC_SCANNING) {
+ this.useFileIcon_ = success;
+ }
+ });
}
private onCancelClick_() {
@@ -573,6 +663,10 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
this.mojoHandler_!.reviewDangerousRequiringGesture(this.data.id);
}
+ private onOpenAnywayClick_() {
+ this.mojoHandler_!.openFileRequiringGesture(this.data.id);
+ }
+
private onDragStart_(e: Event) {
e.preventDefault();
this.mojoHandler_!.drag(this.data.id);
@@ -584,6 +678,9 @@ export class DownloadsItemElement extends DownloadsItemElementBase {
}
private onUrlClick_() {
+ if (!this.data.url) {
+ return;
+ }
chrome.send(
'metricsHandler:recordAction', ['Downloads_OpenUrlOfDownloadedItem']);
}
diff --git a/chromium/chrome/browser/resources/downloads/manager.html b/chromium/chrome/browser/resources/downloads/manager.html
index 7e1005946e8..5d6ece79a3e 100644
--- a/chromium/chrome/browser/resources/downloads/manager.html
+++ b/chromium/chrome/browser/resources/downloads/manager.html
@@ -25,7 +25,7 @@
downloads-item,
#downloadsList {
--downloads-card-margin: 24px;
- --downloads-card-width: 680px;
+ --downloads-card-width: clamp(550px, 80%, 680px);
}
#downloadsList {
diff --git a/chromium/chrome/browser/resources/engagement/BUILD.gn b/chromium/chrome/browser/resources/engagement/BUILD.gn
index 29f5516928a..f0f980c533b 100644
--- a/chromium/chrome/browser/resources/engagement/BUILD.gn
+++ b/chromium/chrome/browser/resources/engagement/BUILD.gn
@@ -7,12 +7,14 @@ import("//ui/webui/resources/tools/build_webui.gni")
build_webui("build") {
grd_prefix = "engagement"
static_files = [ "site_engagement.html" ]
- non_web_component_files = [ "site_engagement.ts" ]
+ web_component_files = [ "app.ts" ]
+ html_to_wrapper_template = "native"
mojo_files_deps =
[ "//components/site_engagement/core/mojom:mojo_bindings_ts__generator" ]
mojo_files = [ "$root_gen_dir/components/site_engagement/core/mojom/site_engagement_details.mojom-webui.ts" ]
+ ts_composite = true
ts_deps = [
"//ui/webui/resources/js:build_ts",
"//ui/webui/resources/mojo:build_ts",
diff --git a/chromium/chrome/browser/resources/engagement/COMMON_METADATA b/chromium/chrome/browser/resources/engagement/COMMON_METADATA
new file mode 100644
index 00000000000..dd2971ebd38
--- /dev/null
+++ b/chromium/chrome/browser/resources/engagement/COMMON_METADATA
@@ -0,0 +1,3 @@
+monorail {
+ component: "Internals>SiteEngagement"
+}
diff --git a/chromium/chrome/browser/resources/engagement/DIR_METADATA b/chromium/chrome/browser/resources/engagement/DIR_METADATA
index a438d8de140..7d55937abef 100644
--- a/chromium/chrome/browser/resources/engagement/DIR_METADATA
+++ b/chromium/chrome/browser/resources/engagement/DIR_METADATA
@@ -1,3 +1 @@
-monorail {
- component: "Internals>Permissions>SiteEngagement"
-}
+mixins: "//chrome/browser/resources/engagement/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/engagement/app.html b/chromium/chrome/browser/resources/engagement/app.html
new file mode 100644
index 00000000000..8133f9314f9
--- /dev/null
+++ b/chromium/chrome/browser/resources/engagement/app.html
@@ -0,0 +1,107 @@
+<style>
+ :host {
+ --color-row-hover: rgb(255, 255, 187);
+ --color-engagement-bar: black;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ :host {
+ --color-row-hover: rgb(3, 220, 176);
+ --color-engagement-bar: white;
+ }
+ }
+
+ table {
+ border-collapse: collapse;
+ }
+
+ table td,
+ table th {
+ border: 1px solid #777;
+ padding-left: 4px;
+ padding-right: 4px;
+ }
+
+ table th {
+ background: rgb(224, 236, 255);
+ color: black;
+ cursor: pointer;
+ padding-bottom: 4px;
+ padding-right: 16px;
+ padding-top: 4px;
+ white-space: nowrap;
+ }
+
+ .engagement-bar {
+ background-color: var(--color-engagement-bar);
+ height: 2px;
+ }
+
+ .engagement-bar-cell {
+ border: none;
+ }
+
+ .origin-cell {
+ background-color: rgba(230, 230, 230, 0.5);
+ min-width: 500px;
+ }
+
+ .base-score-cell,
+ .bonus-score-cell,
+ .total-score-cell {
+ background-color: rgba(230, 230, 230, 0.5);
+ text-align: right;
+ }
+
+ .base-score-input {
+ border: 1px solid #ccc;
+ border-radius: 2px;
+ text-align: right;
+ width: 70px;
+ }
+
+ .base-score-input:focus {
+ border: 1px solid rgb(143, 185, 252);
+ box-shadow: 0 0 2px rgb(113, 158, 206);
+ outline: none;
+ }
+
+ .add-origin-button {
+ width: 10em;
+ }
+
+ table tr:hover {
+ background: var(--color-row-hover);
+ }
+
+ th.sort-column::after {
+ content: '▲';
+ position: absolute;
+ }
+
+ th[sort-reverse].sort-column::after {
+ content: '▼';
+ position: absolute;
+ }
+</style>
+<h1>Site Engagement</h1>
+<table>
+ <thead>
+ <tr id="engagement-table-header">
+ <th sort-key="origin">
+ Origin
+ </th>
+ <th sort-key="baseScore" sort-reverse>
+ Base
+ </th>
+ <th sort-key="bonusScore" sort-reverse>
+ Bonus
+ </th>
+ <th sort-key="totalScore" class="sort-column" sort-reverse>
+ Total
+ </th>
+ </tr>
+ </thead>
+ <tbody id="engagement-table-body">
+ </tbody>
+</table>
diff --git a/chromium/chrome/browser/resources/engagement/app.ts b/chromium/chrome/browser/resources/engagement/app.ts
new file mode 100644
index 00000000000..7e00da5f4d9
--- /dev/null
+++ b/chromium/chrome/browser/resources/engagement/app.ts
@@ -0,0 +1,297 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
+import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+
+import {getTemplate} from './app.html.js';
+import {SiteEngagementDetails, SiteEngagementDetailsProvider, SiteEngagementDetailsProviderInterface} from './site_engagement_details.mojom-webui.js';
+
+/**
+ * Rounds the supplied value to two decimal places of accuracy.
+ */
+function roundScore(score: number): number {
+ return Number(Math.round(score * 100) / 100);
+}
+
+/**
+ * Compares two SiteEngagementDetails objects based on |sortKey|.
+ * @param sortKey The name of the property to sort by.
+ * @return A negative number if |a| should be ordered before |b|, a
+ * positive number otherwise.
+ */
+function compareTableItem(
+ sortKey: string, a: {[k: string]: any}, b: {[k: string]: any}): number {
+ const val1 = a[sortKey];
+ const val2 = b[sortKey];
+
+ // Compare the hosts of the origin ignoring schemes.
+ if (sortKey === 'origin') {
+ return new URL(val1.url).host > new URL(val2.url).host ? 1 : -1;
+ }
+
+ if (sortKey === 'baseScore' || sortKey === 'bonusScore' ||
+ sortKey === 'totalScore') {
+ return val1 - val2;
+ }
+
+ assertNotReached('Unsupported sort key: ' + sortKey);
+}
+
+
+export class SiteEngagementAppElement extends CustomElement {
+ static get is() {
+ return 'site-engagement-app';
+ }
+
+ static override get template() {
+ return getTemplate();
+ }
+
+ private engagementTableBody: HTMLElement|null = null;
+ private info: SiteEngagementDetails[]|null = null;
+ engagementDetailsProvider: SiteEngagementDetailsProviderInterface =
+ SiteEngagementDetailsProvider.getRemote();
+ private updateInterval: number|null = null;
+ private sortKey: string = 'totalScore';
+ private sortReverse: boolean = true;
+ private whenPopulatedResolver: PromiseResolver<void> = new PromiseResolver();
+
+ connectedCallback() {
+ const engagementTableHeader =
+ this.getRequiredElement<HTMLElement>('#engagement-table-header');
+ this.engagementTableBody =
+ this.getRequiredElement<HTMLElement>('#engagement-table-body');
+
+ const headers = engagementTableHeader.children;
+ for (let i = 0; i < headers.length; i++) {
+ headers[i]!.addEventListener('click', e => {
+ const target = e.target as HTMLElement;
+ const newSortKey = target.getAttribute('sort-key');
+ assert(newSortKey);
+ if (this.sortKey === newSortKey) {
+ this.sortReverse = !this.sortReverse;
+ } else {
+ this.sortKey = newSortKey;
+ this.sortReverse = false;
+ }
+ const oldSortColumn =
+ this.getRequiredElement<HTMLElement>('.sort-column');
+ oldSortColumn.classList.remove('sort-column');
+ target.classList.add('sort-column');
+ target.toggleAttribute('sort-reverse', this.sortReverse);
+ this.renderTable();
+ });
+ }
+
+ this.updateEngagementTable();
+ this.enableAutoupdate();
+ }
+
+ /**
+ * Creates a single row in the engagement table.
+ * @param info The info to create the row from.
+ */
+ private createRow(info: SiteEngagementDetails): HTMLElement {
+ const originCell = document.createElement('td');
+ originCell.classList.add('origin-cell');
+ originCell.textContent = info.origin.url;
+
+ const baseScoreInput = document.createElement('input');
+ baseScoreInput.classList.add('base-score-input');
+ baseScoreInput.addEventListener('focus', () => this.disableAutoupdate());
+ baseScoreInput.addEventListener('blur', () => this.enableAutoupdate());
+ baseScoreInput.value = String(info.baseScore);
+
+ const baseScoreCell = document.createElement('td');
+ baseScoreCell.classList.add('base-score-cell');
+ baseScoreCell.appendChild(baseScoreInput);
+
+ const bonusScoreCell = document.createElement('td');
+ bonusScoreCell.classList.add('bonus-score-cell');
+ bonusScoreCell.textContent = String(info.installedBonus);
+
+ const totalScoreCell = document.createElement('td');
+ totalScoreCell.classList.add('total-score-cell');
+ totalScoreCell.textContent = String(info.totalScore);
+
+ const engagementBar = document.createElement('div');
+ engagementBar.classList.add('engagement-bar');
+ engagementBar.style.width = (info.totalScore * 4) + 'px';
+
+ const engagementBarCell = document.createElement('td');
+ engagementBarCell.classList.add('engagement-bar-cell');
+ engagementBarCell.appendChild(engagementBar);
+
+ const row = document.createElement('tr');
+ row.appendChild(originCell);
+ row.appendChild(baseScoreCell);
+ row.appendChild(bonusScoreCell);
+ row.appendChild(totalScoreCell);
+ row.appendChild(engagementBarCell);
+
+ baseScoreInput.addEventListener(
+ 'change',
+ (e: Event) =>
+ this.handleBaseScoreChange(info.origin, engagementBar, e));
+
+ return row;
+ }
+
+ disableAutoupdate() {
+ if (this.updateInterval) {
+ clearInterval(this.updateInterval);
+ }
+ this.updateInterval = null;
+ }
+
+ private enableAutoupdate() {
+ if (this.updateInterval) {
+ clearInterval(this.updateInterval);
+ }
+ this.updateInterval = setInterval(() => this.updateEngagementTable(), 5000);
+ }
+
+ /**
+ * Sets the base engagement score when a score input is changed.
+ * Resets the length of engagement-bar-cell to match the new score.
+ * Also resets the update interval.
+ * @param origin The origin of the engagement score to set.
+ */
+ private handleBaseScoreChange(origin: Url, barCell: HTMLElement, e: Event) {
+ const baseScoreInput = e.target as HTMLInputElement;
+ this.engagementDetailsProvider.setSiteEngagementBaseScoreForUrl(
+ origin, parseFloat(baseScoreInput.value));
+ barCell.style.width = (parseFloat(baseScoreInput.value) * 4) + 'px';
+ baseScoreInput.blur();
+ this.enableAutoupdate();
+ }
+
+ /**
+ * Adds a new origin with the given base score.
+ * @param originInput The text input containing the origin to add.
+ * @param scoreInput The text input containing the score to add.
+ */
+ private handleAddOrigin(
+ originInput: HTMLInputElement, scoreInput: HTMLInputElement) {
+ try {
+ // Validate the URL. If we don't validate here, IPC will kill this
+ // renderer on invalid URLs. Other checks like scheme are done on the
+ // browser side.
+ new URL(originInput.value);
+ } catch {
+ return;
+ }
+ const origin = new Url();
+ origin.url = originInput.value;
+ const score = parseFloat(scoreInput.value);
+
+ this.engagementDetailsProvider.setSiteEngagementBaseScoreForUrl(
+ origin, score);
+ scoreInput.blur();
+ this.updateEngagementTable();
+ this.enableAutoupdate();
+ }
+
+ /**
+ * Remove all rows from the engagement table.
+ */
+ private clearTable() {
+ assert(this.engagementTableBody);
+ this.engagementTableBody.innerHTML = window.trustedTypes!.emptyHTML;
+ }
+
+ /**
+ * Sort the engagement info based on |sortKey| and |sortReverse|.
+ */
+ private sortInfo() {
+ assert(this.info);
+ this.info.sort((a, b) => {
+ return (this.sortReverse ? -1 : 1) * compareTableItem(this.sortKey, a, b);
+ });
+ }
+
+ /**
+ * Regenerates the engagement table from |info|.
+ */
+ private renderTable() {
+ this.clearTable();
+ this.sortInfo();
+
+ assert(this.info);
+ this.info.forEach((info) => {
+ // Round all scores to 2 decimal places.
+ info.baseScore = roundScore(info.baseScore);
+ info.installedBonus = roundScore(info.installedBonus);
+ info.totalScore = roundScore(info.totalScore);
+
+ assert(this.engagementTableBody);
+ this.engagementTableBody.appendChild(this.createRow(info));
+ });
+
+ // Add another row for adding a new origin.
+ const originInput = document.createElement('input');
+ originInput.classList.add('origin-input');
+ originInput.addEventListener('focus', () => this.disableAutoupdate());
+ originInput.addEventListener('blur', () => this.enableAutoupdate());
+ originInput.value = 'http://example.com';
+
+ const originCell = document.createElement('td');
+ originCell.appendChild(originInput);
+
+ const baseScoreInput = document.createElement('input');
+ baseScoreInput.classList.add('base-score-input');
+ baseScoreInput.addEventListener('focus', () => this.disableAutoupdate());
+ baseScoreInput.addEventListener('blur', () => this.enableAutoupdate());
+ baseScoreInput.value = '0';
+
+ const baseScoreCell = document.createElement('td');
+ baseScoreCell.classList.add('base-score-cell');
+ baseScoreCell.appendChild(baseScoreInput);
+
+ const addButton = document.createElement('button');
+ addButton.textContent = 'Add';
+ addButton.classList.add('add-origin-button');
+
+ const buttonCell = document.createElement('td');
+ buttonCell.colSpan = 2;
+ buttonCell.classList.add('base-score-cell');
+ buttonCell.appendChild(addButton);
+
+ const row = document.createElement('tr');
+ row.appendChild(originCell);
+ row.appendChild(baseScoreCell);
+ row.appendChild(buttonCell);
+ addButton.addEventListener(
+ 'click', () => this.handleAddOrigin(originInput, baseScoreInput));
+
+ assert(this.engagementTableBody);
+ this.engagementTableBody.appendChild(row);
+ }
+
+ /**
+ * Retrieve site engagement info and render the engagement table.
+ */
+ private async updateEngagementTable() {
+ // Populate engagement table.
+ this.info =
+ (await this.engagementDetailsProvider.getSiteEngagementDetails()).info;
+ this.renderTable();
+ this.whenPopulatedResolver.resolve();
+ }
+
+ whenPopulatedForTest() {
+ return this.whenPopulatedResolver.promise;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'site-engagement-app': SiteEngagementAppElement;
+ }
+}
+
+customElements.define(SiteEngagementAppElement.is, SiteEngagementAppElement);
diff --git a/chromium/chrome/browser/resources/engagement/site_engagement.html b/chromium/chrome/browser/resources/engagement/site_engagement.html
index a81bf12e0f8..d685935f55f 100644
--- a/chromium/chrome/browser/resources/engagement/site_engagement.html
+++ b/chromium/chrome/browser/resources/engagement/site_engagement.html
@@ -4,119 +4,15 @@
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
- <script src="site_engagement.js" type="module"></script>
<style>
- :root {
- --color-row-hover: rgb(255, 255, 187);
- --color-engagement-bar: black;
- }
-
- @media (prefers-color-scheme: dark) {
- :root {
- --color-row-hover: rgb(3, 220, 176);
- --color-engagement-bar: white;
- }
- }
body {
font-family: 'Roboto', 'Noto', sans-serif;
font-size: 14px;
}
-
- table {
- border-collapse: collapse;
- }
-
- table td,
- table th {
- border: 1px solid #777;
- padding-left: 4px;
- padding-right: 4px;
- }
-
- table th {
- background: rgb(224, 236, 255);
- color: black;
- cursor: pointer;
- padding-bottom: 4px;
- padding-right: 16px;
- padding-top: 4px;
- white-space: nowrap;
- }
-
- .engagement-bar {
- background-color: var(--color-engagement-bar);
- height: 2px;
- }
-
- .engagement-bar-cell {
- border: none;
- }
-
- .origin-cell {
- background-color: rgba(230, 230, 230, 0.5);
- min-width: 500px;
- }
-
- .base-score-cell,
- .bonus-score-cell,
- .total-score-cell {
- background-color: rgba(230, 230, 230, 0.5);
- text-align: right;
- }
-
- .base-score-input {
- border: 1px solid #ccc;
- border-radius: 2px;
- text-align: right;
- width: 70px;
- }
-
- .base-score-input:focus {
- border: 1px solid rgb(143, 185, 252);
- box-shadow: 0 0 2px rgb(113, 158, 206);
- outline: none;
- }
-
- .add-origin-button {
- width: 10em;
- }
-
- table tr:hover {
- background: var(--color-row-hover);
- }
-
- th.sort-column::after {
- content: '▲';
- position: absolute;
- }
-
- th[sort-reverse].sort-column::after {
- content: '▼';
- position: absolute;
- }
</style>
</head>
<body>
- <h1>Site Engagement</h1>
- <table>
- <thead>
- <tr id="engagement-table-header">
- <th sort-key="origin">
- Origin
- </th>
- <th sort-key="baseScore" sort-reverse>
- Base
- </th>
- <th sort-key="bonusScore" sort-reverse>
- Bonus
- </th>
- <th sort-key="totalScore" class="sort-column" sort-reverse>
- Total
- </th>
- </tr>
- </thead>
- <tbody id="engagement-table-body">
- </tbody>
- </table>
+ <script src="app.js" type="module"></script>
+ <site-engagement-app></site-engagement-app>
</body>
</html>
diff --git a/chromium/chrome/browser/resources/engagement/site_engagement.ts b/chromium/chrome/browser/resources/engagement/site_engagement.ts
deleted file mode 100644
index ff92fa43e66..00000000000
--- a/chromium/chrome/browser/resources/engagement/site_engagement.ts
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
-import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
-
-import {SiteEngagementDetails, SiteEngagementDetailsProvider} from './site_engagement_details.mojom-webui.js';
-
-const pageIsPopulatedResolver = new PromiseResolver<void>();
-
-const whenPageIsPopulatedForTest = function() {
- return pageIsPopulatedResolver.promise;
-};
-
-function initialize() {
- const engagementDetailsProvider = SiteEngagementDetailsProvider.getRemote();
-
- const engagementTableBody =
- document.body.querySelector<HTMLElement>('#engagement-table-body')!;
- let updateInterval: number|null = null;
- let info: SiteEngagementDetails[]|null = null;
- let sortKey: string = 'totalScore';
- let sortReverse: boolean = true;
-
- // Set table header sort handlers.
- const engagementTableHeader =
- document.body.querySelector<HTMLElement>('#engagement-table-header');
- assert(engagementTableHeader);
- const headers = engagementTableHeader.children;
- for (let i = 0; i < headers.length; i++) {
- headers[i]!.addEventListener('click', e => {
- const target = e.target as HTMLElement;
- const newSortKey = target.getAttribute('sort-key');
- assert(newSortKey);
- if (sortKey === newSortKey) {
- sortReverse = !sortReverse;
- } else {
- sortKey = newSortKey;
- sortReverse = false;
- }
- const oldSortColumn = document.querySelector<HTMLElement>('.sort-column');
- assert(oldSortColumn);
- oldSortColumn.classList.remove('sort-column');
- target.classList.add('sort-column');
- target.toggleAttribute('sort-reverse', sortReverse);
- renderTable();
- });
- }
-
- /**
- * Creates a single row in the engagement table.
- * @param info The info to create the row from.
- */
- function createRow(info: SiteEngagementDetails): HTMLElement {
- const originCell = document.createElement('td');
- originCell.classList.add('origin-cell');
- originCell.textContent = info.origin.url;
-
- const baseScoreInput = document.createElement('input');
- baseScoreInput.classList.add('base-score-input');
- baseScoreInput.addEventListener('focus', disableAutoupdate);
- baseScoreInput.addEventListener('blur', enableAutoupdate);
- baseScoreInput.value = String(info.baseScore);
-
- const baseScoreCell = document.createElement('td');
- baseScoreCell.classList.add('base-score-cell');
- baseScoreCell.appendChild(baseScoreInput);
-
- const bonusScoreCell = document.createElement('td');
- bonusScoreCell.classList.add('bonus-score-cell');
- bonusScoreCell.textContent = String(info.installedBonus);
-
- const totalScoreCell = document.createElement('td');
- totalScoreCell.classList.add('total-score-cell');
- totalScoreCell.textContent = String(info.totalScore);
-
- const engagementBar = document.createElement('div');
- engagementBar.classList.add('engagement-bar');
- engagementBar.style.width = (info.totalScore * 4) + 'px';
-
- const engagementBarCell = document.createElement('td');
- engagementBarCell.classList.add('engagement-bar-cell');
- engagementBarCell.appendChild(engagementBar);
-
- const row = document.createElement('tr');
- row.appendChild(originCell);
- row.appendChild(baseScoreCell);
- row.appendChild(bonusScoreCell);
- row.appendChild(totalScoreCell);
- row.appendChild(engagementBarCell);
-
- baseScoreInput.addEventListener(
- 'change',
- handleBaseScoreChange.bind(undefined, info.origin, engagementBar));
-
- return row;
- }
-
- function disableAutoupdate() {
- if (updateInterval) {
- clearInterval(updateInterval);
- }
- updateInterval = null;
- }
-
- function enableAutoupdate() {
- if (updateInterval) {
- clearInterval(updateInterval);
- }
- updateInterval = setInterval(updateEngagementTable, 5000);
- }
-
- /**
- * Sets the base engagement score when a score input is changed.
- * Resets the length of engagement-bar-cell to match the new score.
- * Also resets the update interval.
- * @param origin The origin of the engagement score to set.
- */
- function handleBaseScoreChange(origin: Url, barCell: HTMLElement, e: Event) {
- const baseScoreInput = e.target as HTMLInputElement;
- engagementDetailsProvider.setSiteEngagementBaseScoreForUrl(
- origin, parseFloat(baseScoreInput.value));
- barCell.style.width = (parseFloat(baseScoreInput.value) * 4) + 'px';
- baseScoreInput.blur();
- enableAutoupdate();
- }
-
- /**
- * Adds a new origin with the given base score.
- * @param originInput The text input containing the origin to add.
- * @param scoreInput The text input containing the score to add.
- */
- function handleAddOrigin(
- originInput: HTMLInputElement, scoreInput: HTMLInputElement) {
- try {
- // Validate the URL. If we don't validate here, IPC will kill this
- // renderer on invalid URLs. Other checks like scheme are done on the
- // browser side.
- new URL(originInput.value);
- } catch {
- return;
- }
- const origin = new Url();
- origin.url = originInput.value;
- const score = parseFloat(scoreInput.value);
-
- engagementDetailsProvider.setSiteEngagementBaseScoreForUrl(origin, score);
- scoreInput.blur();
- updateEngagementTable();
- enableAutoupdate();
- }
-
- /**
- * Remove all rows from the engagement table.
- */
- function clearTable() {
- engagementTableBody.innerHTML = window.trustedTypes!.emptyHTML;
- }
-
- /**
- * Sort the engagement info based on |sortKey| and |sortReverse|.
- */
- function sortInfo() {
- assert(info);
- info.sort((a, b) => {
- return (sortReverse ? -1 : 1) * compareTableItem(sortKey, a, b);
- });
- }
-
- /**
- * Compares two SiteEngagementDetails objects based on |sortKey|.
- * @param sortKey The name of the property to sort by.
- * @return A negative number if |a| should be ordered before |b|, a
- * positive number otherwise.
- */
- function compareTableItem(
- sortKey: string, a: {[k: string]: any}, b: {[k: string]: any}): number {
- const val1 = a[sortKey];
- const val2 = b[sortKey];
-
- // Compare the hosts of the origin ignoring schemes.
- if (sortKey === 'origin') {
- return new URL(val1.url).host > new URL(val2.url).host ? 1 : -1;
- }
-
- if (sortKey === 'baseScore' || sortKey === 'bonusScore' ||
- sortKey === 'totalScore') {
- return val1 - val2;
- }
-
- assertNotReached('Unsupported sort key: ' + sortKey);
- }
-
- /**
- * Rounds the supplied value to two decimal places of accuracy.
- */
- function roundScore(score: number): number {
- return Number(Math.round(score * 100) / 100);
- }
-
- /**
- * Regenerates the engagement table from |info|.
- */
- function renderTable() {
- clearTable();
- sortInfo();
-
- assert(info);
- info.forEach((info) => {
- // Round all scores to 2 decimal places.
- info.baseScore = roundScore(info.baseScore);
- info.installedBonus = roundScore(info.installedBonus);
- info.totalScore = roundScore(info.totalScore);
-
- engagementTableBody.appendChild(createRow(info));
- });
-
- // Add another row for adding a new origin.
- const originInput = document.createElement('input');
- originInput.classList.add('origin-input');
- originInput.addEventListener('focus', disableAutoupdate);
- originInput.addEventListener('blur', enableAutoupdate);
- originInput.value = 'http://example.com';
-
- const originCell = document.createElement('td');
- originCell.appendChild(originInput);
-
- const baseScoreInput = document.createElement('input');
- baseScoreInput.classList.add('base-score-input');
- baseScoreInput.addEventListener('focus', disableAutoupdate);
- baseScoreInput.addEventListener('blur', enableAutoupdate);
- baseScoreInput.value = '0';
-
- const baseScoreCell = document.createElement('td');
- baseScoreCell.classList.add('base-score-cell');
- baseScoreCell.appendChild(baseScoreInput);
-
- const addButton = document.createElement('button');
- addButton.textContent = 'Add';
- addButton.classList.add('add-origin-button');
-
- const buttonCell = document.createElement('td');
- buttonCell.colSpan = 2;
- buttonCell.classList.add('base-score-cell');
- buttonCell.appendChild(addButton);
-
- const row = document.createElement('tr');
- row.appendChild(originCell);
- row.appendChild(baseScoreCell);
- row.appendChild(buttonCell);
- addButton.addEventListener(
- 'click', () => handleAddOrigin(originInput, baseScoreInput));
-
- engagementTableBody.appendChild(row);
- }
-
- /**
- * Retrieve site engagement info and render the engagement table.
- */
- async function updateEngagementTable() {
- // Populate engagement table.
- ({info} = await engagementDetailsProvider.getSiteEngagementDetails());
- renderTable();
- pageIsPopulatedResolver.resolve();
- }
-
- updateEngagementTable();
- enableAutoupdate();
-
- // We explicitly set these on the global Window object so test code can use
- // them.
- Object.assign(window, {
- whenPageIsPopulatedForTest,
- disableAutoupdateForTests: disableAutoupdate,
- engagementDetailsProvider,
- });
-}
-
-document.addEventListener('DOMContentLoaded', initialize);
diff --git a/chromium/chrome/browser/resources/extensions/COMMON_METADATA b/chromium/chrome/browser/resources/extensions/COMMON_METADATA
new file mode 100644
index 00000000000..81a57a13916
--- /dev/null
+++ b/chromium/chrome/browser/resources/extensions/COMMON_METADATA
@@ -0,0 +1,4 @@
+monorail {
+ component: "Platform>Extensions"
+}
+team_email: "extensions-dev@chromium.org"
diff --git a/chromium/chrome/browser/resources/extensions/DIR_METADATA b/chromium/chrome/browser/resources/extensions/DIR_METADATA
index 81a57a13916..50560790128 100644
--- a/chromium/chrome/browser/resources/extensions/DIR_METADATA
+++ b/chromium/chrome/browser/resources/extensions/DIR_METADATA
@@ -1,4 +1 @@
-monorail {
- component: "Platform>Extensions"
-}
-team_email: "extensions-dev@chromium.org"
+mixins: "//chrome/browser/resources/extensions/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/extensions/detail_view.html b/chromium/chrome/browser/resources/extensions/detail_view.html
index 4f5ae82e37f..99f8e5b16ae 100644
--- a/chromium/chrome/browser/resources/extensions/detail_view.html
+++ b/chromium/chrome/browser/resources/extensions/detail_view.html
@@ -47,6 +47,37 @@
padding: var(--cr-section-vertical-padding) var(--cr-section-padding);
}
+ .safety-check-warning-container {
+ align-items: center;
+ background-color: var(--google-grey-50);
+ display: flex;
+ padding: 15px;
+ }
+
+ .safety-check-warning-container iron-icon {
+ border-radius: 50%;
+ height: var(--cr-icon-size);
+ padding: 6px;
+ width: var(--cr-icon-size);
+ }
+
+ @media (prefers-color-scheme: dark) {
+ .safety-check-warning-container {
+ /* Google Grey 911 according to Extensions Safety Check mock. */
+ background-color: rgb(53, 54, 58);
+ }
+
+ .safety-check-icon {
+ background-color: var(--google-blue-300);
+ fill: var(--review-panel-icon-color);
+ }
+ }
+
+ .keep-button {
+ margin-inline-end: 10px;
+ margin-inline-start: 40px;
+ }
+
.cr-row.control-line {
justify-content: space-between;
}
@@ -162,6 +193,26 @@
[[data.name]]
</span>
</div>
+ <div class="safety-check-warning-container" id="safetyCheckWarningContainer"
+ hidden$="[[!showSafetyCheck_]]">
+ <iron-icon role="img" icon="cr:extension"
+ class="safety-check-icon">
+ </iron-icon>
+ <div class="safety-check-wrapper">
+ <span class="section-title" aria-level="2">
+ $i18n{safetyCheckExtensionsDetailPagePrimaryLabel}
+ </span>
+ <div class="section-content">
+ [[data.safetyCheckText.detailString]]
+ </div>
+ </div>
+ <cr-button class="keep-button" on-click="onKeepClick_">
+ $i18n{safetyCheckExtensionsKeep}
+ </cr-button>
+ <cr-button class="action-button" on-click="onRemoveClick_">
+ $i18n{remove}
+ </cr-button>
+ </div>
<div class="cr-row first control-line" id="enable-section">
<span class$="[[computeEnabledStyle_(data.state)]]">
[[computeEnabledText_(data.state, '$i18nPolymer{itemOn}',
@@ -186,16 +237,18 @@
icon-aria-label="$i18n{parentDisabledPermissions}">
</cr-tooltip-icon>
<cr-toggle id="enableToggle"
- aria-label$="[[appOrExtension(
- data.type,
- '$i18nPolymer{appEnabled}',
- '$i18nPolymer{extensionEnabled}')]]"
- aria-describedby="name"
+ aria-label$="[[getEnableToggleAriaLabel_(data.*)]]"
+ aria-describedby="name enable-toggle-tooltip"
checked="[[isEnabled_(data.state)]]"
on-change="onEnableToggleChange_"
disabled$="[[!isEnableToggleEnabled_(data.*)]]"
hidden$="[[!showEnableToggle_(data.*)]]">
</cr-toggle>
+ <paper-tooltip id="enable-toggle-tooltip" for="enableToggle"
+ position="left" aria-hidden="true" animation-delay="0"
+ fit-to-visible-bounds>
+ [[getEnableToggleTooltipText_(data.*)]]
+ </paper-tooltip>
</div>
</div>
<div id="warnings" hidden$="[[!hasSevereWarnings_(data.*)]]">
@@ -236,7 +289,7 @@
</cr-button>
</div>
<div class="cr-row continuation warning" id="blacklisted-warning"
- hidden$="[[!data.blacklistText]]">
+ hidden$="[[!showBlocklistText_]]">
<iron-icon class="warning-icon" icon="cr:warning"></iron-icon>
<span>[[data.blacklistText]]</span>
</div>
@@ -415,6 +468,14 @@
on-click="onSiteSettingsClick_" external></cr-link-row>
<template is="dom-if" if="[[shouldShowOptionsSection_(data.*)]]">
<div id="options-section">
+ <template is="dom-if" if="[[canPinToToolbar_(data.pinnedToToolbar)]]">
+ <extensions-toggle-row id="pin-to-toolbar"
+ checked="[[data.pinnedToToolbar]]"
+ class="hr"
+ on-change="onPinnedToToolbarChange_">
+ <span>$i18n{itemPinToToolbar}</span>
+ </extensions-toggle-row>
+ </template>
<template is="dom-if"
if="[[shouldShowIncognitoOption_(
data.incognitoAccess.isEnabled, incognitoAvailable)]]">
diff --git a/chromium/chrome/browser/resources/extensions/detail_view.ts b/chromium/chrome/browser/resources/extensions/detail_view.ts
index be3d1225074..f22d3e08540 100644
--- a/chromium/chrome/browser/resources/extensions/detail_view.ts
+++ b/chromium/chrome/browser/resources/extensions/detail_view.ts
@@ -26,6 +26,7 @@ import './toggle_row.js';
import {CrLinkRowElement} from 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {CrTooltipIconElement} from 'chrome://resources/cr_elements/policy/cr_tooltip_icon.js';
import {assert} from 'chrome://resources/js/assert_ts.js';
import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
@@ -35,7 +36,7 @@ import {afterNextRender, DomRepeatEvent, PolymerElement} from 'chrome://resource
import {getTemplate} from './detail_view.html.js';
import {ItemDelegate} from './item.js';
import {ItemMixin} from './item_mixin.js';
-import {computeInspectableViewLabel, EnableControl, getEnableControl, getItemSource, getItemSourceString, isEnabled, sortViews, userCanChangeEnablement} from './item_util.js';
+import {computeInspectableViewLabel, EnableControl, getEnableControl, getEnableToggleAriaLabel, getEnableToggleTooltipText, getItemSource, getItemSourceString, isEnabled, sortViews, userCanChangeEnablement} from './item_util.js';
import {navigation, Page} from './navigation_helper.js';
import {ExtensionsToggleRowElement} from './toggle_row.js';
@@ -47,11 +48,12 @@ export interface ExtensionsDetailViewElement {
extensionsActivityLogLink: HTMLElement,
extensionsOptions: CrLinkRowElement,
parentDisabledPermissionsToolTip: CrTooltipIconElement,
+ safetyCheckWarningContainer: HTMLElement,
source: HTMLElement,
};
}
-const ExtensionsDetailViewElementBase = ItemMixin(PolymerElement);
+const ExtensionsDetailViewElementBase = I18nMixin(ItemMixin(PolymerElement));
export class ExtensionsDetailViewElement extends
ExtensionsDetailViewElementBase {
@@ -98,6 +100,19 @@ export class ExtensionsDetailViewElement extends
type: Array,
computed: 'computeSortedViews_(data.views)',
},
+
+ /** Whether the extensions safety check warning is shown. */
+ showSafetyCheck_: {
+ type: Boolean,
+ computed: 'computeShowSafetyCheck_(data.safetyCheckText)',
+ observer: 'onShowSafetyCheckChanged_',
+ },
+
+ /** Whether the extensions blocklist text is shown. */
+ showBlocklistText_: {
+ type: Boolean,
+ computed: 'computeShowBlocklistText_(data.blacklistText)',
+ },
};
}
@@ -112,8 +127,11 @@ export class ExtensionsDetailViewElement extends
incognitoAvailable: boolean;
showActivityLog: boolean;
fromActivityLog: boolean;
+ private showSafetyCheck_: boolean;
+ private showBlocklistText_: boolean;
private size_: string;
private sortedViews_: chrome.developerPrivate.ExtensionView[];
+ private safetyCheckExtensionsEnabled_: boolean;
override ready() {
super.ready();
@@ -166,6 +184,16 @@ export class ExtensionsDetailViewElement extends
'itemDetailsBackButtonRoleDescription', this.data.name);
}
+ private getEnableToggleAriaLabel_(): string {
+ return getEnableToggleAriaLabel(
+ this.isEnabled_(), this.data.type, this.i18n('appEnabled'),
+ this.i18n('extensionEnabled'), this.i18n('itemOff'));
+ }
+
+ private getEnableToggleTooltipText_(): string {
+ return getEnableToggleTooltipText(this.data);
+ }
+
private onCloseButtonClick_() {
navigation.navigateTo({page: Page.LIST});
}
@@ -215,10 +243,14 @@ export class ExtensionsDetailViewElement extends
}
private shouldShowOptionsSection_(): boolean {
- return this.data.incognitoAccess.isEnabled ||
+ return this.canPinToToolbar_() || this.data.incognitoAccess.isEnabled ||
this.data.fileAccess.isEnabled || this.data.errorCollection.isEnabled;
}
+ private canPinToToolbar_(): boolean {
+ return this.data.pinnedToToolbar !== undefined;
+ }
+
private shouldShowIncognitoOption_(): boolean {
return this.data.incognitoAccess.isEnabled && this.incognitoAvailable;
}
@@ -245,9 +277,19 @@ export class ExtensionsDetailViewElement extends
}
private onRemoveClick_() {
+ if (this.showSafetyCheck_) {
+ chrome.metricsPrivate.recordUserAction('SafetyCheck.DetailRemoveClicked');
+ }
this.delegate.deleteItem(this.data.id);
}
+ private onKeepClick_() {
+ if (this.showSafetyCheck_) {
+ chrome.metricsPrivate.recordUserAction('SafetyCheck.DetailKeepClicked');
+ }
+ this.delegate.setItemSafetyCheckWarningAcknowledged(this.data.id);
+ }
+
private onRepairClick_() {
this.delegate.repairItem(this.data.id);
}
@@ -256,6 +298,14 @@ export class ExtensionsDetailViewElement extends
this.delegate.showInFolder(this.data.id);
}
+ private onPinnedToToolbarChange_() {
+ this.delegate.setItemPinnedToToolbar(
+ this.data.id,
+ this.shadowRoot!
+ .querySelector<ExtensionsToggleRowElement>(
+ '#pin-to-toolbar')!.checked);
+ }
+
private onAllowIncognitoChange_() {
this.delegate.setItemAllowedIncognito(
this.data.id,
@@ -361,6 +411,26 @@ export class ExtensionsDetailViewElement extends
return getEnableControl(this.data) === EnableControl.RELOAD;
}
+ private computeShowSafetyCheck_(): boolean {
+ if (!loadTimeData.getBoolean('safetyCheckShowReviewPanel')) {
+ return false;
+ }
+
+ return !!(
+ this.data.safetyCheckText && this.data.safetyCheckText.detailString &&
+ this.data.acknowledgeSafetyCheckWarning !== true);
+ }
+
+ private onShowSafetyCheckChanged_() {
+ if (this.showSafetyCheck_) {
+ chrome.metricsPrivate.recordUserAction('SafetyCheck.DetailWarningShown');
+ }
+ }
+
+ private computeShowBlocklistText_(): boolean {
+ return !this.showSafetyCheck_ && !!this.data.blacklistText;
+ }
+
private showRepairButton_(): boolean {
return getEnableControl(this.data) === EnableControl.REPAIR;
}
diff --git a/chromium/chrome/browser/resources/extensions/drag_and_drop_handler.ts b/chromium/chrome/browser/resources/extensions/drag_and_drop_handler.ts
index 312188cb291..76b785dd625 100644
--- a/chromium/chrome/browser/resources/extensions/drag_and_drop_handler.ts
+++ b/chromium/chrome/browser/resources/extensions/drag_and_drop_handler.ts
@@ -9,7 +9,8 @@ import {Service} from './service.js';
declare global {
interface HTMLElementEventMap {
- 'drag-and-drop-load-error': CustomEvent<chrome.developerPrivate.LoadError>;
+ 'drag-and-drop-load-error':
+ CustomEvent<Error|chrome.developerPrivate.LoadError>;
}
}
diff --git a/chromium/chrome/browser/resources/extensions/icons.html b/chromium/chrome/browser/resources/extensions/icons.html
index 8e705da19ed..a248017d21f 100644
--- a/chromium/chrome/browser/resources/extensions/icons.html
+++ b/chromium/chrome/browser/resources/extensions/icons.html
@@ -18,6 +18,12 @@
<!-- Copied from iron-icons. -->
<g id="input"><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3z"></path></g>
<g id="business"><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"></path></g>
+
+ <!-- Icons for the extensions page sidebar. -->
+ <g id="my_extensions" viewBox="0 -960 960 960"><path d="M216-135.869q-33.287 0-56.709-23.422-23.422-23.422-23.422-56.709v-172.304q37.609-2 63.218-28.424 25.608-26.424 25.608-63.272t-25.608-63.272q-25.609-26.424-63.218-28.424V-744q0-33.287 23.422-56.709 23.422-23.422 56.709-23.422h161.065q2.631-40.956 31.96-69.315 29.329-28.359 70.75-28.359t70.975 28.199q29.554 28.199 32.185 69.475H744q33.287 0 56.709 23.422 23.422 23.422 23.422 56.709v161.065q40.956 2.631 69.315 31.96 28.359 29.329 28.359 70.75t-28.199 70.975q-28.199 29.554-69.475 32.185V-216q0 33.287-23.422 56.709-23.422 23.422-56.709 23.422H216Zm2.87-83.001h522.26v-522.26H218.87v108.652q42.13 22.63 65.597 63.772 23.468 41.141 23.468 88.706 0 49.01-23.468 90.168Q261-348.674 218.87-327.283v108.413ZM480-480Z"></path></g>
+ <g id="site_permissions" viewBox="0 -960 960 960"><path d="M454.087-136.587V-384H533.5v84h288v79.413h-288v84h-79.413Zm-315.587-84V-300h247.413v79.413H138.5Zm144-135.826v-84h-144v-79.174h144v-84h79.413v247.174H282.5Zm147.587-84v-79.174H821.5v79.174H430.087Zm144-135.587v-247.413H653.5v84h168V-660h-168v84h-79.413ZM138.5-660v-79.413h391.413V-660H138.5Z"></path></g>
+ <g id="keyboard_shortcuts" viewBox="0 -960 960 960"><path d="M168-229q-34.483 0-58.741-24.259-24.26-24.258-24.26-58.741v-336q0-34.483 24.26-58.741Q133.517-731 168-731h624q34.483 0 58.741 24.259Q875-682.483 875-648v336q0 34.483-24.259 58.741Q826.483-229 792-229H168Zm0-83h624v-336H168v336Zm168-24h288v-72H336v72Zm-96-120h72v-72h-72v72Zm102 0h72v-72h-72v72Zm102 0h72v-72h-72v72Zm102 0h72v-72h-72v72Zm102 0h72v-72h-72v72Zm-408-96h72v-72h-72v72Zm102 0h72v-72h-72v72Zm102 0h72v-72h-72v72Zm102 0h72v-72h-72v72Zm102 0h72v-72h-72v72ZM168-312v-336 336Z"></path></g>
+ <g id="web_store" enable-background="new 0 0 192 192" height="24px" viewBox="0 0 192 192" width="24px"><path fill="none" d="M0 0h192v192H0z"></path><path fill="#F1F3F4" d="M172 28H20v121.63c0 5.72 4.64 10.37 10.37 10.37h131.27c5.72 0 10.37-4.64 10.37-10.37V28z"></path><path fill="#F1F3F4" d="M172 28H20v121.63c0 5.72 4.64 10.37 10.37 10.37h131.27c5.72 0 10.37-4.64 10.37-10.37V28z"></path><path fill="#E8EAED" d="M20 28h152v66.35H20z"></path><path fill="#FFF" d="M113.27 56.34H78.73c-3.82 0-6.91-3.09-6.91-6.91 0-3.82 3.09-6.91 6.91-6.91h34.54c3.82 0 6.91 3.09 6.91 6.91 0 3.81-3.09 6.91-6.91 6.91z"></path><defs><path id="a" d="M172 28H20v121.63c0 5.72 4.64 10.37 10.37 10.37h131.27c5.72 0 10.37-4.64 10.37-10.37V28z"></path></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"></use></clipPath><g clip-path="url(#b)"><linearGradient id="c" x1="39.161" x2="152.841" y1="125.013" y2="125.013" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#d93025"></stop><stop offset="1" style="stop-color:#ea4335"></stop></linearGradient><path fill="url(#c)" d="m39.16 116.8 9.05 27.61 19.38 21.63L96 116.81l56.84-.01C141.49 97.18 120.29 83.99 96 83.99c-24.29 0-45.49 13.19-56.84 32.81z"></path><linearGradient id="d" x1="-3.897" x2="109.806" y1="36.608" y2="36.608" gradientTransform="rotate(-120 100 93.002)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#1e8e3e"></stop><stop offset="1" style="stop-color:#34a853"></stop></linearGradient><path fill="url(#d)" d="m95.99 215.28 19.38-21.64 9.04-27.6H67.58L39.16 116.8c-11.31 19.64-12.14 44.61.01 65.65 12.14 21.04 34.17 32.81 56.82 32.83z"></path><linearGradient id="e" x1="96.791" x2="210.494" y1="8.387" y2="8.387" gradientTransform="rotate(120 88.856 76)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#fbbc04"></stop><stop offset="1" style="stop-color:#fcc934"></stop></linearGradient><path fill="url(#e)" d="M152.84 116.81H96l28.42 49.23L96 215.28c22.66-.02 44.69-11.79 56.83-32.83 12.15-21.04 11.32-46 .01-65.64z"></path><ellipse cx="96" cy="149.63" fill="#F1F3F4" rx="32.81" ry="32.82"></ellipse><ellipse cx="96" cy="149.63" fill="#1A73E8" rx="26.66" ry="26.67"></ellipse></g><path fill="#BDC1C6" d="M20 94.35h152v.86H20zM20 93.48h152v.86H20z" opacity=".1"></path></g>
</defs>
</svg>
</iron-iconset-svg>
diff --git a/chromium/chrome/browser/resources/extensions/item.html b/chromium/chrome/browser/resources/extensions/item.html
index fae1fb3f138..73fb2ba8175 100644
--- a/chromium/chrome/browser/resources/extensions/item.html
+++ b/chromium/chrome/browser/resources/extensions/item.html
@@ -338,12 +338,13 @@
icon-class="cr20:kite"
icon-aria-label="$i18n{parentDisabledPermissions}">
</cr-tooltip-icon>
+ <paper-tooltip id="enable-toggle-tooltip" for="enableToggle" position="left"
+ aria-hidden="true" animation-delay="0" fit-to-visible-bounds>
+ [[getEnableToggleTooltipText_(data.*)]]
+ </paper-tooltip>
<cr-toggle id="enableToggle"
- aria-label$="[[appOrExtension(
- data.type,
- '$i18nPolymer{appEnabled}',
- '$i18nPolymer{extensionEnabled}')]]"
- aria-describedby="a11yAssociation"
+ aria-label$="[[getEnableToggleAriaLabel_(data.*)]]"
+ aria-describedby="a11yAssociation enable-toggle-tooltip"
checked="[[isEnabled_(data.state)]]"
on-change="onEnableToggleChange_"
disabled$="[[!isEnableToggleEnabled_(data.*)]]"
diff --git a/chromium/chrome/browser/resources/extensions/item.ts b/chromium/chrome/browser/resources/extensions/item.ts
index d0923f2466e..efeb5244449 100644
--- a/chromium/chrome/browser/resources/extensions/item.ts
+++ b/chromium/chrome/browser/resources/extensions/item.ts
@@ -23,17 +23,19 @@ import 'chrome://resources/polymer/v3_0/paper-tooltip/paper-tooltip.js';
import {ChromeEvent} from '/tools/typescript/definitions/chrome_event.js';
import {getToastManager} from 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.js';
import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
-import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
import {flush, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './item.html.js';
import {ItemMixin} from './item_mixin.js';
-import {computeInspectableViewLabel, EnableControl, getEnableControl, getItemSource, getItemSourceString, isEnabled, sortViews, SourceType, userCanChangeEnablement} from './item_util.js';
+import {computeInspectableViewLabel, EnableControl, getEnableControl, getEnableToggleAriaLabel, getEnableToggleTooltipText, getItemSource, getItemSourceString, isEnabled, sortViews, SourceType, userCanChangeEnablement} from './item_util.js';
import {navigation, Page} from './navigation_helper.js';
export interface ItemDelegate {
deleteItem(id: string): void;
+ deleteItems(ids: string[]): Promise<void>;
+ uninstallItem(id: string): Promise<void>;
setItemEnabled(id: string, isEnabled: boolean): void;
setItemAllowedIncognito(id: string, isAllowedIncognito: boolean): void;
setItemAllowedOnFileUrls(id: string, isAllowedOnFileUrls: boolean): void;
@@ -50,7 +52,9 @@ export interface ItemDelegate {
getExtensionSize(id: string): Promise<string>;
addRuntimeHostPermission(id: string, host: string): Promise<void>;
removeRuntimeHostPermission(id: string, host: string): Promise<void>;
+ setItemSafetyCheckWarningAcknowledged(id: string): void;
setShowAccessRequestsInToolbar(id: string, showRequests: boolean): void;
+ setItemPinnedToToolbar(id: string, pinnedToToolbar: boolean): void;
// TODO(tjudkins): This function is not specific to items, so should be pulled
// out to a more generic place when we need to access it from elsewhere.
@@ -91,6 +95,11 @@ export class ExtensionsItemElement extends ExtensionsItemElementBase {
value: false,
},
+ safetyCheckShowing: {
+ type: Boolean,
+ value: false,
+ },
+
// The underlying ExtensionInfo itself. Public for use in declarative
// bindings.
data: Object,
@@ -115,6 +124,7 @@ export class ExtensionsItemElement extends ExtensionsItemElementBase {
delegate: ItemDelegate;
inDevMode: boolean;
+ safetyCheckShowing: boolean;
data: chrome.developerPrivate.ExtensionInfo;
private showingDetails_: boolean;
private firstInspectView_: chrome.developerPrivate.ExtensionView;
@@ -126,15 +136,31 @@ export class ExtensionsItemElement extends ExtensionsItemElementBase {
new CustomEvent(eventName, {bubbles: true, composed: true, detail}));
}
- getDetailsButton() {
+ /** @return The "Details" button. */
+ getDetailsButton(): HTMLElement {
return this.$.detailsButton;
}
+ /** @return The "Remove" button, if it exists. */
+ getRemoveButton(): HTMLElement|null {
+ return this.data.mustRemainInstalled ? null : this.$.removeButton;
+ }
+
/** @return The "Errors" button, if it exists. */
getErrorsButton(): HTMLElement|null {
return this.shadowRoot!.querySelector('#errors-button');
}
+ private getEnableToggleAriaLabel_(): string {
+ return getEnableToggleAriaLabel(
+ this.isEnabled_(), this.data.type, this.i18n('appEnabled'),
+ this.i18n('extensionEnabled'), this.i18n('itemOff'));
+ }
+
+ private getEnableToggleTooltipText_(): string {
+ return getEnableToggleTooltipText(this.data);
+ }
+
private observeIdVisibility_() {
flush();
const idElement = this.shadowRoot!.querySelector('#extension-id');
@@ -159,6 +185,12 @@ export class ExtensionsItemElement extends ExtensionsItemElementBase {
}
private onRemoveClick_() {
+ if (this.safetyCheckShowing) {
+ const actionToRecord = this.data.safetyCheckText ?
+ 'SafetyCheck.ReviewPanelRemoveClicked' :
+ 'SafetyCheck.NonTriggeringExtensionRemoved';
+ chrome.metricsPrivate.recordUserAction(actionToRecord);
+ }
this.delegate.deleteItem(this.data.id);
}
diff --git a/chromium/chrome/browser/resources/extensions/item_list.html b/chromium/chrome/browser/resources/extensions/item_list.html
index 49a48fa68ba..807d3076ee9 100644
--- a/chromium/chrome/browser/resources/extensions/item_list.html
+++ b/chromium/chrome/browser/resources/extensions/item_list.html
@@ -14,6 +14,10 @@
padding: 24px 60px 64px;
}
+ #content-wrapper:has(extensions-review-panel) {
+ padding-top: 14px;
+ }
+
.empty-list-message {
color: #6e6e6e;
font-size: 123%; /* Should be 16px when 100% is 13px. */
@@ -22,6 +26,12 @@
text-align: center;
}
+ .extension-title-container {
+ font-size: 15px;
+ font-weight: 400;
+ margin: 0 0 16px 5px;
+ }
+
@media (prefers-color-scheme: dark) {
.empty-list-message {
color: var(--cr-secondary-text-color);
@@ -41,8 +51,14 @@
}
.items-container.review-panel-container :first-child {
+ max-width: var(--cr-toolbar-field-width);
grid-column: 1 / -1;
- padding-bottom: 20px;
+ padding-bottom: 48px;
+ }
+
+ extensions-review-panel {
+ margin: 0 auto;
+ width: 100%;
}
#checkup-container {
@@ -85,9 +101,10 @@
<managed-footnote hidden="[[filter]]"></managed-footnote>
<div id="content-wrapper" style="--max-columns: [[maxColumns_]];">
<div class="items-container review-panel-container">
- <template is="dom-if" if="[[showSafetyCheckReviewPanel_]]">
+ <template is="dom-if" if="[[showSafetyCheckReviewPanel_]]" restamp>
<extensions-review-panel extensions="[[extensions]]" delegate="[[delegate]]">
</extensions-review-panel>
+ <h2 class="extension-title-container">$i18n{safetyCheckAllExtensions}</h2>
</template>
</div>
<div id="no-items" class="empty-list-message"
@@ -111,6 +128,7 @@
filter="[[computedFilter_]]"
rendered-item-count="{{shownExtensionsCount_::dom-change}}">
<extensions-item id="[[item.id]]" data="[[item]]"
+ safety-check-showing="[[hasSafetyCheckTriggeringExtension_]]"
delegate="[[delegate]]" in-dev-mode="[[inDevMode]]">
</extensions-item>
</template>
diff --git a/chromium/chrome/browser/resources/extensions/item_list.ts b/chromium/chrome/browser/resources/extensions/item_list.ts
index c50ddec2b61..4b121dbace3 100644
--- a/chromium/chrome/browser/resources/extensions/item_list.ts
+++ b/chromium/chrome/browser/resources/extensions/item_list.ts
@@ -8,6 +8,7 @@ import './shared_style.css.js';
import './review_panel.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {IronA11yAnnouncer} from 'chrome://resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -68,6 +69,11 @@ export class ExtensionsItemListElement extends ExtensionsItemListElementBase {
type: Boolean,
value: () => loadTimeData.getBoolean('safetyCheckShowReviewPanel'),
},
+
+ hasSafetyCheckTriggeringExtension_: {
+ type: Boolean,
+ computed: 'computeHasSafetyCheckTriggeringExtension_(extensions)',
+ },
};
}
@@ -81,6 +87,7 @@ export class ExtensionsItemListElement extends ExtensionsItemListElementBase {
private shownAppsCount_: number;
private shownExtensionsCount_: number;
private showSafetyCheckReviewPanel_: boolean;
+ private hasSafetyCheckTriggeringExtension_: boolean;
getDetailsButton(id: string): HTMLElement|null {
const item =
@@ -88,6 +95,12 @@ export class ExtensionsItemListElement extends ExtensionsItemListElementBase {
return item && item.getDetailsButton();
}
+ getRemoveButton(id: string): HTMLElement|null {
+ const item =
+ this.shadowRoot!.querySelector<ExtensionsItemElement>(`#${id}`);
+ return item && item.getRemoveButton();
+ }
+
getErrorsButton(id: string): HTMLElement|null {
const item =
this.shadowRoot!.querySelector<ExtensionsItemElement>(`#${id}`);
@@ -95,6 +108,18 @@ export class ExtensionsItemListElement extends ExtensionsItemListElementBase {
}
/**
+ * Focus the remove button for the item matching `id`. If the remove button is
+ * not visible, focus the details button instead.
+ */
+ focusItemButton(id: string) {
+ const item =
+ this.shadowRoot!.querySelector<ExtensionsItemElement>(`#${id}`);
+ assert(item);
+ const buttonToFocus = item.getRemoveButton() || item.getDetailsButton();
+ buttonToFocus!.focus();
+ }
+
+ /**
* Computes the filter function to be used for determining which items
* should be shown. A |null| value indicates that everything should be
* shown.
@@ -110,6 +135,20 @@ export class ExtensionsItemListElement extends ExtensionsItemListElementBase {
s => s.toLowerCase().includes(formattedFilter));
}
+ private computeHasSafetyCheckTriggeringExtension_(): boolean {
+ if (!this.extensions) {
+ return false;
+ }
+ for (const extension of this.extensions) {
+ if (!!extension.safetyCheckText &&
+ !!extension.safetyCheckText.panelString &&
+ this.showSafetyCheckReviewPanel_) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private shouldShowEmptyItemsMessage_() {
if (!this.apps || !this.extensions) {
return;
diff --git a/chromium/chrome/browser/resources/extensions/item_util.ts b/chromium/chrome/browser/resources/extensions/item_util.ts
index 69b62520706..3dc19d847b0 100644
--- a/chromium/chrome/browser/resources/extensions/item_util.ts
+++ b/chromium/chrome/browser/resources/extensions/item_util.ts
@@ -155,6 +155,30 @@ export function computeInspectableViewLabel(
}
/**
+ * Computes the accessible human-facing aria label for an extension toggle item.
+ */
+export function getEnableToggleAriaLabel(
+ toggleEnabled: boolean,
+ extensionsDataType: chrome.developerPrivate.ExtensionType,
+ appEnabled: string, extensionEnabled: string, itemOff: string): string {
+ if (!toggleEnabled) {
+ return itemOff;
+ }
+
+ const ExtensionType = chrome.developerPrivate.ExtensionType;
+ switch (extensionsDataType) {
+ case ExtensionType.HOSTED_APP:
+ case ExtensionType.LEGACY_PACKAGED_APP:
+ case ExtensionType.PLATFORM_APP:
+ return appEnabled;
+ case ExtensionType.EXTENSION:
+ case ExtensionType.SHARED_MODULE:
+ return extensionEnabled;
+ }
+ assertNotReached('Item type is not App or Extension.');
+}
+
+/**
* Clones the array and returns a new array with background pages and service
* workers sorted before other views.
* @returns Sorted array.
@@ -195,3 +219,18 @@ export function getEnableControl(data: chrome.developerPrivate.ExtensionInfo):
}
return EnableControl.ENABLE_TOGGLE;
}
+
+/**
+ * @return The tooltip to show for an extension's enable toggle.
+ */
+export function getEnableToggleTooltipText(
+ data: chrome.developerPrivate.ExtensionInfo): string {
+ if (!isEnabled(data.state)) {
+ return loadTimeData.getString('enableToggleTooltipDisabled');
+ }
+
+ return loadTimeData.getString(
+ data.permissions.canAccessSiteData ?
+ 'enableToggleTooltipEnabledWithSiteAccess' :
+ 'enableToggleTooltipEnabled');
+}
diff --git a/chromium/chrome/browser/resources/extensions/load_error.html b/chromium/chrome/browser/resources/extensions/load_error.html
index d59e3abd662..574742430a4 100644
--- a/chromium/chrome/browser/resources/extensions/load_error.html
+++ b/chromium/chrome/browser/resources/extensions/load_error.html
@@ -16,13 +16,13 @@
<div slot="title">$i18n{loadErrorHeading}</div>
<div slot="body">
<div id="info">
- <div id="file" class="description-row">
+ <div id="file" class="description-row" hidden$="[[!file_]]">
<span class="row-label">$i18n{loadErrorFileLabel}</span>
- <span class="row-value">[[loadError.path]]</span>
+ <span class="row-value">[[file_]]</span>
</div>
<div id="error" class="description-row">
<span class="row-label">$i18n{loadErrorErrorLabel}</span>
- <span class="row-value">[[loadError.error]]</span>
+ <span class="row-value">[[error_]]</span>
</div>
</div>
<extensions-code-section id="code"
diff --git a/chromium/chrome/browser/resources/extensions/load_error.ts b/chromium/chrome/browser/resources/extensions/load_error.ts
index e5577b1e8a6..bd376f152e4 100644
--- a/chromium/chrome/browser/resources/extensions/load_error.ts
+++ b/chromium/chrome/browser/resources/extensions/load_error.ts
@@ -20,7 +20,7 @@ export interface LoadErrorDelegate {
/**
* Attempts to load the previously-attempted unpacked extension.
*/
- retryLoadUnpacked(retryGuid: string): Promise<boolean>;
+ retryLoadUnpacked(retryGuid?: string): Promise<boolean>;
}
export interface ExtensionsLoadErrorElement {
@@ -43,6 +43,14 @@ export class ExtensionsLoadErrorElement extends PolymerElement {
return {
delegate: Object,
loadError: Object,
+ file_: {
+ type: String,
+ value: null,
+ },
+ error_: {
+ type: String,
+ value: null,
+ },
retrying_: Boolean,
};
}
@@ -54,7 +62,10 @@ export class ExtensionsLoadErrorElement extends PolymerElement {
}
delegate: LoadErrorDelegate;
- loadError: chrome.developerPrivate.LoadError;
+ loadError: Error|chrome.developerPrivate.LoadError;
+
+ private file_?: string;
+ private error_?: string;
private retrying_: boolean;
show() {
@@ -67,7 +78,10 @@ export class ExtensionsLoadErrorElement extends PolymerElement {
private onRetryClick_() {
this.retrying_ = true;
- this.delegate.retryLoadUnpacked(this.loadError.retryGuid)
+ this.delegate
+ .retryLoadUnpacked(
+ this.loadError instanceof Error ? undefined :
+ this.loadError.retryGuid)
.then(
() => {
this.close();
@@ -80,6 +94,17 @@ export class ExtensionsLoadErrorElement extends PolymerElement {
private observeLoadErrorChanges_() {
assert(this.loadError);
+
+ if (this.loadError instanceof Error) {
+ this.file_ = undefined;
+ this.error_ = this.loadError.message;
+ this.$.code.isActive = false;
+ return;
+ }
+
+ this.file_ = this.loadError.path;
+ this.error_ = this.loadError.error;
+
const source = this.loadError.source;
// CodeSection expects a RequestFileSourceResponse, rather than an
// ErrorFileSource. Massage into place.
@@ -93,6 +118,7 @@ export class ExtensionsLoadErrorElement extends PolymerElement {
};
this.$.code.code = codeSectionProperties;
+ this.$.code.isActive = true;
}
}
diff --git a/chromium/chrome/browser/resources/extensions/manager.html b/chromium/chrome/browser/resources/extensions/manager.html
index 13332ad9947..89c0e1c16a0 100644
--- a/chromium/chrome/browser/resources/extensions/manager.html
+++ b/chromium/chrome/browser/resources/extensions/manager.html
@@ -4,7 +4,6 @@
display: flex;
flex-direction: column;
height: 100%;
- --cr-toolbar-field-width: 680px;
}
#viewManager {
diff --git a/chromium/chrome/browser/resources/extensions/manager.ts b/chromium/chrome/browser/resources/extensions/manager.ts
index 19e781ba4b3..d523a1abf81 100644
--- a/chromium/chrome/browser/resources/extensions/manager.ts
+++ b/chromium/chrome/browser/resources/extensions/manager.ts
@@ -77,7 +77,7 @@ function compareExtensions(
declare global {
interface HTMLElementEventMap {
- 'load-error': CustomEvent<chrome.developerPrivate.LoadError>;
+ 'load-error': CustomEvent<Error|chrome.developerPrivate.LoadError>;
}
}
@@ -376,6 +376,7 @@ export class ExtensionsManagerElement extends ExtensionsManagerElementBase {
case EventType.PERMISSIONS_CHANGED:
case EventType.SERVICE_WORKER_STARTED:
case EventType.SERVICE_WORKER_STOPPED:
+ case EventType.PINNED_ACTIONS_CHANGED:
// |extensionInfo| can be undefined in the case of an extension
// being unloaded right before uninstallation. There's nothing to do
// here.
@@ -402,6 +403,15 @@ export class ExtensionsManagerElement extends ExtensionsManagerElementBase {
case EventType.UNINSTALLED:
this.removeItem_(eventData.item_id);
break;
+ case EventType.CONFIGURATION_CHANGED:
+ const index = this.getIndexInList_('extensions_', eventData.item_id);
+ this.updateItem_(
+ 'extensions_', index,
+ Object.assign({}, this.getData_(eventData.item_id), {
+ acknowledgeSafetyCheckWarning:
+ eventData.extensionInfo?.acknowledgeSafetyCheckWarning,
+ }));
+ break;
default:
assertNotReached();
}
@@ -525,15 +535,33 @@ export class ExtensionsManagerElement extends ExtensionsManagerElementBase {
}
}
+ // When an item is removed while on the 'item list' page, move focus to the
+ // next item in the list with `listId` if available. If no items are in that
+ // list, focus to the search bar as a fallback.
+ // This is a fix for crbug.com/1416324 which causes focus to linger on a
+ // deleted element, which is then read by the screen reader.
+ private focusAfterItemRemoved_(listId: string, index: number) {
+ // A timeout is used so elements are focused after the DOM is updated.
+ setTimeout(() => {
+ if (this.get(listId).length) {
+ const focusIndex = Math.min(this.get(listId).length - 1, index);
+ const itemToFocusId = this.get([listId, focusIndex])!.id;
+ this.$['items-list'].focusItemButton(itemToFocusId);
+ } else {
+ this.$.toolbar.focusSearchInput();
+ }
+ }, 0);
+ }
+
/**
* @param itemId The id of item to remove.
*/
private removeItem_(itemId: string) {
- // Search for the item to be deleted in |extensions_|.
+ // Search for the item to be deleted in `extensions_`.
let listId = 'extensions_';
let index = this.getIndexInList_(listId, itemId);
if (index === -1) {
- // If not in |extensions_| it must be in |apps_|.
+ // If not in `extensions_` it must be in `apps_`.
listId = 'apps_';
index = this.getIndexInList_(listId, itemId);
}
@@ -541,16 +569,20 @@ export class ExtensionsManagerElement extends ExtensionsManagerElementBase {
// We should never try and remove a non-existent item.
assert(index >= 0);
this.splice(listId, index, 1);
- if ((this.currentPage_!.page === Page.ACTIVITY_LOG ||
+ if (this.currentPage_!.page === Page.LIST) {
+ this.focusAfterItemRemoved_(listId, index);
+ } else if (
+ (this.currentPage_!.page === Page.ACTIVITY_LOG ||
this.currentPage_!.page === Page.DETAILS ||
this.currentPage_!.page === Page.ERRORS) &&
this.currentPage_!.extensionId === itemId) {
- // Leave the details page (the 'list' page is a fine choice).
+ // Leave the details page (the 'item list' page is a fine choice).
navigation.replaceWith({page: Page.LIST});
}
}
- private onLoadError_(e: CustomEvent<chrome.developerPrivate.LoadError>) {
+ private onLoadError_(
+ e: CustomEvent<Error|chrome.developerPrivate.LoadError>) {
this.showLoadErrorDialog_ = true;
setTimeout(() => {
const dialog = this.shadowRoot!.querySelector('extensions-load-error')!;
diff --git a/chromium/chrome/browser/resources/extensions/review_panel.html b/chromium/chrome/browser/resources/extensions/review_panel.html
index c4115915552..82b128f9fdf 100644
--- a/chromium/chrome/browser/resources/extensions/review_panel.html
+++ b/chromium/chrome/browser/resources/extensions/review_panel.html
@@ -7,27 +7,42 @@
.header-with-icon {
align-items: center;
display: flex;
- padding: 15px 30px 15px 20px;
+ padding: 0 30px 15px 0px;
}
.header-with-icon h3 {
- margin: 5px 0 5px 0;
+ margin: 5px 0 3px 0;
+ font-weight: 400;
}
/** TODO(crbug.com/1432194): Clean up the CSS with the shared styles added
* in https://crrev.com/c/4579629 once its in. */
.header-with-icon iron-icon {
border-radius: 50%;
+ flex-shrink: 0;
height: var(--cr-icon-size);
padding: 6px;
width: var(--cr-icon-size);
}
+ iron-icon[icon='cr:check'] {
+ background-color: var(--google-green-50);
+ fill: var(--google-green-700);
+ }
+
+ @media (prefers-color-scheme: dark) {
+ iron-icon[icon='cr:check'] {
+ background-color: var(--google-green-300);
+ fill: var(--grey-900-white-4-percent);
+ }
+ }
+
.text-container {
padding-left: 20px;
}
.header-icon {
+ align-items: center;
background-color: var(--google-blue-50);
fill: var(--google-blue-600);
}
@@ -35,16 +50,28 @@
@media (prefers-color-scheme: dark) {
.header-icon {
background-color: var(--google-blue-300);
+ fill: var(--review-panel-icon-color);
}
}
+ .header-group-wrapper {
+ flex: 1;
+ margin-inline-start: 15px;
+ }
+
+ .completion-container {
+ margin-top: 20px;
+ font-weight: 500;
+ font-size: 14px;
+ }
+
#extension-icon {
height: var(--cr-icon-size);
width: var(--cr-icon-size);
}
.extension-row {
- padding-inline-end: 10px;
+ padding-inline-end: 20px;
padding-inline-start: 50px;
}
@@ -58,16 +85,21 @@
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
- white-space: nowrap;
}
.bulk-action-button {
margin-inline-start: auto;
}
+
+ .cr-row.first {
+ padding-top: 5px;
+ padding-left: 0px;
+ }
</style>
<cr-expand-button class="cr-row first" no-hover
id="expandButton"
- expanded="{{unsafeExtensionsReviewListExpanded_}}">
+ expanded="{{unsafeExtensionsReviewListExpanded_}}"
+ hidden$="[[!shouldShowUnsafeExtensions_]]">
<div class="header-with-icon" id="reviewPanelContainer">
<iron-icon aria-hidden="true" icon="cr:extension" class="header-icon">
<!-- TODO(crbug.com/1432194):Will replace this
@@ -84,24 +116,25 @@
</div>
</cr-expand-button>
<iron-collapse class="extension-list"
- opened="[[unsafeExtensionsReviewListExpanded_]]">
+ opened="[[unsafeExtensionsReviewListExpanded_]]"
+ hidden$="[[!shouldShowUnsafeExtensions_]]">
<template is="dom-repeat" items="[[unsafeExtensions_]]">
<div class="extension-row cr-row">
<img id="extension-icon" src="[[item.iconUrl]]" role="presentation">
<div class="display-name text-elide">
<div class="extension-representation">[[item.name]]</div>
- <div class="secondary">
- <!-- TODO(crbug.com/1432194): Add secondary string
- (trigger warnings). -->
- Placeholder for the trigger text.
+ <div class="cr-secondary-text">
+ [[item.safetyCheckText.panelString]]
</div>
</div>
<cr-icon-button iron-icon="cr:delete"
- actionable on-click="onRemoveExtensionClick_">
+ actionable on-click="onRemoveExtensionClick_"
+ aria-label="[[getRemoveButtonA11yLabel_(item.name)]]">
</cr-icon-button>
<cr-icon-button class="icon-more-vert header-aligned-button"
id="makeExceptionMenuButton"
on-click="onMakeExceptionMenuClick_"
+ aria-label="[[getOptionMenuA11yLabel_(item.name)]]"
focus-type="makeExceptionMenuButton"></cr-icon-button>
</div>
</template>
@@ -114,6 +147,11 @@
</cr-button>
</div>
</iron-collapse>
+<div class="header-with-icon completion-container"
+ hidden$="[[!shouldShowCompletionInfo_]]">
+ <iron-icon role="img" icon="cr:check"></iron-icon>
+ <span class="header-group-wrapper">$i18n{safetyCheckAllDoneForNow}</span>
+</div>
<cr-action-menu id="makeExceptionMenu">
<button id="menuKeepExtension"
class="dropdown-item"
diff --git a/chromium/chrome/browser/resources/extensions/review_panel.ts b/chromium/chrome/browser/resources/extensions/review_panel.ts
index 4c6832decd2..d7884221096 100644
--- a/chromium/chrome/browser/resources/extensions/review_panel.ts
+++ b/chromium/chrome/browser/resources/extensions/review_panel.ts
@@ -10,12 +10,19 @@ import './shared_style.css.js';
import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
import {CrExpandButtonElement} from 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js';
import {DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {ItemDelegate} from './item.js';
import {getTemplate} from './review_panel.html.js';
+export interface ReviewItemDelegate {
+ setItemSafetyCheckWarningAcknowledged(id: string): void;
+ uninstallItem(id: string): Promise<void>;
+}
+
export interface ExtensionsReviewPanelElement {
$: {
makeExceptionMenu: CrActionMenuElement,
@@ -27,7 +34,10 @@ export interface ExtensionsReviewPanelElement {
};
}
-export class ExtensionsReviewPanelElement extends PolymerElement {
+const ExtensionsReviewPanelElementBase = I18nMixin(PolymerElement);
+
+export class ExtensionsReviewPanelElement extends
+ ExtensionsReviewPanelElementBase {
static get is() {
return 'extensions-review-panel';
}
@@ -62,12 +72,44 @@ export class ExtensionsReviewPanelElement extends PolymerElement {
unsafeExtensions_: Array,
/**
+ * Indicates whether to show completion info after user has finished the
+ * review process.
+ */
+ shouldShowCompletionInfo_: {
+ type: Boolean,
+ computed:
+ 'computeShouldShowCompletionInfo_(extensions.*, hasChangeBeenMade_)',
+ },
+
+ /**
+ * Indicates whether to show the potentially unsafe extensions or not.
+ */
+ shouldShowUnsafeExtensions_: {
+ type: Boolean,
+ computed: 'computeShouldShowUnsafeExtensions_(extensions.*)',
+ },
+
+ /**
* Indicates if the list of unsafe extensions is expanded or collapsed.
*/
unsafeExtensionsReviewListExpanded_: {
type: Boolean,
value: true,
},
+
+ /**
+ * Indicates if any potential unsafe extensions has been kept or removed.
+ */
+ hasChangeBeenMade_: {
+ type: Boolean,
+ value: false,
+ },
+
+ /**
+ * The latest id of an extension whose action menu (Keep the extension)
+ * was expanded.
+ * */
+ lastClickedExtensionId_: String,
};
}
@@ -75,12 +117,17 @@ export class ExtensionsReviewPanelElement extends PolymerElement {
return ['onExtensionsChanged_(extensions.*)'];
}
- delegate: ItemDelegate;
+ delegate: ItemDelegate&ReviewItemDelegate;
extensions: chrome.developerPrivate.ExtensionInfo[];
- private unsafeExtensions_: chrome.developerPrivate.ExtensionInfo[]|null;
+ private hasChangeBeenMade_: boolean;
+ private completionMetricLogged_: boolean;
+ private unsafeExtensions_: chrome.developerPrivate.ExtensionInfo[];
private headerString_: string;
private subtitleString_: string;
private unsafeExtensionsReviewListExpanded_: boolean;
+ private shouldShowCompletionInfo_: boolean;
+ private shouldShowUnsafeExtensions_: boolean;
+ private lastClickedExtensionId_: string;
private async onExtensionsChanged_() {
this.unsafeExtensions_ = this.getUnsafeExtensions_(this.extensions);
@@ -95,18 +142,52 @@ export class ExtensionsReviewPanelElement extends PolymerElement {
private getUnsafeExtensions_(extensions:
chrome.developerPrivate.ExtensionInfo[]):
chrome.developerPrivate.ExtensionInfo[] {
- // TODO(crbug.com/1432194): Update this filter criteria when new trigger
- // texts are added to getExtensionInfo API.
- return extensions.filter(
- extension => extension.disableReasons.corruptInstall ||
- extension.disableReasons.suspiciousInstall ||
- extension.runtimeWarnings.length || !!extension.blacklistText);
+ return extensions?.filter(
+ extension =>
+ !!(extension.safetyCheckText &&
+ extension.safetyCheckText.panelString &&
+ !extension.controlledInfo &&
+ extension.acknowledgeSafetyCheckWarning !== true));
+ }
+
+ /**
+ * Determines whether or not to show the completion info after the user
+ * finished reviewing extensions.
+ */
+ private computeShouldShowCompletionInfo_(): boolean {
+ const updatedUnsafeExtensions =
+ this.getUnsafeExtensions_(this.extensions) || [];
+ if (this.hasChangeBeenMade_ && updatedUnsafeExtensions.length === 0) {
+ if (!this.completionMetricLogged_) {
+ this.completionMetricLogged_ = true;
+ chrome.metricsPrivate.recordUserAction('SafetyCheck.ReviewCompletion');
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private computeShouldShowUnsafeExtensions_(): boolean {
+ const updatedUnsafeExtensions =
+ this.getUnsafeExtensions_(this.extensions) || [];
+ if (updatedUnsafeExtensions.length !== 0) {
+ if (!this.shouldShowUnsafeExtensions_) {
+ chrome.metricsPrivate.recordUserAction('SafetyCheck.ReviewPanelShown');
+ }
+ this.completionMetricLogged_ = false;
+ return true;
+ } else {
+ return false;
+ }
}
/**
* Opens the extension action menu.
*/
- private onMakeExceptionMenuClick_(e: Event) {
+ private onMakeExceptionMenuClick_(
+ e: DomRepeatEvent<chrome.developerPrivate.ExtensionInfo>) {
+ this.lastClickedExtensionId_ = e.model.item.id;
this.$.makeExceptionMenu.showAt(e.target as HTMLElement);
}
@@ -114,18 +195,52 @@ export class ExtensionsReviewPanelElement extends PolymerElement {
* Acknowledges the extension safety check warning.
*/
private onKeepExtensionClick_() {
+ chrome.metricsPrivate.recordUserAction(
+ 'SafetyCheck.ReviewPanelKeepClicked');
this.$.makeExceptionMenu.close();
- // TODO(crbug.com/1432194): Call the private API to keep the extension in
- // pref.
+ if (this.lastClickedExtensionId_) {
+ this.delegate.setItemSafetyCheckWarningAcknowledged(
+ this.lastClickedExtensionId_);
+ this.hasChangeBeenMade_ = true;
+ }
+ }
+
+ private getRemoveButtonA11yLabel_(extensionName: string): string {
+ return loadTimeData.substituteString(
+ this.i18n('safetyCheckRemoveButtonA11yLabel'), extensionName);
+ }
+
+ private getOptionMenuA11yLabel_(extensionName: string) {
+ return loadTimeData.substituteString(
+ this.i18n('safetyCheckOptionMenuA11yLabel'), extensionName);
}
- private onRemoveExtensionClick_(
- e: DomRepeatEvent<chrome.developerPrivate.ExtensionInfo>): void {
- this.delegate.deleteItem(e.model.item.id);
+ private async onRemoveExtensionClick_(
+ e: DomRepeatEvent<chrome.developerPrivate.ExtensionInfo>): Promise<void> {
+ chrome.metricsPrivate.recordUserAction(
+ 'SafetyCheck.ReviewPanelRemoveClicked');
+ try {
+ await this.delegate.uninstallItem(e.model.item.id);
+ this.hasChangeBeenMade_ = true;
+ } catch (_) {
+ // The error was almost certainly the user canceling the dialog.
+ // Do nothing.
+ }
}
- private onRemoveAllExtensions_(): void {
- // TODO(crbug.com/1432194): Call the private API to remove all extensions.
+ private async onRemoveAllClick_(): Promise<void> {
+ chrome.metricsPrivate.recordUserAction(
+ 'SafetyCheck.ReviewPanelRemoveAllClicked');
+ try {
+ await this.delegate.deleteItems(
+ this.unsafeExtensions_.map(extension => extension.id));
+ // If the Remove button was clicked and no errors were thrown, change
+ // the flag.
+ this.hasChangeBeenMade_ = true;
+ } catch (_) {
+ // The error was almost certainly the user canceling the dialog.
+ // Do nothing.
+ }
}
}
diff --git a/chromium/chrome/browser/resources/extensions/service.ts b/chromium/chrome/browser/resources/extensions/service.ts
index 1081d384b41..8aaf6e8272a 100644
--- a/chromium/chrome/browser/resources/extensions/service.ts
+++ b/chromium/chrome/browser/resources/extensions/service.ts
@@ -171,6 +171,28 @@ export class Service implements ServiceInterface {
});
}
+ /**
+ * Allows the consumer to call the API asynchronously.
+ */
+ uninstallItem(id: string): Promise<void> {
+ chrome.metricsPrivate.recordUserAction('Extensions.RemoveExtensionClick');
+ return chrome.management.uninstall(id, {showConfirmDialog: true});
+ }
+
+ deleteItems(ids: string[]): Promise<void> {
+ this.isDeleting_ = true;
+ return chrome.developerPrivate.removeMultipleExtensions(ids).finally(() => {
+ this.isDeleting_ = false;
+ });
+ }
+
+ setItemSafetyCheckWarningAcknowledged(id: string): Promise<void> {
+ return chrome.developerPrivate.updateExtensionConfiguration({
+ extensionId: id,
+ acknowledgeSafetyCheckWarning: true,
+ });
+ }
+
setItemEnabled(id: string, isEnabled: boolean) {
chrome.metricsPrivate.recordUserAction(
isEnabled ? 'Extensions.ExtensionEnabled' :
@@ -207,6 +229,13 @@ export class Service implements ServiceInterface {
});
}
+ setItemPinnedToToolbar(id: string, pinnedToToolbar: boolean) {
+ chrome.developerPrivate.updateExtensionConfiguration({
+ extensionId: id,
+ pinnedToToolbar,
+ });
+ }
+
inspectItemView(id: string, view: chrome.developerPrivate.ExtensionView):
void {
chrome.developerPrivate.openDevTools({
@@ -258,10 +287,10 @@ export class Service implements ServiceInterface {
return this.loadUnpackedHelper_();
}
- retryLoadUnpacked(retryGuid: string): Promise<boolean> {
+ retryLoadUnpacked(retryGuid?: string): Promise<boolean> {
// Attempt to load an unpacked extension, optionally as another attempt at
// a previously-specified load.
- return this.loadUnpackedHelper_({retryGuid: retryGuid});
+ return this.loadUnpackedHelper_({retryGuid});
}
choosePackRootDirectory(): Promise<string> {
diff --git a/chromium/chrome/browser/resources/extensions/shared_style.css b/chromium/chrome/browser/resources/extensions/shared_style.css
index e69974fdcd1..7785c384817 100644
--- a/chromium/chrome/browser/resources/extensions/shared_style.css
+++ b/chromium/chrome/browser/resources/extensions/shared_style.css
@@ -44,6 +44,18 @@ a[href] {
margin: 0 8px;
}
+.safety-check-wrapper {
+ flex: 1;
+ margin-inline-start: 15px;
+}
+
+.safety-check-icon {
+ background-color: var(--google-blue-50);
+ align-self: flex-start;
+ display: flex;
+ fill: var(--google-blue-600);
+}
+
.matching-restricted-sites-warning {
align-items: flex-start;
display: flex;
diff --git a/chromium/chrome/browser/resources/extensions/shared_vars.css b/chromium/chrome/browser/resources/extensions/shared_vars.css
index 67ceaef3d6a..24af72c8bdf 100644
--- a/chromium/chrome/browser/resources/extensions/shared_vars.css
+++ b/chromium/chrome/browser/resources/extensions/shared_vars.css
@@ -17,10 +17,12 @@ html {
--extensions-card-height: 160px;
--separator-gap: 9px;
--sidebar-width: 256px;
+ --cr-toolbar-field-width: 680px;
}
@media (prefers-color-scheme: dark) {
html {
+ --review-panel-icon-color: rgb(50, 54, 57); /* #323639 */
--error-color: var(--google-red-300);
--warning-color: var(--google-yellow-300);
}
diff --git a/chromium/chrome/browser/resources/extensions/sidebar.html b/chromium/chrome/browser/resources/extensions/sidebar.html
index 2c382510c96..103812bf71f 100644
--- a/chromium/chrome/browser/resources/extensions/sidebar.html
+++ b/chromium/chrome/browser/resources/extensions/sidebar.html
@@ -1,11 +1,11 @@
-<style include="cr-icons cr-hidden-style">
+<style include="cr-icons cr-hidden-style cr-nav-menu-item-style
+ cr-shared-style">
:host {
--sidebar-inactive-color: #5a5a5a;
color: var(--sidebar-inactive-color);
display: flex;
flex-direction: column;
height: 100%;
- justify-content: space-between;
overflow-x: hidden;
overflow-y: auto;
width: var(--sidebar-width);
@@ -17,28 +17,11 @@
}
}
- iron-selector .iron-selected {
- color: var(--cr-link-color);
- }
-
#sectionMenu {
padding-top: 8px;
user-select: none;
}
- .section-item {
- align-items: center;
- color: inherit;
- display: flex;
- font-weight: 500;
- /* Ensure the focus outline appears correctly (crbug.com/655503). */
- margin-inline-end: 4px;
- min-height: 40px;
- padding-inline-start: 24px;
- position: relative;
- text-decoration: none;
- }
-
.separator {
border-top: var(--cr-separator-line);
margin: 8px 0;
@@ -47,41 +30,55 @@
#moreExtensions {
align-items: center;
display: flex;
- justify-content: space-between;
margin-bottom: 8px;
}
- .cr-icon {
- margin-inline-end: calc(
- var(--cr-section-padding) - var(--cr-icon-ripple-padding));
+ #web-store-icon {
+ --iron-icon-height: 24px;
+ --iron-icon-width: 24px;
+ margin-inline-end: 18px;
+ margin-inline-start: -2px;
+ }
+
+ #discover-more-text {
+ line-height: 19px;
+ margin-inline-end: 10px;
}
</style>
-<iron-selector id="sectionMenu">
+<iron-selector id="sectionMenu"
+ selected-attribute="selected"
+ attr-for-selected="data-path"
+ selected="[[selectedPath_]]">
<!-- Values for "data-path" attribute must match the "Page" enum. -->
- <a class="section-item" id="sectionsExtensions" href="/"
+ <a class="cr-nav-menu-item" id="sectionsExtensions" href="/"
on-click="onLinkClick_" data-path="items-list">
+ <iron-icon icon="extensions-icons:my_extensions"></iron-icon>
$i18n{sidebarExtensions}
<paper-ripple></paper-ripple>
</a>
- <a class="section-item" id="sectionsSitePermissions"
+ <a class="cr-nav-menu-item" id="sectionsSitePermissions"
hidden="[[!enableEnhancedSiteControls]]"
href="/sitePermissions" on-click="onLinkClick_"
data-path="site-permissions">
+ <iron-icon icon="extensions-icons:site_permissions"></iron-icon>
$i18n{sitePermissions}
<paper-ripple></paper-ripple>
</a>
- <a class="section-item" id="sectionsShortcuts" href="/shortcuts"
+ <a class="cr-nav-menu-item" id="sectionsShortcuts" href="/shortcuts"
on-click="onLinkClick_" data-path="keyboard-shortcuts">
+ <iron-icon icon="extensions-icons:keyboard_shortcuts"></iron-icon>
$i18n{keyboardShortcuts}
<paper-ripple></paper-ripple>
</a>
</iron-selector>
<div>
<div class="separator"></div>
- <a class="section-item" id="moreExtensions" target="_blank"
- href="$i18n{getMoreExtensionsUrl}" on-click="onMoreExtensionsClick_">
- <span>$i18n{openChromeWebStore}</span>
- <div class="cr-icon icon-external"></div>
+ <div class="cr-nav-menu-item" id="moreExtensions">
+ <iron-icon id="web-store-icon" icon="extensions-icons:web_store">
+ </iron-icon>
+ <span id="discover-more-text" class="cr-secondary-text"
+ on-click="onMoreExtensionsClick_"
+ inner-h-t-m-l="[[discoverMoreText_]]"></span>
<paper-ripple></paper-ripple>
- </a>
+ </div>
</div>
diff --git a/chromium/chrome/browser/resources/extensions/sidebar.ts b/chromium/chrome/browser/resources/extensions/sidebar.ts
index 375ac5a32e0..d0997e4433f 100644
--- a/chromium/chrome/browser/resources/extensions/sidebar.ts
+++ b/chromium/chrome/browser/resources/extensions/sidebar.ts
@@ -3,11 +3,17 @@
// found in the LICENSE file.
import 'chrome://resources/cr_elements/cr_icons.css.js';
import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
+import 'chrome://resources/cr_elements/cr_nav_menu_item_style.css.js';
+import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
import 'chrome://resources/polymer/v3_0/paper-ripple/paper-ripple.js';
import 'chrome://resources/polymer/v3_0/paper-styles/color.js';
+import './icons.html.js';
import './shared_vars.css.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {IronSelectorElement} from 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -24,7 +30,9 @@ export interface ExtensionsSidebarElement {
};
}
-export class ExtensionsSidebarElement extends PolymerElement {
+const ExtensionsSidebarElementBase = I18nMixin(PolymerElement);
+
+export class ExtensionsSidebarElement extends ExtensionsSidebarElementBase {
static get is() {
return 'extensions-sidebar';
}
@@ -36,28 +44,67 @@ export class ExtensionsSidebarElement extends PolymerElement {
static get properties() {
return {
enableEnhancedSiteControls: Boolean,
+
+ /**
+ * The data path/page that identifies the entry to be selected in the
+ * sidebar. Note that this may not match the page that's actually
+ * displayed.
+ */
+ selectedPath_: String,
+
+ /**
+ * The text displayed in the sidebar containing the link to open the
+ * Chrome Web Store to get more extensions.
+ */
+ discoverMoreText_: {
+ type: String,
+ computed: 'computeDiscoverMoreText_()',
+ },
};
}
enableEnhancedSiteControls: boolean;
+ private selectedPath_: Page;
+ private discoverMoreText_: TrustedHTML;
+
+ /**
+ * The ID of the listener on |navigation|. Stored so that the
+ * listener can be removed when this element is detached (happens in tests).
+ */
+ private navigationListener_: number|null = null;
override ready() {
super.ready();
this.setAttribute('role', 'navigation');
+ this.computeSelectedPath_(navigation.getCurrentPage().page);
}
override connectedCallback() {
super.connectedCallback();
+ this.navigationListener_ = navigation.addListener(newPage => {
+ this.computeSelectedPath_(newPage.page);
+ });
+ }
- const page = navigation.getCurrentPage().page;
- let selectIndex = 0;
- if (page === Page.SITE_PERMISSIONS ||
- page === Page.SITE_PERMISSIONS_ALL_SITES) {
- selectIndex = 1;
- } else if (page === Page.SHORTCUTS) {
- selectIndex = 2;
+ override disconnectedCallback() {
+ super.disconnectedCallback();
+ assert(this.navigationListener_);
+ assert(navigation.removeListener(this.navigationListener_));
+ this.navigationListener_ = null;
+ }
+
+ private computeSelectedPath_(page: Page) {
+ switch (page) {
+ case Page.SITE_PERMISSIONS:
+ case Page.SITE_PERMISSIONS_ALL_SITES:
+ this.selectedPath_ = Page.SITE_PERMISSIONS;
+ break;
+ case Page.SHORTCUTS:
+ this.selectedPath_ = Page.SHORTCUTS;
+ break;
+ default:
+ this.selectedPath_ = Page.LIST;
}
- this.$.sectionMenu.select(selectIndex);
}
private onLinkClick_(e: Event) {
@@ -68,8 +115,18 @@ export class ExtensionsSidebarElement extends PolymerElement {
new CustomEvent('close-drawer', {bubbles: true, composed: true}));
}
- private onMoreExtensionsClick_() {
- chrome.metricsPrivate.recordUserAction('Options_GetMoreExtensions');
+ private onMoreExtensionsClick_(e: Event) {
+ if ((e.target as HTMLElement).tagName === 'A') {
+ chrome.metricsPrivate.recordUserAction('Options_GetMoreExtensions');
+ }
+ }
+
+ private computeDiscoverMoreText_(): TrustedHTML {
+ return this.i18nAdvanced('sidebarDiscoverMore', {
+ tags: ['a'],
+ attrs: ['target', 'on-click'],
+ substitutions: [loadTimeData.getString('getMoreExtensionsUrl')],
+ });
}
}
diff --git a/chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.html b/chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.html
index 78e8f85b398..166eb72bb40 100644
--- a/chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.html
+++ b/chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.html
@@ -104,7 +104,8 @@
<option value="[[hostAccessEnum_.ON_SPECIFIC_SITES]]">
$i18n{sitePermissionsAlwaysOnThisSite}
</option>
- <option value="[[hostAccessEnum_.ON_ALL_SITES]]">
+ <option value="[[hostAccessEnum_.ON_ALL_SITES]]"
+ disabled="[[!item.canRequestAllSites]]">
$i18n{sitePermissionsAlwaysOnAllSites}
</option>
</select>
diff --git a/chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.ts b/chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.ts
index ac2b8ce0559..3852948d739 100644
--- a/chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.ts
+++ b/chromium/chrome/browser/resources/extensions/site_permissions_edit_permissions_dialog.ts
@@ -28,6 +28,7 @@ interface ExtensionSiteAccessInfo {
iconUrl: string;
siteAccess: string;
addedByPolicy: boolean;
+ canRequestAllSites: boolean;
}
export interface SitePermissionsEditPermissionsDialogElement {
@@ -202,13 +203,13 @@ export class SitePermissionsEditPermissionsDialogElement extends
await this.delegate.getMatchingExtensionsForSite(siteToCheck);
const extensionSiteAccessData: ExtensionSiteAccessInfo[] = [];
- matchingExtensionsInfo.forEach(({id, siteAccess}) => {
+ matchingExtensionsInfo.forEach(({id, siteAccess, canRequestAllSites}) => {
assert(this.extensionsIdToInfo_.has(id));
const {name, iconUrl} = this.extensionsIdToInfo_.get(id)!;
const addedByPolicy = getItemSource(this.extensionsIdToInfo_.get(id)!) ===
SourceType.POLICY;
extensionSiteAccessData.push(
- {id, name, iconUrl, siteAccess, addedByPolicy});
+ {id, name, iconUrl, siteAccess, addedByPolicy, canRequestAllSites});
// Remove the unsaved HostAccess from `unsavedExtensionsIdToHostAccess_`
// if it is now the same as `siteAccess`.
@@ -311,6 +312,12 @@ export class SitePermissionsEditPermissionsDialogElement extends
const newSiteAccess =
selectMenu.value as chrome.developerPrivate.HostAccess;
+ // Sanity check that extensions that don't request all sites access cannot
+ // request all sites access from the dialog.
+ assert(
+ e.model.item.canRequestAllSites ||
+ newSiteAccess !== chrome.developerPrivate.HostAccess.ON_ALL_SITES);
+
if (originalSiteAccess === newSiteAccess) {
this.unsavedExtensionsIdToHostAccess_.delete(e.model.item.id);
} else {
diff --git a/chromium/chrome/browser/resources/extensions/toolbar.ts b/chromium/chrome/browser/resources/extensions/toolbar.ts
index 05eca3abab7..2a16f0a3a08 100644
--- a/chromium/chrome/browser/resources/extensions/toolbar.ts
+++ b/chromium/chrome/browser/resources/extensions/toolbar.ts
@@ -118,6 +118,14 @@ export class ExtensionsToolbarElement extends ExtensionsToolbarElementBase {
this.setAttribute('role', 'banner');
}
+ focusSearchInput() {
+ this.$.toolbar.getSearchField().showAndFocus();
+ }
+
+ isSearchFocused(): boolean {
+ return this.$.toolbar.getSearchField().isSearchFocused();
+ }
+
private fire_(eventName: string, detail?: any) {
this.dispatchEvent(
new CustomEvent(eventName, {bubbles: true, composed: true, detail}));
diff --git a/chromium/chrome/browser/resources/feedback/BUILD.gn b/chromium/chrome/browser/resources/feedback/BUILD.gn
index e698a88c321..04687c0b4ba 100644
--- a/chromium/chrome/browser/resources/feedback/BUILD.gn
+++ b/chromium/chrome/browser/resources/feedback/BUILD.gn
@@ -30,7 +30,7 @@ build_webui("build") {
if (is_chromeos_ash) {
static_files += [
- "css/assistant_logs_info.css",
+ "css/logs_info.css",
"html/assistant_logs_info.html",
"html/bluetooth_logs_info.html",
]
@@ -38,6 +38,7 @@ build_webui("build") {
non_web_component_files = [
"js/autofill_metadata.ts",
+ "js/jelly_colors.ts",
"js/feedback.ts",
"js/feedback_util.ts",
"js/logs_map_page.ts",
diff --git a/chromium/chrome/browser/resources/feedback/css/feedback.css b/chromium/chrome/browser/resources/feedback/css/feedback.css
index e199bde4a27..3d12212bbff 100644
--- a/chromium/chrome/browser/resources/feedback/css/feedback.css
+++ b/chromium/chrome/browser/resources/feedback/css/feedback.css
@@ -48,6 +48,83 @@ body {
padding: 20px;
}
+html:has(body.jelly-enabled),
+html:has(body.jelly-enabled) body,
+html:has(body.jelly-enabled) .text-field-container > select,
+html:has(body.jelly-enabled) .content {
+ background-color: var(--feedback-bg-color);
+ color: var(--cros-sys-on_surface_variant);
+ font: var(--cros-body-2-font);
+}
+
+html:has(body.jelly-enabled) textarea,
+html:has(body.jelly-enabled) input {
+ background-color: var(--cros-sys-input_field_on_shaded);
+ font: var(--cros-body-2-font);
+}
+
+html:has(body.jelly-enabled) .checkbox-field-container,
+html:has(body.jelly-enabled) .text-field-container {
+ color: var(--cros-sys-on_surface);
+ font: var(--cros-body-2-font);
+}
+
+html:has(body.jelly-enabled) a {
+ color: var(--cros-sys-primary);
+}
+
+html:has(body.jelly-enabled) .attach-file-notification,
+html:has(body.jelly-enabled) .description-empty-notification {
+ color: var(--cros-sys-error);
+ font: var(--cros-annotation-2-font);
+}
+
+html:has(body.jelly-enabled) textarea[aria-invalid="true"] {
+ color: var(--cros-sys-error);
+ outline: 2px solid var(--cros-sys-error);
+}
+
+html:has(body.jelly-enabled) #free-form-text,
+html:has(body.jelly-enabled) #additional-info-label {
+ font: var(--cros-headline-1-font);
+}
+
+html:has(body.jelly-enabled) .bottom-buttons button {
+ border-radius: 18px;
+ font: var(--cros-button-2-font);
+}
+
+html:has(body.jelly-enabled) #send-report-button {
+ background-color: var(--cros-sys-primary);
+ color: var(--cros-sys-on_primary);
+}
+
+html:has(body.jelly-enabled) #cancel-button {
+ background-color: var(--cros-sys-primary_container);
+ color: var(--cros-sys-on_primary_container);
+}
+
+#cancel-button-hover-bg,
+#send-button-hover-bg {
+ border-radius: 18px;
+ content: '';
+ display: none;
+ inset: 0;
+ pointer-events: none;
+ position: absolute;
+ z-index: 1;
+}
+
+html:has(body.jelly-enabled) #send-report-button:hover > #send-button-hover-bg {
+ background-color: var(--cros-sys-hover_on_prominent);
+ display: block;
+}
+
+html:has(body.jelly-enabled) #cancel-button:hover > #cancel-button-hover-bg {
+ background-color: var(--cros-sys-hover_on_subtle);
+ display: block;
+}
+
.content {
color: var(--feedback-primary-color);
font-size: 12px;
@@ -175,7 +252,7 @@ body {
margin-inline-end: 130px;
}
-.content #privacy-note {
+.content .privacy-note {
font-size: 12px;
line-height: 20px;
margin-bottom: 0;
diff --git a/chromium/chrome/browser/resources/feedback/css/assistant_logs_info.css b/chromium/chrome/browser/resources/feedback/css/logs_info.css
index a0d5a87e9e4..ff1a96c1f9f 100644
--- a/chromium/chrome/browser/resources/feedback/css/assistant_logs_info.css
+++ b/chromium/chrome/browser/resources/feedback/css/logs_info.css
@@ -17,7 +17,7 @@ body {
width: 100%;
}
-#assistant-logs-info-container {
+.logs-info-container {
line-height: 20px;
margin-bottom: 20px;
margin-inline-end: 20px;
@@ -26,3 +26,11 @@ body {
text-align: justify;
text-justify: inter-word;
}
+
+html:has(body.jelly-enabled),
+html:has(body.jelly-enabled) body,
+html:has(body.jelly-enabled) .logs-info-container {
+ background-color: var(--feedback-bg-color);
+ color: var(--cros-sys-on_surface_variant);
+ font: var(--cros-body-2-font);
+}
diff --git a/chromium/chrome/browser/resources/feedback/html/assistant_logs_info.html b/chromium/chrome/browser/resources/feedback/html/assistant_logs_info.html
index 8e451e4c4b8..edb24001703 100644
--- a/chromium/chrome/browser/resources/feedback/html/assistant_logs_info.html
+++ b/chromium/chrome/browser/resources/feedback/html/assistant_logs_info.html
@@ -3,11 +3,18 @@
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
-<link rel="stylesheet" href="../css/assistant_logs_info.css"></link>
-<link rel="stylesheet" href="../css/feedback_shared_styles.css"></link>
+<if expr="chromeos_ash">
+ <link rel="stylesheet" href="//theme/colors.css?sets=sys">
+</if>
+<link rel="stylesheet" href="../css/feedback_shared_styles.css">
+<link rel="stylesheet" href="../css/logs_info.css">
+</link>
+<if expr="chromeos_ash">
+ <script type="module" src="../js/jelly_colors.js"></script>
+</if>
</head>
<body>
- <div id="assistant-logs-info-container">
+ <div class="logs-info-container">
<span>$i18n{assistantLogsMessage}</span>
</div>
</body>
diff --git a/chromium/chrome/browser/resources/feedback/html/autofill_metadata_info.html b/chromium/chrome/browser/resources/feedback/html/autofill_metadata_info.html
index c80f7f8f6da..f0773161885 100644
--- a/chromium/chrome/browser/resources/feedback/html/autofill_metadata_info.html
+++ b/chromium/chrome/browser/resources/feedback/html/autofill_metadata_info.html
@@ -7,9 +7,14 @@
<title>$i18n{autofillMetadataPageTitle}</title>
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="stylesheet" href="../css/about_sys.css">
+ <if expr="chromeos_ash">
+ <link rel="stylesheet" href="//theme/colors.css?sets=sys">
+ <link rel="stylesheet" href="//theme/typography.css">
+ </if>
<link rel="stylesheet" href="../css/logs_map_page.css">
<link rel="stylesheet" href="../css/feedback_shared_styles.css">
-<script type="module" src="../js/autofill_metadata.js"></script>
+
+ <script type="module" src="../js/autofill_metadata.js"></script>
</head>
<body>
diff --git a/chromium/chrome/browser/resources/feedback/html/bluetooth_logs_info.html b/chromium/chrome/browser/resources/feedback/html/bluetooth_logs_info.html
index feceb495ea5..9d54b6fbfef 100644
--- a/chromium/chrome/browser/resources/feedback/html/bluetooth_logs_info.html
+++ b/chromium/chrome/browser/resources/feedback/html/bluetooth_logs_info.html
@@ -2,10 +2,18 @@
<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta name="color-scheme" content="light dark">
+ <if expr="chromeos_ash">
+ <link rel="stylesheet" href="//theme/colors.css?sets=sys">
+ </if>
<link rel="stylesheet" href="../css/feedback_shared_styles.css">
+ <link rel="stylesheet" href="../css/logs_info.css">
+ </link>
+ <if expr="chromeos_ash">
+ <script type="module" src="../js/jelly_colors.js"></script>
+ </if>
</head>
<body>
- <div id="bluetooth-logs-info-container">
+ <div class="logs-info-container">
<span>$i18n{bluetoothLogsMessage}</span>
</div>
</body>
diff --git a/chromium/chrome/browser/resources/feedback/html/default.html b/chromium/chrome/browser/resources/feedback/html/default.html
index 35503c2cc44..492303c2b55 100644
--- a/chromium/chrome/browser/resources/feedback/html/default.html
+++ b/chromium/chrome/browser/resources/feedback/html/default.html
@@ -8,6 +8,10 @@
<link rel="stylesheet" href="../css/common.css">
<link rel="stylesheet" href="../css/feedback.css">
<link rel="stylesheet" href="../css/feedback_shared_styles.css">
+ <if expr="chromeos_ash">
+ <link rel="stylesheet" href="chrome://theme/colors.css?sets=sys">
+ <link rel="stylesheet" href="//theme/typography.css">
+ </if>
<script type="module" src="../js/feedback.js"></script>
</head>
@@ -51,7 +55,7 @@
</div>
<div id="attach-file-note" aria-hidden="true">$i18n{attachFileNote}</div>
<!-- User Consent -->
- <div id="consent-container" class="checkbox-field-container">
+ <div id="consent-container" class="checkbox-field-container" hidden>
<input id="consent-checkbox" type="checkbox"
aria-labelledby="consent-chk-label">
<label id="consent-chk-label">$i18n{consentCheckboxLabel}</label>
@@ -97,17 +101,28 @@
</div>
</if>
<!-- Privacy note -->
- <div id="privacy-note">$i18nRaw{privacyNote}</div>
+ <div id="privacy-note" class="privacy-note">$i18nRaw{privacyNote}</div>
+<if expr="chromeos_ash">
+ <div id="share-privacy-note" class="privacy-note">
+ $i18n{mayBeSharedWithPartnerNote}
+ </div>
+</if>
</div>
<!-- Buttons -->
<div id="bottom-buttons-container" class="content">
<div class="buttons-pane bottom-buttons">
- <button id="cancel-button" type="submit"
- class="white-button">$i18n{cancel}
+ <button id="cancel-button" type="submit" class="white-button">
+ $i18n{cancel}
+ <if expr="chromeos_ash">
+ <div id="cancel-button-hover-bg"></div>
+ </if>
</button>
<button id="send-report-button" type="submit"
class="blue-button" aria-describedby="questionnaire-notification">
$i18n{sendReport}
+ <if expr="chromeos_ash">
+ <div id="send-button-hover-bg"></div>
+ </if>
</button>
</div>
</div>
diff --git a/chromium/chrome/browser/resources/feedback/js/autofill_metadata.ts b/chromium/chrome/browser/resources/feedback/js/autofill_metadata.ts
index 26f8de502e0..1569ee6ed93 100644
--- a/chromium/chrome/browser/resources/feedback/js/autofill_metadata.ts
+++ b/chromium/chrome/browser/resources/feedback/js/autofill_metadata.ts
@@ -1,6 +1,7 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import {configureJellyColors} from './jelly_colors.js';
import {createLogsMapTable} from './logs_map_page.js';
const dialogArgs: string = chrome.getVariableValue('dialogArguments');
@@ -54,4 +55,5 @@ window.onload = function() {
}
createAutofillMetadataTable();
+ configureJellyColors();
};
diff --git a/chromium/chrome/browser/resources/feedback/js/feedback.ts b/chromium/chrome/browser/resources/feedback/js/feedback.ts
index 4220394e4e2..a6e1fa2f373 100644
--- a/chromium/chrome/browser/resources/feedback/js/feedback.ts
+++ b/chromium/chrome/browser/resources/feedback/js/feedback.ts
@@ -1,8 +1,11 @@
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import '../../strings.m.js';
+import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
import {assert} from 'chrome://resources/js/assert_ts.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {$, getRequiredElement} from 'chrome://resources/js/util_ts.js';
import {FEEDBACK_LANDING_PAGE, FEEDBACK_LANDING_PAGE_TECHSTOP, FEEDBACK_LEGAL_HELP_URL, FEEDBACK_PRIVACY_POLICY_URL, FEEDBACK_TERM_OF_SERVICE_URL, openUrlInAppWindow} from './feedback_util.js';
@@ -703,6 +706,8 @@ function initialize() {
// Now we can unhide the user email section:
getRequiredElement('user-email').hidden = false;
+ // Only show email consent checkbox when an email address exists.
+ getRequiredElement('consent-container').hidden = false;
});
// An extension called us with an attached file.
@@ -839,6 +844,11 @@ function initialize() {
}
applyData(feedbackInfo);
+ if (loadTimeData.getBoolean('isJellyEnabledForOsFeedback')) {
+ document.body.classList.add('jelly-enabled');
+ ColorChangeUpdater.forDocument().start();
+ }
+
Object.assign(window, {feedbackInfo, feedbackHelper});
// Setup our event handlers.
diff --git a/chromium/chrome/browser/resources/feedback/js/jelly_colors.ts b/chromium/chrome/browser/resources/feedback/js/jelly_colors.ts
new file mode 100644
index 00000000000..0f38a212763
--- /dev/null
+++ b/chromium/chrome/browser/resources/feedback/js/jelly_colors.ts
@@ -0,0 +1,25 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+import '../../strings.m.js';
+
+import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+
+// Configures color change listener for dynamic colors. Used when sys_info
+// dialog is opened in OS Feedback.
+// TODO(b/276493287): Move add class to HTML when jelly colors and OS Feedback
+// launches by default in ASH.
+export function configureJellyColors() {
+ if (loadTimeData.getBoolean('isJellyEnabledForOsFeedback')) {
+ document.body.classList.add('jelly-enabled');
+ ColorChangeUpdater.forDocument().start();
+ }
+}
+
+/**
+ * Initializes the page when the window is loaded.
+ */
+window.onload = function() {
+ configureJellyColors();
+};
diff --git a/chromium/chrome/browser/resources/feedback/js/questionnaire.ts b/chromium/chrome/browser/resources/feedback/js/questionnaire.ts
index 1548a59dcea..001c4f0dab1 100644
--- a/chromium/chrome/browser/resources/feedback/js/questionnaire.ts
+++ b/chromium/chrome/browser/resources/feedback/js/questionnaire.ts
@@ -122,11 +122,10 @@ const questionThunderboltDisplays = '[Thunderbolt] If you are having trouble ' +
const questionAudioApp = '[Audio] When the issue occurred, which specific ' +
'app were you using?';
-const questionAudioDevice = '[Audio] What audio device are you using? Are ' +
- 'you using any external audio devices? If so, please provide more ' +
- 'details about how your audio devices are connected to your chromebook, ' +
- 'ex: via Bluetooth, USB dock or just the built-in speaker and mic. ' +
- 'Please provide the brand and model name if any external devices are used';
+const questionAudioDevice = '[Audio] What audio devices are you using? Are ' +
+ 'you using the built-in speaker and mic? If not, please provide more ' +
+ 'details about the brand and model name of your audio devices and how ' +
+ 'they are connected to your Chromebook?';
const questionAudioMeet = '[Audio] If it’s a Meet issue, please provide the ' +
'affected Meeting Id.';
diff --git a/chromium/chrome/browser/resources/gaia_auth_host/BUILD.gn b/chromium/chrome/browser/resources/gaia_auth_host/BUILD.gn
index 6556ebe7efd..296ba241980 100644
--- a/chromium/chrome/browser/resources/gaia_auth_host/BUILD.gn
+++ b/chromium/chrome/browser/resources/gaia_auth_host/BUILD.gn
@@ -5,7 +5,6 @@
import("//build/config/chromeos/ui_mode.gni")
import("//chrome/common/features.gni")
import("//chrome/test/base/js2gtest.gni")
-import("//chrome/test/include_js_tests.gni")
import("//third_party/closure_compiler/compile_js.gni")
import("//tools/grit/grit_rule.gni")
import("//tools/grit/preprocess_if_expr.gni")
diff --git a/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js b/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js
index 3e2f7629a6e..28fcd07cf6a 100644
--- a/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js
+++ b/chromium/chrome/browser/resources/gaia_auth_host/authenticator.js
@@ -199,23 +199,16 @@ export const SUPPORTED_PARAMS = [
'ssoProfile', // An identifier for the device's managing OU's
// SAML SSO setting. Used by the login screen to
// pass to Gaia.
-
- // The email fields allow for the following possibilities:
- //
- // 1/ If 'email' is not supplied, then the email text field is blank and the
- // user must type an email to proceed.
- //
- // 2/ If 'email' is supplied, and 'readOnlyEmail' is truthy, then the email
- // is hardcoded and the user cannot change it. The user is asked for
- // password. This is useful for re-auth scenarios, where chrome needs the
- // user to authenticate for a specific account and only that account.
- //
- // 3/ If 'email' is supplied, and 'readOnlyEmail' is falsy, gaia will
- // prefill the email text field using the given email address, but the user
- // can still change it and then proceed. This is used on desktop when the
- // user disconnects their profile then reconnects, to encourage them to use
- // the same account.
+ // The email can be passed to Gaia to let it know which user is trying to
+ // sign in. Gaia behavior can be different depending on the `gaiaPath`: it
+ // can either simply prefill the email field, but still allow modifying it,
+ // or it can proceed straight to the authentication challenge for the
+ // corresponding account, not allowing the user to modify the email.
'email',
+ // Determines which URL parameter will be used to pass the email to Gaia.
+ // TODO(b/292087570): misleading name, should be either renamed or
+ // removed completely (need to confirm if email_hint URL parameter
+ // is still relevant for some flows).
'readOnlyEmail',
'realm',
// If the authentication is done via external IdP, 'startsOnSamlPage'
@@ -232,6 +225,9 @@ export const SUPPORTED_PARAMS = [
// username.
'urlParameterToAutofillSAMLUsername',
'forceDarkMode',
+ // A tri-state value which indicates the support level for passwordless login.
+ // Refer to `GaiaView::PasswordlessSupportLevel` for details.
+ 'pwl',
];
// Timeout in ms to wait for the message from Gaia indicating end of the flow.
@@ -778,6 +774,9 @@ export class Authenticator extends EventTarget {
'&scope=https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthLogin&' +
'client_id=' + encodeURIComponent(data.clientId) +
'&access_type=offline');
+ if (data.rart) {
+ url = appendParam(url, 'rart', data.rart);
+ }
return url;
}
@@ -864,6 +863,9 @@ export class Authenticator extends EventTarget {
if (data.forceDarkMode) {
url = appendParam(url, 'color_scheme', 'dark');
}
+ if (data.pwl) {
+ url = appendParam(url, 'pwl', data.pwl);
+ }
return url;
}
diff --git a/chromium/chrome/browser/resources/hangout_services/thunk.js b/chromium/chrome/browser/resources/hangout_services/thunk.js
index f10e62559ed..dbe61578029 100644
--- a/chromium/chrome/browser/resources/hangout_services/thunk.js
+++ b/chromium/chrome/browser/resources/hangout_services/thunk.js
@@ -78,35 +78,21 @@ chrome.runtime.onMessageExternal.addListener(function(
// logs have already been stopped or not started. Therefore, ignore
// any errors along the way, but store them, so that if upload fails
// they are all reported back.
- // Stop incoming and outgoing RTP dumps separately, otherwise
- // stopRtpDump will fail and not stop anything if either type has not
- // been started.
const errors = [];
- chrome.webrtcLoggingPrivate.stopRtpDump(
- requestInfo, origin, true /* incoming */, false /* outgoing */,
- function() {
- appendLastErrorMessage(errors);
- chrome.webrtcLoggingPrivate.stopRtpDump(
- requestInfo, origin, false /* incoming */, true /* outgoing */,
- function() {
- appendLastErrorMessage(errors);
- chrome.webrtcLoggingPrivate.stop(
- requestInfo, origin, function() {
- appendLastErrorMessage(errors);
- chrome.webrtcLoggingPrivate.upload(
- requestInfo, origin, function(uploadValue) {
- let errorMessage = null;
- // If upload fails, report all previous errors.
- // Otherwise, throw them away.
- if (chrome.runtime.lastError !== undefined) {
- appendLastErrorMessage(errors);
- errorMessage = errors.join('; ');
- }
- doSendResponse(uploadValue, errorMessage);
- });
- });
- });
- });
+ chrome.webrtcLoggingPrivate.stop(requestInfo, origin, function() {
+ appendLastErrorMessage(errors);
+ chrome.webrtcLoggingPrivate.upload(
+ requestInfo, origin, function(uploadValue) {
+ let errorMessage = null;
+ // If upload fails, report all previous errors.
+ // Otherwise, throw them away.
+ if (chrome.runtime.lastError !== undefined) {
+ appendLastErrorMessage(errors);
+ errorMessage = errors.join('; ');
+ }
+ doSendResponse(uploadValue, errorMessage);
+ });
+ });
return true;
} else if (method === 'logging.store') {
const logId = message['logId'];
@@ -116,18 +102,6 @@ chrome.runtime.onMessageExternal.addListener(function(
} else if (method === 'logging.discard') {
chrome.webrtcLoggingPrivate.discard(requestInfo, origin, doSendResponse);
return true;
- } else if (method === 'logging.startRtpDump') {
- const incoming = message['incoming'] || false;
- const outgoing = message['outgoing'] || false;
- chrome.webrtcLoggingPrivate.startRtpDump(
- requestInfo, origin, incoming, outgoing, doSendResponse);
- return true;
- } else if (method === 'logging.stopRtpDump') {
- const incoming = message['incoming'] || false;
- const outgoing = message['outgoing'] || false;
- chrome.webrtcLoggingPrivate.stopRtpDump(
- requestInfo, origin, incoming, outgoing, doSendResponse);
- return true;
} else if (method === 'logging.startEventLogging') {
const sessionId = message['sessionId'] || '';
const maxLogSizeBytes = message['maxLogSizeBytes'] || 0;
diff --git a/chromium/chrome/browser/resources/hats/BUILD.gn b/chromium/chrome/browser/resources/hats/BUILD.gn
new file mode 100644
index 00000000000..5bd97fc1905
--- /dev/null
+++ b/chromium/chrome/browser/resources/hats/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD - style license that can be
+# found in the LICENSE file.
+
+import("//ui/webui/resources/tools/build_webui.gni")
+
+assert(!is_android)
+
+build_webui("build") {
+ grd_prefix = "hats"
+
+ static_files = [ "hats.html" ]
+
+ non_web_component_files = [ "hats.ts" ]
+
+ ts_deps = [ "//ui/webui/resources/js:build_ts" ]
+}
diff --git a/chromium/chrome/browser/resources/hats/DIR_METADATA b/chromium/chrome/browser/resources/hats/DIR_METADATA
new file mode 100644
index 00000000000..4b06f183eb4
--- /dev/null
+++ b/chromium/chrome/browser/resources/hats/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+ component: "UI>Browser>HaTS"
+}
diff --git a/chromium/chrome/browser/resources/hats/OWNERS b/chromium/chrome/browser/resources/hats/OWNERS
new file mode 100644
index 00000000000..bbdb9fdfe9a
--- /dev/null
+++ b/chromium/chrome/browser/resources/hats/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ui/hats/OWNERS
diff --git a/chromium/chrome/browser/resources/hats/hats.html b/chromium/chrome/browser/resources/hats/hats.html
new file mode 100644
index 00000000000..40c9128c8bc
--- /dev/null
+++ b/chromium/chrome/browser/resources/hats/hats.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ margin: 0;
+ }
+ </style>
+</head>
+
+<body>
+ <script type="module" src="hats.js"></script>
+ <h1>HaTS</h1>
+ <div id="example-div">This is a sample survey.</div>
+</body>
+
+</html>
diff --git a/chromium/chrome/browser/resources/hats/hats.ts b/chromium/chrome/browser/resources/hats/hats.ts
new file mode 100644
index 00000000000..ef76a591932
--- /dev/null
+++ b/chromium/chrome/browser/resources/hats/hats.ts
@@ -0,0 +1,7 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function initialize() {}
+
+document.addEventListener('DOMContentLoaded', initialize);
diff --git a/chromium/chrome/browser/resources/history/app.ts b/chromium/chrome/browser/resources/history/app.ts
index 7a162bfa479..dd641bee076 100644
--- a/chromium/chrome/browser/resources/history/app.ts
+++ b/chromium/chrome/browser/resources/history/app.ts
@@ -200,7 +200,7 @@ export class HistoryAppElement extends HistoryAppElementBase {
historyClustersPath_: {
type: Boolean,
value: () =>
- loadTimeData.getBoolean('renameJourneys') ? '2' : 'journeys',
+ loadTimeData.getBoolean('renameJourneys') ? 'grouped' : 'journeys',
},
showHistoryClusters_: {
diff --git a/chromium/chrome/browser/resources/history/history_item.html b/chromium/chrome/browser/resources/history/history_item.html
index f9ac90980b4..92a190f5988 100644
--- a/chromium/chrome/browser/resources/history/history_item.html
+++ b/chromium/chrome/browser/resources/history/history_item.html
@@ -243,7 +243,7 @@
title="$i18n{actionMenuDescription}" on-click="onMenuButtonClick_"
on-keydown="onMenuButtonKeydown_"
aria-haspopup="menu"
- aria-describedby$="[[ariaDescribedByForHeading_]]">
+ aria-describedby$="[[ariaDescribedByForActions_]]">
</cr-icon-button>
</div>
</div>
diff --git a/chromium/chrome/browser/resources/history/history_item.ts b/chromium/chrome/browser/resources/history/history_item.ts
index 0471073ec09..30dd315c827 100644
--- a/chromium/chrome/browser/resources/history/history_item.ts
+++ b/chromium/chrome/browser/resources/history/history_item.ts
@@ -103,6 +103,11 @@ export class HistoryItemElement extends HistoryItemElementBase {
type: String,
computed: 'getAriaDescribedByForHeading_(isCardStart, isCardEnd)',
},
+
+ ariaDescribedByForActions_: {
+ type: String,
+ computed: 'getAriaDescribedByForActions_(isCardStart, isCardEnd)',
+ },
};
}
@@ -233,6 +238,16 @@ export class HistoryItemElement extends HistoryItemElementBase {
return this.isCardStart || this.isCardEnd ? 'date-accessed' : '';
}
+ /**
+ * Actions menu is described by the title and domain of the row and may
+ * include the date to make sure users know if they have jumped between dates.
+ */
+ private getAriaDescribedByForActions_(): string {
+ return this.isCardStart || this.isCardEnd ?
+ 'title-and-domain date-accessed' :
+ 'title-and-domain';
+ }
+
private getAriaChecked_(selected: boolean): string {
return selected ? 'true' : 'false';
}
diff --git a/chromium/chrome/browser/resources/history/router.ts b/chromium/chrome/browser/resources/history/router.ts
index 1c0c7f5585c..3e35de715d0 100644
--- a/chromium/chrome/browser/resources/history/router.ts
+++ b/chromium/chrome/browser/resources/history/router.ts
@@ -16,7 +16,7 @@ import {getTemplate} from './router.html.js';
// these values for better type check when `loadTimeData` is no longer needed.
export const Page = {
HISTORY: 'history',
- HISTORY_CLUSTERS: loadTimeData.getBoolean('renameJourneys') ? '2' :
+ HISTORY_CLUSTERS: loadTimeData.getBoolean('renameJourneys') ? 'grouped' :
'journeys',
SYNCED_TABS: 'syncedTabs',
};
diff --git a/chromium/chrome/browser/resources/identity_scope_approval_dialog/OWNERS b/chromium/chrome/browser/resources/identity_scope_approval_dialog/OWNERS
deleted file mode 100644
index fd467bd1965..00000000000
--- a/chromium/chrome/browser/resources/identity_scope_approval_dialog/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-msarda@chromium.org
diff --git a/chromium/chrome/browser/resources/identity_scope_approval_dialog/background.js b/chromium/chrome/browser/resources/identity_scope_approval_dialog/background.js
deleted file mode 100644
index bed7b27ba3a..00000000000
--- a/chromium/chrome/browser/resources/identity_scope_approval_dialog/background.js
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * Displays a webview based authorization dialog.
- * @param {string} key A unique identifier that the caller can use to locate
- * the dialog window.
- * @param {string} url A URL that will be loaded in the webview.
- * @param {string} mode 'interactive' or 'silent'. The window will be displayed
- * if the mode is 'interactive'.
- * @param {string} partition A name used for the webview partition.
- */
-function showAuthDialog(key, url, mode, partition) {
- const options =
- {frame: 'none', id: key, minWidth: 1024, minHeight: 768, hidden: true};
- chrome.app.window.create(
- 'scope_approval_dialog.html', options, function(win) {
- win.contentWindow.addEventListener('load', function(event) {
- let windowParam;
- if (mode === 'interactive') {
- windowParam = win;
- }
- win.contentWindow.loadAuthUrlAndShowWindow(
- url, windowParam, partition);
- });
- });
-}
-
-chrome.identityPrivate.onWebFlowRequest.addListener(showAuthDialog);
diff --git a/chromium/chrome/browser/resources/identity_scope_approval_dialog/inject.js b/chromium/chrome/browser/resources/identity_scope_approval_dialog/inject.js
deleted file mode 100644
index c3531c49422..00000000000
--- a/chromium/chrome/browser/resources/identity_scope_approval_dialog/inject.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-const injectCode =
- `const identityAppExtensionId = 'ahjaciijnoiaklcomgnblndopackapon';
-if (!window.OAuthConsent) {
- window.OAuthConsent = {};
-}
-if (!window.OAuthConsent.setConsentResult) {
- window.OAuthConsent.setConsentResult = function(result) {
- chrome.runtime.sendMessage(identityAppExtensionId, {consentResult: result});
- };
-}`;
-
-const script = document.createElement('script');
-script.innerText = injectCode;
-document.documentElement.prepend(script);
diff --git a/chromium/chrome/browser/resources/identity_scope_approval_dialog/manifest.json b/chromium/chrome/browser/resources/identity_scope_approval_dialog/manifest.json
deleted file mode 100644
index 89cb20dd27d..00000000000
--- a/chromium/chrome/browser/resources/identity_scope_approval_dialog/manifest.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- // chrome-extension://ahjaciijnoiaklcomgnblndopackapon
- "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNyyvaNmqNZsjBwes4YNlrsy64asdP710pdMUM27jtvOe2YkXUdvglcC6r2ihlvPg16mjYK+ZmvxchcEu497KUPqBq34jXILabiUuXLrQJlvl3A7QMLatuZlijSx1qXL/5w5/ggF2Tblo9SHSVtlVyhwyyGkT9ckga5erBUbbwkQIDAQAB",
- "name": "Identity API Scope Approval UI",
- "version": "1.1",
- "manifest_version": 2,
- "description": "Displays scope approval dialog boxes for the Identity API",
-
- "permissions": [
- "chrome://theme/",
- "identityPrivate",
- "resourcesPrivate",
- "webview"
- ],
-
- "app": {
- "background": {
- "scripts": [
- "background.js"
- ]
- },
- "content_security_policy": "default-src 'none'; script-src 'self' blob: filesystem:; style-src 'self' blob: filesystem:; img-src chrome://theme; object-src 'self' blob: filesystem:"
- },
- "display_in_launcher": false,
- "display_in_new_tab_page": false,
- "externally_connectable": {
- "matches" : ["https://accounts.google.com/*"]
- }
-}
diff --git a/chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.css b/chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.css
deleted file mode 100644
index a540587f419..00000000000
--- a/chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.css
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Copyright 2013 The Chromium Authors
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-body {
- margin: 0;
- padding: 0;
-}
-
-.titlebar {
- -webkit-app-region: drag;
- background-color: white;
- height: 26px;
- white-space: nowrap;
- width: 100%;
-}
-
-.titlebar-border {
- border-bottom: 1px solid #e5e5e5;
-}
-
-.titlebar-close-button {
- background-image: -webkit-image-set(
- url(chrome://theme/IDR_CLOSE_DIALOG) 1x,
- url(chrome://theme/IDR_CLOSE_DIALOG@2x) 2x);
- -webkit-app-region: no-drag;
- height: 14px;
- margin: 6px;
- position: absolute;
- right: 0;
- width: 14px;
-}
-
-.titlebar-close-button:active {
- background-image: -webkit-image-set(
- url(chrome://theme/IDR_CLOSE_DIALOG_P) 1x,
- url(chrome://theme/IDR_CLOSE_DIALOG_P@2x) 2x);
-}
-
-.titlebar-close-button:hover {
- background-image: -webkit-image-set(
- url(chrome://theme/IDR_CLOSE_DIALOG_H) 1x,
- url(chrome://theme/IDR_CLOSE_DIALOG_H@2x) 2x);
-}
-
-.content {
- height: auto;
- width: 100%;
-}
diff --git a/chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.html b/chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.html
deleted file mode 100644
index a5946ae4dbf..00000000000
--- a/chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<html>
- <head>
- <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
- <link rel="stylesheet" type="text/css" href="scope_approval_dialog.css">
- <script src="scope_approval_dialog.js"></script>
- </head>
- <body>
- <div class="titlebar">
- <span class="titlebar-close-button"></span>
- </div>
- <webview class="content" id="providerview"></webview>
- </body>
-</html>
diff --git a/chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.js b/chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.js
deleted file mode 100644
index f6da33e2460..00000000000
--- a/chromium/chrome/browser/resources/identity_scope_approval_dialog/scope_approval_dialog.js
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-let webview;
-let windowId;
-
-/**
- * Points the webview to the starting URL of a scope authorization
- * flow, and unhides the dialog once the page has loaded.
- * @param {string} url The url of the authorization entry point.
- * @param {Object} win The dialog window that contains this page. Can
- * be left undefined if the caller does not want to display the
- * window.
- * @param {string} partition The partition name used for the webview.
- */
-function loadAuthUrlAndShowWindow(url, win, partition) {
- // Send popups from the webview to a normal browser window.
- webview.addEventListener('newwindow', function(e) {
- e.window.discard();
- window.open(e.targetUrl);
- });
-
- webview.addContentScripts([{
- name: 'injectRule',
- matches: ['https://accounts.google.com/*'],
- js: {files: ['inject.js']},
- run_at: 'document_start',
- }]);
-
- // Request a customized view from GAIA.
- webview.request.onBeforeSendHeaders.addListener(
- function(details) {
- headers = details.requestHeaders || [];
- headers.push({'name': 'X-Browser-View', 'value': 'embedded'});
- return {requestHeaders: headers};
- },
- {
- urls: ['https://accounts.google.com/*'],
- },
- ['blocking', 'requestHeaders']);
-
- if (!url.toLowerCase().startsWith('https://accounts.google.com/')) {
- document.querySelector('.titlebar').classList.add('titlebar-border');
- }
-
- webview.partition = partition;
- webview.src = url;
- let windowShown = false;
- webview.addEventListener('loadstop', function() {
- if (win && !windowShown) {
- win.show();
- windowId = win.id;
- windowShown = true;
- }
- });
-}
-
-chrome.runtime.onMessageExternal.addListener(function(
- message, sender, sendResponse) {
- chrome.identityPrivate.setConsentResult(message.consentResult, windowId);
-});
-
-document.addEventListener('DOMContentLoaded', function() {
- webview = document.querySelector('webview');
-
- document.querySelector('.titlebar-close-button').onclick = function() {
- window.close();
- };
-
- chrome.resourcesPrivate.getStrings('identity', function(strings) {
- document.title = strings['window-title'];
- });
-});
diff --git a/chromium/chrome/browser/resources/inline_login/welcome_page_app.ts b/chromium/chrome/browser/resources/inline_login/welcome_page_app.ts
index 426caba13df..e856cb871c8 100644
--- a/chromium/chrome/browser/resources/inline_login/welcome_page_app.ts
+++ b/chromium/chrome/browser/resources/inline_login/welcome_page_app.ts
@@ -102,21 +102,52 @@ export class WelcomePageAppElement extends PolymerElement {
this.shadowRoot!.querySelector('#newPersonLink')]
.filter(link => !!link)
.forEach(link => {
+ const handleClick = () =>
+ this.dispatchEvent(new CustomEvent('opened-new-window'));
+ link!.addEventListener('click', handleClick as EventListener);
link!.addEventListener(
- 'click',
- () => this.dispatchEvent(new CustomEvent('opened-new-window')));
+ 'auxclick',
+ // For middle-click, do the same things as Ctrl+click
+ ((event: MouseEvent) => {
+ if (event.button === 1) {
+ handleClick();
+ }
+ }) as EventListener);
});
if (this.isArcAccountRestrictionsEnabled_) {
const guestModeLink = this.shadowRoot!.querySelector('#guestModeLink');
if (guestModeLink) {
- guestModeLink.addEventListener('click', () => this.openGuestLink_());
+ const handleClick = (event: MouseEvent) => {
+ event.preventDefault();
+ this.openGuestLink_();
+ };
+ guestModeLink.addEventListener('click', handleClick as EventListener);
+ guestModeLink.addEventListener(
+ 'auxclick',
+ // For middle-click, do the same things as Ctrl+click
+ ((event: MouseEvent) => {
+ if (event.button === 1) {
+ handleClick(event);
+ }
+ }) as EventListener);
}
} else {
const incognitoLink = this.shadowRoot!.querySelector('#incognitoLink');
if (incognitoLink) {
+ const handleClick = (event: MouseEvent) => {
+ event.preventDefault();
+ this.openIncognitoLink_();
+ };
+ incognitoLink.addEventListener('click', handleClick as EventListener);
incognitoLink.addEventListener(
- 'click', () => this.openIncognitoLink_());
+ 'auxclick',
+ // For middle-click, do the same things as Ctrl+click
+ ((event: MouseEvent) => {
+ if (event.button === 1) {
+ handleClick(event);
+ }
+ }) as EventListener);
}
}
}
diff --git a/chromium/chrome/browser/resources/inspect/inspect.css b/chromium/chrome/browser/resources/inspect/inspect.css
index 54082e819c0..0d397218ffe 100644
--- a/chromium/chrome/browser/resources/inspect/inspect.css
+++ b/chromium/chrome/browser/resources/inspect/inspect.css
@@ -52,7 +52,7 @@ img {
#navigation {
flex-shrink: 0;
padding-top: 20px;
- width: 150px;
+ width: 200px;
}
#content {
diff --git a/chromium/chrome/browser/resources/inspect/inspect.html b/chromium/chrome/browser/resources/inspect/inspect.html
index a1a0f3e4b82..496b93a4d27 100644
--- a/chromium/chrome/browser/resources/inspect/inspect.html
+++ b/chromium/chrome/browser/resources/inspect/inspect.html
@@ -75,6 +75,10 @@ found in the LICENSE file.
<div class="content-header">Service workers</div>
<div id="service-workers-list" class="list"></div>
</div>
+ <div id="shared-storage-worklets">
+ <div class="content-header">Shared storage worklets</div>
+ <div id="shared-storage-worklets-list" class="list"></div>
+ </div>
<div id="native-ui" hidden>
<div class="content-header">Native UI</div>
<div class="settings-bar">
diff --git a/chromium/chrome/browser/resources/inspect/inspect.js b/chromium/chrome/browser/resources/inspect/inspect.js
index 20dad82541b..7dba0690bcf 100644
--- a/chromium/chrome/browser/resources/inspect/inspect.js
+++ b/chromium/chrome/browser/resources/inspect/inspect.js
@@ -168,6 +168,7 @@ function populateLocalTargets(data) {
removeChildren('apps-list');
removeChildren('workers-list');
removeChildren('service-workers-list');
+ removeChildren('shared-storage-worklets-list');
removeChildren('others-list');
data.sort((a, b) => a.name.localeCompare(b.name));
@@ -183,6 +184,8 @@ function populateLocalTargets(data) {
addToWorkersList(data[i]);
} else if (data[i].type === 'service_worker') {
addToServiceWorkersList(data[i]);
+ } else if (data[i].type === 'shared_storage_worklet') {
+ addToSharedStorageWorkletsList(data[i]);
} else {
addToOthersList(data[i]);
}
@@ -523,6 +526,12 @@ function addToServiceWorkersList(data) {
row, 'terminate', sendTargetCommand.bind(null, 'close', data), false);
}
+function addToSharedStorageWorkletsList(data) {
+ const row = addTargetToList(
+ data, $('shared-storage-worklets-list'), ['name', 'description', 'url']);
+ // TODO(yaoxia): add the "terminate" link when the backend supports it
+}
+
function addToOthersList(data) {
addTargetToList(data, $('others-list'), ['url']);
}
diff --git a/chromium/chrome/browser/resources/intro/BUILD.gn b/chromium/chrome/browser/resources/intro/BUILD.gn
index bd7311854f4..26b4361bc7e 100644
--- a/chromium/chrome/browser/resources/intro/BUILD.gn
+++ b/chromium/chrome/browser/resources/intro/BUILD.gn
@@ -11,7 +11,12 @@ assert(is_chromeos_lacros || enable_dice_support)
build_webui("build") {
grd_prefix = "intro"
- static_files = [ "intro.html" ]
+ static_files = [
+ "default_browser/default_browser.html",
+ "intro.html",
+ "images/default_browser_frame.svg",
+ "images/default_browser_frame_dark.svg",
+ ]
if (enable_dice_support) {
static_files += [
@@ -23,6 +28,7 @@ build_webui("build") {
static_files += [ "images/security.svg" ]
}
web_component_files = [
+ "default_browser/app.ts",
"dice_app.ts",
"sign_in_promo.ts",
]
@@ -33,7 +39,10 @@ build_webui("build") {
web_component_files = [ "lacros_app.ts" ]
}
- non_web_component_files = [ "browser_proxy.ts" ]
+ non_web_component_files = [
+ "default_browser/browser_proxy.ts",
+ "browser_proxy.ts",
+ ]
ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
ts_composite = true
diff --git a/chromium/chrome/browser/resources/intro/default_browser/OWNERS b/chromium/chrome/browser/resources/intro/default_browser/OWNERS
new file mode 100644
index 00000000000..f1561d58595
--- /dev/null
+++ b/chromium/chrome/browser/resources/intro/default_browser/OWNERS
@@ -0,0 +1 @@
+gabolvr@google.com
diff --git a/chromium/chrome/browser/resources/intro/default_browser/app.html b/chromium/chrome/browser/resources/intro/default_browser/app.html
new file mode 100644
index 00000000000..df3ee840a00
--- /dev/null
+++ b/chromium/chrome/browser/resources/intro/default_browser/app.html
@@ -0,0 +1,154 @@
+<style include="tangible-sync-style-shared">
+ :host {
+ --cr-button-height: 36px;
+ --btn-container-height:
+ calc(2 * var(--btn-margin) + var(--cr-button-height));
+ --btn-margin: 24px;
+ --default-browser-frame-image-url: url(images/default_browser_frame.svg);
+ --left-background-image-url: url(images/left_illustration.svg);
+ --right-background-image-url: url(images/right_illustration.svg);
+ color: var(--cr-primary-text-color);
+ }
+
+ @media (prefers-color-scheme: dark) {
+ :host {
+ --default-browser-frame-image-url:
+ url(images/default_browser_frame_dark.svg);
+ --left-background-image-url: url(images/left_illustration_dark.svg);
+ --right-background-image-url: url(images/right_illustration_dark.svg);
+ }
+ }
+
+ .title {
+ margin: 36px 0 12px;
+ }
+
+ .subtitle {
+ margin: 0;
+ }
+
+ .tangible-sync-style-left-banner {
+ content: var(--left-background-image-url);
+ }
+
+ .tangible-sync-style-right-banner {
+ content: var(--right-background-image-url);
+ }
+
+ .tangible-sync-style-left-banner,
+ .tangible-sync-style-right-banner {
+ position: fixed;
+ }
+
+ #safe-zone {
+ box-sizing: border-box;
+ display: flex;
+ height: calc(100% - var(--btn-container-height));
+ justify-content: center;
+ overflow-y: auto;
+ position: fixed;
+ width: 100vw;
+ }
+
+ #content-area {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ margin: auto;
+ text-align: center;
+ }
+
+ #illustration-container {
+ --illustration-height: 110px;
+ --illustration-margin-top: 50px;
+ --illustration-width: 185px;
+ --product-logo-size: 52px;
+ --product-logo-top: 26px;
+ height: var(--illustration-height);
+ margin-top: var(--illustration-margin-top);
+ position: relative;
+ width: var(--illustration-width);
+ }
+
+ @media screen and ((max-width: 780px) or (max-height: 600px)) {
+ #illustration-container {
+ --illustration-height: 73px;
+ --illustration-width: 123px;
+ --product-logo-size: 34px;
+ --product-logo-top: 17px;
+ }
+ }
+
+ #default-browser-frame {
+ content: var(--default-browser-frame-image-url);
+ height: var(--illustration-height);
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: var(--illustration-width);
+ }
+
+ #product-logo {
+ height: var(--product-logo-size);
+ position: absolute;
+ top: var(--product-logo-top);
+ left: calc((var(--illustration-width) - var(--product-logo-size)) / 2);
+ width: var(--product-logo-size);
+ }
+
+ #button-row {
+ bottom: 0;
+ display: flex;
+ height: var(--btn-container-height);
+ justify-content: flex-end;
+ position: fixed;
+ width: 100vw;
+ }
+
+ #button-container {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ justify-content: flex-end;
+ padding: 0 var(--btn-margin);
+ }
+
+ <if expr="is_win">
+ #button-container {
+ flex-flow: row-reverse;
+ }
+ </if>
+
+ #button-container > cr-button {
+ margin-top: var(--btn-margin);
+ }
+</style>
+
+<img class="tangible-sync-style-left-banner" alt="">
+<img class="tangible-sync-style-right-banner" alt="">
+
+<div id="safe-zone" class="tangible-sync-style">
+ <div id="content-area">
+ <div id="illustration-container" role="img"
+ aria-label="$i18n{defaultBrowserIllustrationAltText}">
+ <img id="default-browser-frame" alt="">
+ <img id="product-logo" src="images/product-logo.svg" alt="">
+ </div>
+ <h1 class="title">$i18n{defaultBrowserTitle}</h1>
+ <p class="subtitle">$i18n{defaultBrowserSubtitle}</p>
+ </div>
+</div>
+
+<div id="button-row" class="tangible-sync-style">
+ <div id="button-container">
+ <cr-button id="skip-button" on-click="onSkipDefaultBrowserClick_"
+ disabled="[[areButtonsDisabled_(anyButtonClicked_)]]">
+ $i18n{defaultBrowserSkip}
+ </cr-button>
+ <cr-button id="confirmButton" class="action-button"
+ on-click="onConfirmDefaultBrowserClick_"
+ disabled="[[areButtonsDisabled_(anyButtonClicked_)]]">
+ $i18n{defaultBrowserSetAsDefault}
+ </cr-button>
+ </div>
+</div>
diff --git a/chromium/chrome/browser/resources/intro/default_browser/app.ts b/chromium/chrome/browser/resources/intro/default_browser/app.ts
new file mode 100644
index 00000000000..da2187a653f
--- /dev/null
+++ b/chromium/chrome/browser/resources/intro/default_browser/app.ts
@@ -0,0 +1,73 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://intro/tangible_sync_style_shared.css.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+
+import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './app.html.js';
+import {DefaultBrowserBrowserProxy, DefaultBrowserBrowserProxyImpl} from './browser_proxy.js';
+
+const DefaultBrowserAppElementBase = WebUiListenerMixin(PolymerElement);
+
+export class DefaultBrowserAppElement extends DefaultBrowserAppElementBase {
+ static get is() {
+ return 'default-browser-app';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ anyButtonClicked_: {
+ type: Boolean,
+ value: false,
+ },
+ };
+ }
+
+ private anyButtonClicked_: boolean;
+ private browserProxy_: DefaultBrowserBrowserProxy =
+ DefaultBrowserBrowserProxyImpl.getInstance();
+
+ override connectedCallback() {
+ super.connectedCallback();
+ this.addWebUiListener(
+ 'reset-default-browser-buttons', this.resetButtons_.bind(this));
+ }
+
+ private onConfirmDefaultBrowserClick_() {
+ this.anyButtonClicked_ = true;
+ this.browserProxy_.setAsDefaultBrowser();
+ }
+
+ private onSkipDefaultBrowserClick_() {
+ this.anyButtonClicked_ = true;
+ this.browserProxy_.skipDefaultBrowser();
+ }
+
+ private resetButtons_() {
+ this.anyButtonClicked_ = false;
+ }
+
+ /**
+ * Disable buttons if a button was clicked.
+ */
+ private areButtonsDisabled_() {
+ return this.anyButtonClicked_;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'default-browser-app': DefaultBrowserAppElement;
+ }
+}
+
+customElements.define(DefaultBrowserAppElement.is, DefaultBrowserAppElement);
diff --git a/chromium/chrome/browser/resources/intro/default_browser/browser_proxy.ts b/chromium/chrome/browser/resources/intro/default_browser/browser_proxy.ts
new file mode 100644
index 00000000000..47b349580ee
--- /dev/null
+++ b/chromium/chrome/browser/resources/intro/default_browser/browser_proxy.ts
@@ -0,0 +1,38 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A helper object used by the chrome://intro/default-browser page
+ * to interact with the browser.
+ */
+
+export interface DefaultBrowserBrowserProxy {
+ // Called when the user clicks on the "Set as default" button.
+ setAsDefaultBrowser(): void;
+
+ // Called when the user clicks on the "Skip" button.
+ skipDefaultBrowser(): void;
+}
+
+export class DefaultBrowserBrowserProxyImpl implements
+ DefaultBrowserBrowserProxy {
+ setAsDefaultBrowser() {
+ chrome.send('setAsDefaultBrowser');
+ }
+
+
+ skipDefaultBrowser() {
+ chrome.send('skipDefaultBrowser');
+ }
+
+ static getInstance(): DefaultBrowserBrowserProxy {
+ return instance || (instance = new DefaultBrowserBrowserProxyImpl());
+ }
+
+ static setInstance(obj: DefaultBrowserBrowserProxy) {
+ instance = obj;
+ }
+}
+
+let instance: DefaultBrowserBrowserProxy|null = null;
diff --git a/chromium/chrome/browser/resources/waffle/waffle.html b/chromium/chrome/browser/resources/intro/default_browser/default_browser.html
index 612d29823d3..36080413f31 100644
--- a/chromium/chrome/browser/resources/waffle/waffle.html
+++ b/chromium/chrome/browser/resources/intro/default_browser/default_browser.html
@@ -3,10 +3,12 @@
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="chrome://resources/css/md_colors.css">
- <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
+ <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<style>
body {
+ height: 100vh;
margin: 0;
+ width: 100vw;
}
@media (prefers-color-scheme: dark) {
body {
@@ -17,7 +19,7 @@
</style>
</head>
<body>
- <waffle-app></waffle-app>
- <script type="module" src="app.js"></script>
+ <default-browser-app></default-browser-app>
+ <script type="module" src="/default_browser/app.js"></script>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/intro/images/default_browser_frame.svg b/chromium/chrome/browser/resources/intro/images/default_browser_frame.svg
new file mode 100644
index 00000000000..e3690a8b0b7
--- /dev/null
+++ b/chromium/chrome/browser/resources/intro/images/default_browser_frame.svg
@@ -0,0 +1 @@
+<svg width="185" height="110" viewBox="0 0 185 110" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 106.313C0 108.349 5.153 110 11.51 110h161.98c6.357 0 11.51-1.651 11.51-3.687v-1.041H0v1.041Zm162.039-4.362H22.961a4.759 4.759 0 0 1-3.357-1.387 4.738 4.738 0 0 1-1.39-3.349V4.736a4.726 4.726 0 0 1 1.39-3.349A4.75 4.75 0 0 1 22.96 0h139.078a4.757 4.757 0 0 1 3.358 1.387 4.737 4.737 0 0 1 1.39 3.349v92.479a4.736 4.736 0 0 1-4.748 4.736Zm-.38-95.512H23.341v89.073h138.318V6.44Z" fill="#E8EAED"/></svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/intro/images/default_browser_frame_dark.svg b/chromium/chrome/browser/resources/intro/images/default_browser_frame_dark.svg
new file mode 100644
index 00000000000..a40f6b88f08
--- /dev/null
+++ b/chromium/chrome/browser/resources/intro/images/default_browser_frame_dark.svg
@@ -0,0 +1 @@
+<svg width="185" height="110" viewBox="0 0 185 110" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 106.313C0 108.349 5.153 110 11.51 110h161.98c6.357 0 11.51-1.651 11.51-3.687v-1.041H0v1.041Zm162.039-4.362H22.961a4.759 4.759 0 0 1-3.357-1.387 4.738 4.738 0 0 1-1.39-3.349V4.736a4.726 4.726 0 0 1 1.39-3.349A4.75 4.75 0 0 1 22.96 0h139.078a4.757 4.757 0 0 1 3.358 1.387 4.737 4.737 0 0 1 1.39 3.349v92.479a4.736 4.736 0 0 1-4.748 4.736Zm-.38-95.512H23.341v89.073h138.318V6.44Z" fill="#484C50"/><path fill="#FAFAFA" d="M23 6.592h138.972v89.193H23z"/></svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/invalidations/about_invalidations.js b/chromium/chrome/browser/resources/invalidations/about_invalidations.js
index 01a9ecd0534..30e3173664d 100644
--- a/chromium/chrome/browser/resources/invalidations/about_invalidations.js
+++ b/chromium/chrome/browser/resources/invalidations/about_invalidations.js
@@ -5,7 +5,6 @@
import 'chrome://resources/js/jstemplate_compiled.js';
import {addWebUiListener} from 'chrome://resources/js/cr.js';
import {$} from 'chrome://resources/js/util_ts.js';
-import {loadTestModule} from './test_loader_util.js';
/**
* Local variable where we maintain a count of the invalidations received
@@ -194,7 +193,7 @@ function updateDetailedStatus(newDetails) {
* Function that notifies the InvalidationsMessageHandler that the UI is
* ready to receive real-time notifications.
*/
-async function onLoadWork() {
+function onLoadWork() {
addWebUiListener('handlers-updated', handlers => updateHandlers(handlers));
addWebUiListener(
'state-updated',
@@ -211,9 +210,12 @@ async function onLoadWork() {
chrome.send('requestDetailedStatus');
};
- if (await loadTestModule()) {
+ const params = new URLSearchParams(window.location.search);
+ const isTest = params.has('isTest');
+ if (isTest) {
return;
}
+
chrome.send('doneLoading');
}
diff --git a/chromium/chrome/browser/resources/location_internals/BUILD.gn b/chromium/chrome/browser/resources/location_internals/BUILD.gn
index a853d6e1703..259b1355854 100644
--- a/chromium/chrome/browser/resources/location_internals/BUILD.gn
+++ b/chromium/chrome/browser/resources/location_internals/BUILD.gn
@@ -13,11 +13,28 @@ build_webui("build") {
non_web_component_files = [ "location_internals.ts" ]
- web_component_files = [ "diagnose_info_table.ts" ]
+ web_component_files = [
+ "diagnose_info_table.ts",
+ "diagnose_info_view.ts",
+ ]
- ts_deps = [ "//ui/webui/resources/js:build_ts" ]
+ ts_deps = [
+ "//ui/webui/resources/js:build_ts",
+ "//ui/webui/resources/mojo:build_ts",
+ ]
ts_composite = true
html_to_wrapper_template = "native"
+
+ mojo_files_deps = [
+ "//chrome/browser/ui/webui/location_internals:mojo_bindings_ts__generator",
+ "//services/device/public/mojom:geolocation_internals_ts__generator",
+ "//services/device/public/mojom:geoposition_ts__generator",
+ ]
+ mojo_files = [
+ "$root_gen_dir/chrome/browser/ui/webui/location_internals/location_internals.mojom-webui.ts",
+ "$root_gen_dir/services/device/public/mojom/geolocation_internals.mojom-webui.ts",
+ "$root_gen_dir/services/device/public/mojom/geoposition.mojom-webui.ts",
+ ]
}
diff --git a/chromium/chrome/browser/resources/location_internals/diagnose_info_table.html b/chromium/chrome/browser/resources/location_internals/diagnose_info_table.html
index 77de623dc0e..7b15ac19187 100644
--- a/chromium/chrome/browser/resources/location_internals/diagnose_info_table.html
+++ b/chromium/chrome/browser/resources/location_internals/diagnose_info_table.html
@@ -10,10 +10,17 @@ found in the LICENSE file.
margin-top: 20px;
}
- table caption {
+ #table-title {
font-size: 18px;
font-weight: bold;
text-align: start;
+ white-space: nowrap;
+ }
+
+ #table-footer {
+ caption-side: bottom;
+ text-align: start;
+ white-space: nowrap;
}
td,
@@ -24,7 +31,8 @@ found in the LICENSE file.
}
</style>
<table>
- <caption></caption>
+ <caption id="table-title"></caption>
<thead></thead>
<tbody></tbody>
+ <caption id="table-footer"></caption>
</table>
diff --git a/chromium/chrome/browser/resources/location_internals/diagnose_info_table.ts b/chromium/chrome/browser/resources/location_internals/diagnose_info_table.ts
index f3c811e7254..7b7d0875f10 100644
--- a/chromium/chrome/browser/resources/location_internals/diagnose_info_table.ts
+++ b/chromium/chrome/browser/resources/location_internals/diagnose_info_table.ts
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import {assert} from '//resources/js/assert_ts.js';
import {CustomElement} from '//resources/js/custom_element.js';
+import {getTrustedHTML} from '//resources/js/static_types.js';
import {getTemplate} from './diagnose_info_table.html.js';
@@ -16,40 +16,77 @@ export class DiagnoseInfoTableElement extends CustomElement {
return getTemplate();
}
- private tableCaption_: HTMLElement;
+ private tableTitle_: HTMLElement;
private tableHead_: HTMLElement;
private tableBody_: HTMLElement;
+ private tableFooter_: HTMLElement;
+ private lastTableEntries_: Array<Record<string, string>>;
constructor() {
super();
+ this.tableTitle_ =
+ this.getRequiredElement<HTMLElement>('caption#table-title');
this.tableHead_ = this.getRequiredElement<HTMLElement>('thead');
this.tableBody_ = this.getRequiredElement<HTMLElement>('tbody');
- this.tableCaption_ = this.getRequiredElement<HTMLElement>('caption');
+ this.tableFooter_ =
+ this.getRequiredElement<HTMLElement>('caption#table-footer');
+ this.style.display = 'none';
+ this.lastTableEntries_ = [];
}
- createTableData(input: Array<Record<string, string>>) {
- assert(input.length > 0);
+ hideTable() {
+ this.style.display = 'none';
+ this.tableTitle_.textContent = '';
+ this.tableHead_.innerHTML = getTrustedHTML``;
+ this.tableBody_.innerHTML = getTrustedHTML``;
+ this.tableFooter_.textContent = '';
+ }
+
+ visible(): boolean {
+ return !(this.style.display === 'none');
+ }
+
+ updateTable(
+ tableName: string, entries: Array<Record<string, string>>,
+ footer: string|undefined = undefined) {
+ if (entries.length === 0) {
+ this.hideTable();
+ return;
+ }
+ this.lastTableEntries_ = entries;
+ this.style.display = 'block';
+ this.tableTitle_.textContent = tableName;
+ this.tableHead_.innerHTML = getTrustedHTML``;
+ this.tableBody_.innerHTML = getTrustedHTML``;
const tableHeadFirstRow = document.createElement('tr');
this.tableHead_.appendChild(tableHeadFirstRow);
- for (let i: number = 0; i < input.length; i++) {
- const object = input[i];
+ for (let i: number = 0; i < entries.length; i++) {
+ const entry = entries[i];
const tableBodyRow = document.createElement('tr');
- for (const name in object) {
+ for (const fieldName in entry) {
if (i === 0) {
const nameCell = document.createElement('th');
- nameCell.textContent = name;
+ nameCell.textContent = fieldName;
tableHeadFirstRow.appendChild(nameCell);
}
const valueCell = document.createElement('td');
- valueCell.textContent = object[name]!;
+ valueCell.textContent = entry[fieldName]!;
tableBodyRow.appendChild(valueCell);
}
this.tableBody_.appendChild(tableBodyRow);
}
+ if (footer === undefined) {
+ this.tableFooter_.textContent = '';
+ } else {
+ this.tableFooter_.textContent = footer;
+ }
}
- updateCaption(name: string) {
- this.tableCaption_.textContent = name;
+ outputTable(): Record<string, any> {
+ const table: Record<string, any> = {};
+ const name = this.tableTitle_.textContent;
+ table[name!] = this.lastTableEntries_;
+ return table;
}
}
diff --git a/chromium/chrome/browser/resources/location_internals/diagnose_info_view.html b/chromium/chrome/browser/resources/location_internals/diagnose_info_view.html
new file mode 100644
index 00000000000..25f43e977f0
--- /dev/null
+++ b/chromium/chrome/browser/resources/location_internals/diagnose_info_view.html
@@ -0,0 +1,19 @@
+<!--
+Copyright 2023 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<style>
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-around;
+ padding-inline-end: 20px;
+ padding-inline-start: 20px;
+</style>
+
+<diagnose-info-table id="watch-position-table"></diagnose-info-table>
+<diagnose-info-table id="provider-state-table"></diagnose-info-table>
+<diagnose-info-table id="wifi-data-table"></diagnose-info-table>
+<diagnose-info-table id="position-cache-table"></diagnose-info-table>
+<diagnose-info-table id="wifi-polling-policy-table"></diagnose-info-table>
diff --git a/chromium/chrome/browser/resources/location_internals/diagnose_info_view.ts b/chromium/chrome/browser/resources/location_internals/diagnose_info_view.ts
new file mode 100644
index 00000000000..15352eefb65
--- /dev/null
+++ b/chromium/chrome/browser/resources/location_internals/diagnose_info_view.ts
@@ -0,0 +1,297 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import './diagnose_info_table.js';
+
+import {CustomElement} from '//resources/js/custom_element.js';
+import {Time, TimeDelta} from '//resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
+
+import {DiagnoseInfoTableElement} from './diagnose_info_table.js';
+import {getTemplate} from './diagnose_info_view.html.js';
+import {GeolocationDiagnostics, INVALID_CHANNEL, INVALID_RADIO_SIGNAL_STRENGTH, INVALID_SIGNAL_TO_NOISE, NetworkLocationDiagnostics, PositionCacheDiagnostics, WifiPollingPolicyDiagnostics} from './geolocation_internals.mojom-webui.js';
+import {BAD_ACCURACY, BAD_ALTITUDE, BAD_HEADING, BAD_LATITUDE_LONGITUDE, BAD_SPEED, GeopositionResult} from './geoposition.mojom-webui.js';
+
+export const PROVIDER_STATE_TABLE_ID = 'provider-state-table';
+const PROVIDER_STATE_ENUM: {[key: number]: string} = {
+ 0: 'Stop',
+ 1: 'High Accuracy',
+ 2: 'Low Accuracy',
+ 3: 'Blocked By System Permission',
+};
+export const WATCH_TABLE_ID = 'watch-position-table';
+export const WIFI_DATA_TABLE_ID = 'wifi-data-table';
+export const POSITION_CACHE_TABLE_ID = 'position-cache-table';
+export const WIFI_POLLING_POLICY_TABLE_ID = 'wifi-polling-policy-table';
+
+// Converts `mojoTime` from `mojom_base.mojom.Time` to `Date`.
+function mojoTimeToDate(mojoTime: Time) {
+ // The Javascript `Date()` is based off of the number of milliseconds since
+ // the UNIX epoch (1970-01-01 00::00:00 UTC), while `internalValue``
+ // of the `base::Time` (represented in mojom.Time) represents the
+ // number of microseconds since the Windows FILETIME epoch
+ // (1601-01-01 00:00:00 UTC). This computes the final Javascript time by
+ // computing the epoch delta and the conversion from microseconds to
+ // milliseconds.
+ const windowsEpoch = Date.UTC(1601, 0, 1, 0, 0, 0, 0);
+ const unixEpoch = Date.UTC(1970, 0, 1, 0, 0, 0, 0);
+ // `epochDeltaInMs` is equal to `base::Time::kTimeTToMicrosecondsOffset`.
+ const epochDeltaInMs = unixEpoch - windowsEpoch;
+ const timeInMs = Number(mojoTime.internalValue) / 1000;
+ return new Date(timeInMs - epochDeltaInMs);
+}
+
+// Returns a string representation of `mojoTime`.
+function stringifyMojoTime(mojoTime?: Time) {
+ if (!mojoTime) {
+ return 'None';
+ }
+ return mojoTimeToDate(mojoTime).toLocaleString();
+}
+
+// Returns a string representation of `mojoGeopositionResult`.
+function stringifyMojoGeopositionResult(
+ mojoGeopositionResult?: GeopositionResult) {
+ if (!mojoGeopositionResult) {
+ return 'None';
+ }
+ if ('position' in mojoGeopositionResult) {
+ const mojoGeoposition = mojoGeopositionResult.position;
+ if (mojoGeoposition!.latitude === BAD_LATITUDE_LONGITUDE ||
+ mojoGeoposition!.longitude === BAD_LATITUDE_LONGITUDE) {
+ return 'Invalid geoposition';
+ }
+ const components = [];
+ let latLong =
+ `${mojoGeoposition!.latitude}°, ${mojoGeoposition!.longitude}°`;
+ if (mojoGeoposition!.accuracy !== BAD_ACCURACY) {
+ latLong += ` ±${mojoGeoposition!.accuracy} m`;
+ }
+ components.push(latLong);
+ if (mojoGeoposition!.altitude !== BAD_ALTITUDE) {
+ let altitude = `${mojoGeoposition!.altitude} m`;
+ if (mojoGeoposition!.altitudeAccuracy !== BAD_ACCURACY) {
+ altitude += ` ±${mojoGeoposition!.altitudeAccuracy} m`;
+ }
+ components.push(altitude);
+ }
+ if (mojoGeoposition!.heading !== BAD_HEADING) {
+ components.push(`${mojoGeoposition!.heading}°`);
+ }
+ if (mojoGeoposition!.speed !== BAD_SPEED) {
+ components.push(`${mojoGeoposition!.speed} m/s`);
+ }
+ components.push(stringifyMojoTime(mojoGeoposition!.timestamp));
+ return components.join('; ');
+ }
+ if ('error' in mojoGeopositionResult) {
+ const mojoGeopositionError = mojoGeopositionResult.error;
+ return `${mojoGeopositionError!.errorMessage} (${
+ mojoGeopositionError!.errorCode})`;
+ }
+ return 'Invalid result';
+}
+
+// Return a string representation of `TimeDelta` in second.
+function stringifyMojoTimeDelta(mojoTime: TimeDelta|undefined) {
+ if (!mojoTime) {
+ return 'None';
+ }
+ return `${Number(mojoTime.microseconds) / 1000000}`;
+}
+
+export class DiagnoseInfoViewElement extends CustomElement {
+ static get is() {
+ return 'diagnose-info-view';
+ }
+
+ static override get template() {
+ return getTemplate();
+ }
+
+ watchPositionSuccess = (position: GeolocationPosition) => {
+ const data: Record<string, string> = {};
+ data['timestamp'] = new Date(position.timestamp).toLocaleString();
+
+ for (const key in position.coords) {
+ const value = position.coords[key as keyof GeolocationCoordinates];
+ if (typeof value === 'number' || typeof value === 'string') {
+ data[key] = value.toString();
+ }
+ }
+ this.updateWatchPositionTable(data);
+ };
+
+ watchPositionError = (error: GeolocationPositionError) => {
+ const data: Record<string, string> = {};
+ data['timestamp'] = new Date().toLocaleString();
+ data['fail reason'] = `${error.message}, code: ${error.code}`;
+ this.updateWatchPositionTable(data);
+ };
+
+ private providerStateTable_: DiagnoseInfoTableElement;
+ private wifiDataTable_: DiagnoseInfoTableElement;
+ private positionCacheTable_: DiagnoseInfoTableElement;
+ private watchPositionTable_: DiagnoseInfoTableElement;
+ private wifiPollingPolicyTable_: DiagnoseInfoTableElement;
+
+ constructor() {
+ super();
+ this.providerStateTable_ =
+ this.getRequiredElement<DiagnoseInfoTableElement>(
+ `#${PROVIDER_STATE_TABLE_ID}`);
+ this.wifiDataTable_ = this.getRequiredElement<DiagnoseInfoTableElement>(
+ `#${WIFI_DATA_TABLE_ID}`);
+ this.positionCacheTable_ =
+ this.getRequiredElement<DiagnoseInfoTableElement>(
+ `#${POSITION_CACHE_TABLE_ID}`);
+ this.watchPositionTable_ =
+ this.getRequiredElement<DiagnoseInfoTableElement>(`#${WATCH_TABLE_ID}`);
+ this.wifiPollingPolicyTable_ =
+ this.getRequiredElement<DiagnoseInfoTableElement>(
+ `#${WIFI_POLLING_POLICY_TABLE_ID}`);
+ }
+
+ updateDiagnosticsTables(data: GeolocationDiagnostics) {
+ this.updateProviderState(data.providerState);
+ this.updateNetworkLocationDiagnostics(data.networkLocationDiagnostics);
+ this.updatePositionCacheDiagnostics(data.positionCacheDiagnostics);
+ this.updateWifiPollingPolicyTable(data.wifiPollingPolicyDiagnostics);
+ }
+
+ updateProviderState(providerState: number) {
+ let providerStateString = PROVIDER_STATE_ENUM[providerState];
+ if (providerStateString === undefined) {
+ providerStateString = 'Invalid state';
+ }
+ this.providerStateTable_.updateTable(
+ PROVIDER_STATE_TABLE_ID, [{'Provider State': providerStateString}]);
+ }
+
+ updateNetworkLocationDiagnostics(networkLocationDiagnostics?:
+ NetworkLocationDiagnostics) {
+ if (!networkLocationDiagnostics) {
+ this.wifiDataTable_.hideTable();
+ return;
+ }
+ const wifiData: Array<Record<string, string>> = [];
+ if (networkLocationDiagnostics.accessPointData !== null) {
+ for (const accessPointData of
+ networkLocationDiagnostics.accessPointData) {
+ const row: Record<string, string> = {};
+ row['MAC address'] = accessPointData.macAddress;
+ if (accessPointData.radioSignalStrength ===
+ INVALID_RADIO_SIGNAL_STRENGTH) {
+ row['Signal strength'] = 'N/A';
+ } else {
+ row['Signal strength'] = `${accessPointData.radioSignalStrength} dBm`;
+ }
+ if (accessPointData.channel === INVALID_CHANNEL) {
+ row['Channel'] = 'N/A';
+ } else {
+ row['Channel'] = accessPointData.channel.toString();
+ }
+ if (accessPointData.signalToNoise === INVALID_SIGNAL_TO_NOISE) {
+ row['Signal to Noise Ratio'] = 'N/A';
+ } else {
+ row['Signal to Noise Ratio'] = `${accessPointData.signalToNoise} dB`;
+ }
+ if (accessPointData.timestamp) {
+ row['Timestamp'] = stringifyMojoTime(accessPointData.timestamp);
+ } else {
+ row['Timestamp'] = 'N/A';
+ }
+ wifiData.push(row);
+ }
+ if (wifiData.length === 0) {
+ // TODO: Hide the table when there is no access point data.
+ const row: Record<string, string> = {};
+ row['MAC address'] = 'No access point data';
+ row['Signal strength'] = '';
+ row['Channel'] = '';
+ row['Signal to Noise Ratio'] = '';
+ row['Timestamp'] = '';
+ wifiData.push(row);
+ }
+ }
+ let footerMessage;
+ if (networkLocationDiagnostics.wifiTimestamp === null) {
+ footerMessage = 'No Wi-Fi data received';
+ } else {
+ footerMessage = `Wi-Fi data last received ${
+ stringifyMojoTime(networkLocationDiagnostics.wifiTimestamp)}`;
+ }
+ this.wifiDataTable_.updateTable(
+ WIFI_DATA_TABLE_ID, wifiData, footerMessage);
+ }
+
+ updatePositionCacheDiagnostics(positionCacheDiagnostics?:
+ PositionCacheDiagnostics) {
+ if (!positionCacheDiagnostics) {
+ this.positionCacheTable_.hideTable();
+ return;
+ }
+ const row: Record<string, string> = {};
+ row['Cache size'] = positionCacheDiagnostics.cacheSize.toString();
+ row['Last cache hit'] = stringifyMojoTime(positionCacheDiagnostics.lastHit);
+ row['Last cache miss'] =
+ stringifyMojoTime(positionCacheDiagnostics.lastMiss);
+ if (!positionCacheDiagnostics.hitRate) {
+ row['Cache hit rate'] = 'N/A';
+ } else {
+ row['Cache hit rate'] = `${positionCacheDiagnostics.hitRate * 100}%`;
+ }
+ row['Last result'] = stringifyMojoGeopositionResult(
+ positionCacheDiagnostics.lastNetworkResult);
+ this.positionCacheTable_.updateTable(POSITION_CACHE_TABLE_ID, [row]);
+ }
+
+ updateWatchPositionTable(data: Record<string, string>) {
+ const footerMessage = `Last updated ${new Date().toLocaleString()}`;
+ this.watchPositionTable_.updateTable(WATCH_TABLE_ID, [data], footerMessage);
+ }
+
+ updateWifiPollingPolicyTable(data?: WifiPollingPolicyDiagnostics) {
+ if (!data) {
+ this.wifiPollingPolicyTable_.hideTable();
+ return;
+ }
+ const row: Record<string, string> = {};
+ row['Interval start time'] = stringifyMojoTime(data.intervalStart);
+ row['Interval duration (sec)'] =
+ stringifyMojoTimeDelta(data.intervalDuration);
+ row['Polling interval (sec)'] =
+ stringifyMojoTimeDelta(data.pollingInterval);
+ row['Default interval (sec)'] =
+ stringifyMojoTimeDelta(data.defaultInterval);
+ row['No change interval (sec)'] =
+ stringifyMojoTimeDelta(data.noChangeInterval);
+ row['Two no change interval (sec)'] =
+ stringifyMojoTimeDelta(data.twoNoChangeInterval);
+ row['No Wi-Fi interval (sec)'] =
+ stringifyMojoTimeDelta(data.noWifiInterval);
+ this.wifiPollingPolicyTable_.updateTable(
+ WIFI_POLLING_POLICY_TABLE_ID, [row]);
+ }
+
+ outputTables(): Record<string, any> {
+ const tables = this.$all<DiagnoseInfoTableElement>('diagnose-info-table');
+ const output: Record<string, any> = {};
+ output['LocationInternals'] = [];
+ for (const table of tables) {
+ if (!table.visible()) {
+ continue;
+ }
+ output['LocationInternals'].push(table.outputTable());
+ }
+ return output;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'diagnose-info-view': DiagnoseInfoViewElement;
+ }
+}
+
+customElements.define(DiagnoseInfoViewElement.is, DiagnoseInfoViewElement);
diff --git a/chromium/chrome/browser/resources/location_internals/location_internals.css b/chromium/chrome/browser/resources/location_internals/location_internals.css
index 75c0b9f6c8b..c22dbf20055 100644
--- a/chromium/chrome/browser/resources/location_internals/location_internals.css
+++ b/chromium/chrome/browser/resources/location_internals/location_internals.css
@@ -2,10 +2,11 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
-#container {
- display: flex;
- flex-wrap: wrap;
- justify-content: space-around;
- padding-inline-end: 20px;
- padding-inline-start: 20px;
+#refresh-status {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 25vw;
+ font-size: 16px;
+ word-wrap: break-word;
}
diff --git a/chromium/chrome/browser/resources/location_internals/location_internals.html b/chromium/chrome/browser/resources/location_internals/location_internals.html
index e2004b0642a..89f746a1648 100644
--- a/chromium/chrome/browser/resources/location_internals/location_internals.html
+++ b/chromium/chrome/browser/resources/location_internals/location_internals.html
@@ -15,8 +15,10 @@ found in the LICENSE file.
<h2 class="header">Location Internals</h2>
<header class="page-header">
<button id="watch-btn">Start Watching Position</button>
+ <button id="log-btn">Download the location-internals dump</button>
+ <div id="refresh-status">Error fetching Geolocation API internals</div>
</header>
- <div id="container"></div>
+ <diagnose-info-view id="diagnose-info-view"></diagnose-info-view>
<script type="module" src="location_internals.js"></script>
</body>
</html>
diff --git a/chromium/chrome/browser/resources/location_internals/location_internals.ts b/chromium/chrome/browser/resources/location_internals/location_internals.ts
index 1dad9707a85..0718c2ddd0a 100644
--- a/chromium/chrome/browser/resources/location_internals/location_internals.ts
+++ b/chromium/chrome/browser/resources/location_internals/location_internals.ts
@@ -2,64 +2,104 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import './diagnose_info_table.js';
+import './diagnose_info_view.js';
-import {$, getRequiredElement} from 'chrome://resources/js/util_ts.js';
+import {getRequiredElement} from 'chrome://resources/js/util_ts.js';
-const WATCH_TABLE_ID = 'watch-position-table';
+import {DiagnoseInfoViewElement} from './diagnose_info_view.js';
+import {GeolocationDiagnostics, GeolocationInternalsObserverInterface, GeolocationInternalsObserverReceiver, GeolocationInternalsRemote} from './geolocation_internals.mojom-webui.js';
+import {LocationInternalsHandler} from './location_internals.mojom-webui.js';
-let watchId: number = -1;
-
-document.addEventListener('DOMContentLoaded', () => {
- const watchButton = getRequiredElement<HTMLElement>('watch-btn');
- watchButton.addEventListener('click', () => {
- if (watchId === -1) {
- watchId = navigator.geolocation.watchPosition(logSuccess, logError, {
- enableHighAccuracy: true,
- timeout: 5000,
- maximumAge: 0,
- });
- watchButton.textContent = 'Stop Watching Position';
- } else {
- navigator.geolocation.clearWatch(watchId);
- watchId = -1;
- watchButton.textContent = 'Start Watching Position';
- }
- });
-});
+export const WATCH_BUTTON_ID = 'watch-btn';
+export const LOG_BUTTON_ID = 'log-btn';
+export const REFRESH_STATUS_ID = 'refresh-status';
+export const REFRESH_STATUS_SUCCESS = 'Last updated ';
+export const REFRESH_STATUS_UNINITIALIZED =
+ `Geolocation API is not initialized. Click "Start Watching Position" to
+ begin.`;
+export const REFRESH_FINISH_EVENT = 'refresh-finish-event';
+export const DIAGNOSE_INFO_VIEW_ID = 'diagnose-info-view';
-function logSuccess(position: GeolocationPosition) {
- const data: Record<string, string> = {};
- data['timestamp'] = new Date(position.timestamp).toLocaleString();
+let watchId: number = -1;
+let geolocationInternals: GeolocationInternalsRemote|undefined;
+let geolocationInternalsObserver: GeolocationInternalsObserverReceiver|
+ undefined;
+const diagnoseInfoView =
+ getRequiredElement<DiagnoseInfoViewElement>(DIAGNOSE_INFO_VIEW_ID);
- for (const key in position.coords) {
- const value = position.coords[key as keyof GeolocationCoordinates];
- if (typeof value === 'number' || typeof value === 'string') {
- data[key] = value.toString();
- }
+class GeolocationInternalsObserver implements
+ GeolocationInternalsObserverInterface {
+ onDiagnosticsChanged(diagnostics: GeolocationDiagnostics) {
+ handleDiagnosticsChanged(diagnostics);
}
- updateTable(WATCH_TABLE_ID, [data]);
}
-function logError(error: GeolocationPositionError) {
- const data: Record<string, string> = {};
- data['timestamp'] = new Date().toLocaleString();
- data['fail reason'] = `${error.message}, code: ${error.code}`;
- updateTable(WATCH_TABLE_ID, [data]);
+// Initialize buttons callback
+function initializeButtons() {
+ const watchButton = getRequiredElement<HTMLElement>(WATCH_BUTTON_ID);
+ watchButton.addEventListener('click', watchPosition);
+ const saveButton = getRequiredElement<HTMLElement>(LOG_BUTTON_ID);
+ saveButton.addEventListener('click', saveDiagnostics);
+}
+
+// Initialize Mojo pipe
+export function initializeMojo() {
+ geolocationInternals = new GeolocationInternalsRemote();
+ LocationInternalsHandler.getRemote().bindInternalsInterface(
+ geolocationInternals.$.bindNewPipeAndPassReceiver());
+
+ geolocationInternalsObserver = new GeolocationInternalsObserverReceiver(
+ new GeolocationInternalsObserver());
+ geolocationInternals!
+ .addInternalsObserver(
+ geolocationInternalsObserver.$.bindNewPipeAndPassRemote())
+ .then(data => {
+ handleDiagnosticsChanged(data.diagnostics);
+ });
}
-function updateTable(name: string, data: Array<Record<string, string>>) {
- removeTable(name);
- const newTableElement = document.createElement('diagnose-info-table');
- newTableElement.id = name;
- newTableElement.createTableData(data);
- newTableElement.updateCaption(name);
- getRequiredElement<HTMLElement>('container').appendChild(newTableElement);
+function watchPosition() {
+ const watchButton = getRequiredElement<HTMLElement>(WATCH_BUTTON_ID);
+ if (watchId === -1) {
+ watchId = navigator.geolocation.watchPosition(
+ diagnoseInfoView.watchPositionSuccess,
+ diagnoseInfoView.watchPositionError, {
+ enableHighAccuracy: true,
+ timeout: 5000,
+ maximumAge: 0,
+ });
+ watchButton.textContent = 'Stop Watching Position';
+ } else {
+ navigator.geolocation.clearWatch(watchId);
+ watchId = -1;
+ watchButton.textContent = 'Start Watching Position';
+ }
}
-function removeTable(name: string) {
- const oldTableElement = $(name);
- if (oldTableElement) {
- oldTableElement.remove();
+function handleDiagnosticsChanged(diagnostics: GeolocationDiagnostics|null) {
+ const refreshStatus = getRequiredElement(REFRESH_STATUS_ID);
+ if (diagnostics) {
+ refreshStatus.textContent =
+ REFRESH_STATUS_SUCCESS + new Date().toLocaleString();
+ diagnoseInfoView.updateDiagnosticsTables(diagnostics);
+ } else {
+ refreshStatus.textContent = REFRESH_STATUS_UNINITIALIZED;
}
+ window.dispatchEvent(new CustomEvent(REFRESH_FINISH_EVENT));
+}
+
+function saveDiagnostics() {
+ const tables = diagnoseInfoView.outputTables();
+ const content = JSON.stringify(tables, null, 2);
+ const blob = new Blob([content], {type: 'application/json'});
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `location_internals_${new Date().toISOString()}.json`;
+ a.click();
}
+
+document.addEventListener('DOMContentLoaded', () => {
+ initializeButtons();
+ initializeMojo();
+});
diff --git a/chromium/chrome/browser/resources/management/icons.html b/chromium/chrome/browser/resources/management/icons.html
index 441726e46f7..0ae424f6747 100644
--- a/chromium/chrome/browser/resources/management/icons.html
+++ b/chromium/chrome/browser/resources/management/icons.html
@@ -16,6 +16,7 @@
<g id="public"><path d="M0 0h24v24H0z" fill="none"></path><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"></path></g>
<g id="vpn-lock"><path d="M22,4V3.5C22,2.1,20.9,1,19.5,1C18.1,1,17,2.1,17,3.5V4c-0.6,0-1,0.4-1,1v4c0,0.6,0.4,1,1,1h5c0.6,0,1-0.4,1-1V5C23,4.4,22.6,4,22,4z M21.2,4h-3.4V3.5c0-0.9,0.8-1.7,1.7-1.7c0.9,0,1.7,0.8,1.7,1.7V4z M18.9,12c0,0.3,0.1,0.7,0.1,1c0,2.1-0.8,4-2.1,5.4c-0.3-0.8-1-1.4-1.9-1.4h-1v-3c0-0.6-0.4-1-1-1H7v-2h2c0.6,0,1-0.4,1-1V8h2c1.1,0,2-0.9,2-2V3.5C13.1,3.2,12,3,11,3C5.5,3,1,7.5,1,13c0,5.5,4.5,10,10,10c5.5,0,10-4.5,10-10c0-0.3,0-0.7-0.1-1H18.9z M10,20.9c-3.9-0.5-7-3.9-7-7.9c0-0.6,0.1-1.2,0.2-1.8L8,16v1c0,1.1,0.9,2,2,2V20.9z"></path></g>
<g id="usb"><path d="M15 7v4h1v2h-3V5h2l-3-4-3 4h2v8H8v-2.07c.7-.37 1.2-1.08 1.2-1.93 0-1.21-.99-2.2-2.2-2.2-1.21 0-2.2.99-2.2 2.2 0 .85.5 1.56 1.2 1.93V13c0 1.11.89 2 2 2h3v3.05c-.71.37-1.2 1.1-1.2 1.95 0 1.22.99 2.2 2.2 2.2 1.21 0 2.2-.98 2.2-2.2 0-.85-.49-1.58-1.2-1.95V15h3c1.11 0 2-.89 2-2v-2h1V7h-4z"></path></g>
+ <g id="legacy-tech"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"></path></g>
</defs>
</svg>
</iron-iconset-svg>
diff --git a/chromium/chrome/browser/resources/management/management_browser_proxy.ts b/chromium/chrome/browser/resources/management/management_browser_proxy.ts
index f053105bcbb..c5142e4c807 100644
--- a/chromium/chrome/browser/resources/management/management_browser_proxy.ts
+++ b/chromium/chrome/browser/resources/management/management_browser_proxy.ts
@@ -4,8 +4,15 @@
import {sendWithPromise} from 'chrome://resources/js/cr.js';
+export interface Application {
+ name: string;
+ icon?: string;
+ permissions: string[];
+}
+
export interface Extension {
name: string;
+ icon?: string;
permissions: string[];
}
@@ -15,6 +22,7 @@ export enum ReportingType {
USER = 'user',
USER_ACTIVITY = 'user-activity',
EXTENSIONS = 'extensions',
+ LEGACY_TECH = 'legacy-tech',
}
export interface BrowserReportingResponse {
@@ -23,8 +31,9 @@ export interface BrowserReportingResponse {
}
interface ManagedDataResponse {
+ applicationReportingSubtitle: string;
browserManagementNotice: string;
- extensionReportingTitle: string;
+ extensionReportingSubtitle: string;
managedWebsitesSubtitle: string;
pageSubtitle: string;
managed: boolean;
@@ -70,6 +79,7 @@ export enum DeviceReportingType {
LOGIN_LOGOUT = 'login-logout',
CRD_SESSIONS = 'crd sessions',
PERIPHERALS = 'peripherals',
+ LEGACY_TECH = 'legacy-tech',
}
@@ -85,6 +95,8 @@ export interface ManagementBrowserProxy {
getManagedWebsites(): Promise<string[]>;
+ getApplications(): Promise<Application[]>;
+
// <if expr="is_chromeos">
/**
* @return Whether trust root configured or not.
@@ -121,6 +133,10 @@ export class ManagementBrowserProxyImpl implements ManagementBrowserProxy {
return sendWithPromise('getManagedWebsites');
}
+ getApplications() {
+ return sendWithPromise('getApplications');
+ }
+
// <if expr="is_chromeos">
getLocalTrustRootsInfo() {
return sendWithPromise('getLocalTrustRootsInfo');
diff --git a/chromium/chrome/browser/resources/management/management_ui.html b/chromium/chrome/browser/resources/management/management_ui.html
index c8681554bd6..2558360bba1 100644
--- a/chromium/chrome/browser/resources/management/management_ui.html
+++ b/chromium/chrome/browser/resources/management/management_ui.html
@@ -163,11 +163,13 @@
}
.extension-name,
+ .application-name,
.protection-name {
width: 40%;
}
- .extension-name span {
+ .extension-name span,
+ .application-name span {
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
@@ -175,6 +177,7 @@
}
.extension-permissions,
+ .application-permissions,
.protection-permissions {
width: 60%;
}
@@ -184,13 +187,15 @@
vertical-align: top;
}
- .extension-name img {
+ .extension-name img,
+ .application-name img {
height: 20px;
margin-inline-end: 12px;
width: 20px;
}
.extension-permissions ul,
+ .application-permissions ul,
.report ul {
list-style: none;
margin: 0;
@@ -380,6 +385,41 @@
</div>
</section>
</template>
+ <template is="dom-if"
+ if="[[showApplicationReportingInfo_(applications_)]]">
+ <section class="application-reporting">
+ <h2 class="cr-title-text">$i18n{applicationReporting}</h2>
+ <div class="subtitle">
+ [[applicationReportingSubtitle_]]
+ </div>
+ <table class="content-indented">
+ <tr>
+ <th class="application-name">$i18n{applicationName}</th>
+ <th class="extension-permissions">
+ $i18n{applicationPermissions}
+ </th>
+ </tr>
+ <template is="dom-repeat" items="[[applications_]]">
+ <tr>
+ <td class="application-name">
+ <div title="[[item.name]]" role="presentation">
+ <img src="[[item.icon]]" alt="" aria-hidden="true">
+ <span>[[item.name]]</span>
+ </div>
+ </td>
+ <td class="application-permissions">
+ <ul>
+ <template is="dom-repeat" items="[[item.permissions]]"
+ as="permission">
+ <li>[[permission]]</li>
+ </template>
+ </ul>
+ </td>
+ </tr>
+ </template>
+ </table>
+ </section>
+ </template>
</div>
</div>
</main>
diff --git a/chromium/chrome/browser/resources/management/management_ui.ts b/chromium/chrome/browser/resources/management/management_ui.ts
index cc01578eba7..1994917d630 100644
--- a/chromium/chrome/browser/resources/management/management_ui.ts
+++ b/chromium/chrome/browser/resources/management/management_ui.ts
@@ -13,16 +13,18 @@ import './icons.html.js';
import './strings.m.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {sanitizeInnerHtml} from 'chrome://resources/js/parse_html_subset.js';
-import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {getTemplate} from './management_ui.html.js';
-import {BrowserReportingResponse, Extension, ManagementBrowserProxy, ManagementBrowserProxyImpl, ReportingType, ThreatProtectionInfo} from './management_browser_proxy.js';
+// clang-format off
+import {Application, BrowserReportingResponse, Extension, ManagementBrowserProxy, ManagementBrowserProxyImpl, ReportingType, ThreatProtectionInfo} from './management_browser_proxy.js';
// <if expr="is_chromeos">
import {DeviceReportingResponse, DeviceReportingType} from './management_browser_proxy.js';
// </if>
+import {getTemplate} from './management_ui.html.js';
+// clang-format on
interface BrowserReportingData {
messageIds: string[];
@@ -43,17 +45,32 @@ class ManagementUiElement extends ManagementUiElementBase {
static get properties() {
return {
/**
+ * List of messages related to application reporting.
+ */
+ applications_: Array,
+
+ /**
+ * Title of subsection for application reporting.
+ */
+ applicationReportingSubtitle_: String,
+
+ /**
* List of messages related to browser reporting.
*/
browserReportingInfo_: Array,
/**
- * List of messages related to browser reporting.
+ * List of messages related to extension reporting.
*/
extensions_: Array,
/**
- * List of messages related to browser reporting.
+ * Title of subsection for extension reporting.
+ */
+ extensionReportingSubtitle_: String,
+
+ /**
+ * List of messages related to managed websites reporting.
*/
managedWebsites_: Array,
@@ -85,11 +102,11 @@ class ManagementUiElement extends ManagementUiElementBase {
// </if>
managed_: Boolean,
- extensionReportingSubtitle_: String,
threatProtectionInfo_: Object,
};
}
+ private applications_: Application[]|null;
private browserReportingInfo_: BrowserReportingData[]|null;
private extensions_: Extension[]|null;
private managedWebsites_: string[]|null;
@@ -113,6 +130,7 @@ class ManagementUiElement extends ManagementUiElementBase {
// </if>
private managed_: boolean;
+ private applicationReportingSubtitle_: string;
private extensionReportingSubtitle_: string;
private threatProtectionInfo_: ThreatProtectionInfo;
private browserProxy_: ManagementBrowserProxy|null = null;
@@ -148,6 +166,7 @@ class ManagementUiElement extends ManagementUiElementBase {
this.getExtensions_();
this.getManagedWebsites_();
+ this.getApplications_();
// <if expr="is_chromeos">
this.getDeviceReportingInfo_();
this.getPluginVmDataCollectionStatus_();
@@ -177,6 +196,7 @@ class ManagementUiElement extends ManagementUiElementBase {
[ReportingType.USER]: 3,
[ReportingType.USER_ACTIVITY]: 4,
[ReportingType.DEVICE]: 5,
+ [ReportingType.LEGACY_TECH]: 6,
};
this.browserReportingInfo_ =
@@ -197,6 +217,12 @@ class ManagementUiElement extends ManagementUiElementBase {
});
}
+ private getApplications_() {
+ this.browserProxy_!.getApplications().then(applications => {
+ this.applications_ = applications;
+ });
+ }
+
private getThreatProtectionInfo_() {
this.browserProxy_!.getThreatProtectionInfo().then(info => {
this.threatProtectionInfo_ = info;
@@ -289,6 +315,8 @@ class ManagementUiElement extends ManagementUiElementBase {
return 'management:timelapse';
case DeviceReportingType.PERIPHERALS:
return 'management:usb';
+ case DeviceReportingType.LEGACY_TECH:
+ return 'management:legacy-tech';
default:
return 'cr:computer';
}
@@ -311,6 +339,13 @@ class ManagementUiElement extends ManagementUiElementBase {
}
/**
+ * @return Whether there are application reporting info to show.
+ */
+ private showApplicationReportingInfo_(): boolean {
+ return !!this.applications_ && this.applications_.length > 0;
+ }
+
+ /**
* @return Whether there is managed websites info to show.
*/
private showManagedWebsitesInfo_(): boolean {
@@ -333,6 +368,8 @@ class ManagementUiElement extends ManagementUiElementBase {
return 'management:account-circle';
case ReportingType.USER_ACTIVITY:
return 'management:public';
+ case ReportingType.LEGACY_TECH:
+ return 'management:legacy-tech';
default:
return 'cr:security';
}
@@ -360,8 +397,9 @@ class ManagementUiElement extends ManagementUiElementBase {
private updateManagedFields_() {
this.browserProxy_!.getContextualManagedData().then(data => {
this.managed_ = data.managed;
- this.extensionReportingSubtitle_ = data.extensionReportingTitle;
+ this.extensionReportingSubtitle_ = data.extensionReportingSubtitle;
this.managedWebsitesSubtitle_ = data.managedWebsitesSubtitle;
+ this.applicationReportingSubtitle_ = data.applicationReportingSubtitle;
this.subtitle_ = data.pageSubtitle;
// <if expr="chromeos_ash">
this.customerLogo_ = data.customerLogo;
diff --git a/chromium/chrome/browser/resources/media_router/internals/media_router_internals.html b/chromium/chrome/browser/resources/media_router/internals/media_router_internals.html
index dfec0d9fdc4..b86df62883f 100644
--- a/chromium/chrome/browser/resources/media_router/internals/media_router_internals.html
+++ b/chromium/chrome/browser/resources/media_router/internals/media_router_internals.html
@@ -17,13 +17,7 @@
<div id="cast-status-div" class="status"></div>
<h1>Mirroring Stats</h1>
- <div id="mirroring-stats-div" class="status">
- <label>
- <input id="checkbox-input" type="checkbox">
- <span>Enable Mirroring Stats</span>
- </label>
- <div id="mirroring-stats-enabled-div" class="status"></div>
- </div>
+ <div id="mirroring-stats-div" class="status"></div>
<h1>Logs</h1>
<div id="logs-div" class="status"></div>
diff --git a/chromium/chrome/browser/resources/media_router/internals/media_router_internals.ts b/chromium/chrome/browser/resources/media_router/internals/media_router_internals.ts
index 55e64ae73e1..3e43536b677 100644
--- a/chromium/chrome/browser/resources/media_router/internals/media_router_internals.ts
+++ b/chromium/chrome/browser/resources/media_router/internals/media_router_internals.ts
@@ -10,7 +10,7 @@ function formatJson(jsonObj: object) {
}
function displayMirroringStats(mirroringStats: object) {
- getRequiredElement('mirroring-stats-enabled-div').textContent =
+ getRequiredElement('mirroring-stats-div').textContent =
formatJson(mirroringStats);
}
@@ -29,15 +29,5 @@ document.addEventListener('DOMContentLoaded', function() {
addWebUiListener(
'on-mirroring-stats-update',
(mirroringStats: object) => displayMirroringStats(mirroringStats));
-
- const checkbox = getRequiredElement('checkbox-input') as HTMLInputElement;
- sendWithPromise('isMirroringStatsEnabled').then((enabled: boolean) => {
- checkbox.checked = enabled;
- });
-
- checkbox.addEventListener('change', async function() {
- const mirroringStats =
- await sendWithPromise('setMirroringStatsEnabled', checkbox.checked);
- displayMirroringStats(mirroringStats);
- });
+ sendWithPromise('getMirroringStats').then(displayMirroringStats);
});
diff --git a/chromium/chrome/browser/resources/nearby_internals/BUILD.gn b/chromium/chrome/browser/resources/nearby_internals/BUILD.gn
index 1198a308e31..04d731f316d 100644
--- a/chromium/chrome/browser/resources/nearby_internals/BUILD.gn
+++ b/chromium/chrome/browser/resources/nearby_internals/BUILD.gn
@@ -24,6 +24,7 @@ web_component_files = [
"http_tab.js",
"log_object.js",
"logging_tab.js",
+ "log_types.js",
"nearby_internals.js",
"ui_trigger_list_object.js",
"np_list_object.js",
@@ -72,7 +73,7 @@ preprocess_if_expr("preprocess") {
in_files = [
"nearby_contact_browser_proxy.js",
"nearby_http_browser_proxy.js",
- "nearby_logs_browser_proxy.js",
+ "cross_device_logs_browser_proxy.js",
"nearby_presence_browser_proxy.js",
"nearby_prefs_browser_proxy.js",
"nearby_ui_trigger_browser_proxy.js",
@@ -114,14 +115,15 @@ js_type_check("closure_compile") {
":contact_object",
":contact_tab",
":cross_device_internals",
+ ":cross_device_logs_browser_proxy",
":http_message_object",
":http_tab",
":log_object",
+ ":log_types",
":logging_tab",
":nearby_contact_browser_proxy",
":nearby_http_browser_proxy",
":nearby_internals",
- ":nearby_logs_browser_proxy",
":nearby_prefs_browser_proxy",
":nearby_presence_browser_proxy",
":nearby_ui_trigger_browser_proxy",
@@ -134,6 +136,10 @@ js_type_check("closure_compile") {
js_library("cross_device_internals") {
deps = [
+ ":cross_device_logs_browser_proxy",
+ ":log_object",
+ ":log_types",
+ ":logging_tab",
":nearby_presence_browser_proxy",
":np_list_object",
":types",
@@ -147,13 +153,13 @@ js_library("nearby_internals") {
deps = [
":contact_object",
":contact_tab",
+ ":cross_device_logs_browser_proxy",
":http_message_object",
":http_tab",
":log_object",
":logging_tab",
":nearby_contact_browser_proxy",
":nearby_http_browser_proxy",
- ":nearby_logs_browser_proxy",
":nearby_prefs_browser_proxy",
":nearby_ui_trigger_browser_proxy",
":np_list_object",
@@ -164,10 +170,17 @@ js_library("nearby_internals") {
]
}
+js_library("log_types") {
+ deps = [
+ ":types",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+}
+
js_library("logging_tab") {
deps = [
+ ":cross_device_logs_browser_proxy",
":log_object",
- ":nearby_logs_browser_proxy",
":nearby_prefs_browser_proxy",
":types",
"//ash/webui/common/resources:web_ui_listener_behavior",
@@ -177,13 +190,13 @@ js_library("logging_tab") {
js_library("log_object") {
deps = [
- ":nearby_logs_browser_proxy",
+ ":cross_device_logs_browser_proxy",
":types",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
}
-js_library("nearby_logs_browser_proxy") {
+js_library("cross_device_logs_browser_proxy") {
deps = [
":types",
"//ash/webui/common/resources:cr.m",
diff --git a/chromium/chrome/browser/resources/nearby_internals/OWNERS b/chromium/chrome/browser/resources/nearby_internals/OWNERS
index b76a3f65088..6e0dc9e09fd 100644
--- a/chromium/chrome/browser/resources/nearby_internals/OWNERS
+++ b/chromium/chrome/browser/resources/nearby_internals/OWNERS
@@ -1,2 +1,4 @@
file://chrome/browser/nearby_sharing/OWNERS
khorimoto@chromium.org
+julietlevesque@google.com
+akingsb@google.com
diff --git a/chromium/chrome/browser/resources/nearby_internals/cross_device_internals.html b/chromium/chrome/browser/resources/nearby_internals/cross_device_internals.html
index 5f3fff9b4d4..45809faa0c0 100644
--- a/chromium/chrome/browser/resources/nearby_internals/cross_device_internals.html
+++ b/chromium/chrome/browser/resources/nearby_internals/cross_device_internals.html
@@ -5,60 +5,110 @@
Until it is complete, cross-device-internals will be used as a part of the
ui-triggers-tab.
-->
-<style include="shared-style">
- cr-button {
- height: 35px;
- padding: 5px;
- width: 150px;
- }
+<style include="shared-style md-select cros-color-overrides"></style>
- #buttons {
- margin-bottom: 80px;
- }
+<body>
+ <section id='controls'>
+ <header>
+ <div class='title'>Cross Device Internals</div>
+ </header>
+ <div class='controls-panel'>
+ <div class="control">
+ <div id='logControlSplit'>
+ <log-types id="logType"></log-types>
+ <div id="logFilters">
+ <div class="input-div">
+ <label id="logSearchLabel">Search</label>
+ <input aria-labelledby="logSearchLabel" id="logSearch"
+ type="text">
+ </div>
+ <label id="logLevelLabel">Min Log Level</label>
+ <select aria-labelledby="logLevelLabel" id="logLevelSelector">
+ <template is="dom-repeat" items="[[logLevelList]]">
+ <option
+ value="[[item.value]]">
+ [[item.name]]
+ </option>
+ </template>
+ </select>
+ <div id="filterApplyDiv">
+ <cr-button class="internals-button" id="filterApply"
+ on-click="addLogFilter">Apply Filters</cr-button>
+ </div>
+ </div>
+ </div>
- #header {
- padding: 20px;
- }
+ <div id="buttons">
+ <cr-button disabled="[[!logList_.length]]" class="internals-button"
+ on-click="onSaveUnfilteredLogsButtonClicked_">
+ Save All Logs
+ </cr-button>
+ <cr-button disabled="[[!logList_.length]]" class="internals-button"
+ on-click="onSaveFilteredLogsButtonClicked_">
+ Save Filtered Logs
+ </cr-button>
+ <cr-button disabled="[[!logList_.length]]" class="internals-button"
+ on-click="onClearLogsButtonClicked_">
+ Clear Logs
+ </cr-button>
+ </div>
+ </div>
+ </div>
- #devices-section {
- display: flex;
- flex-direction: column;
- height: 100%;
- padding: 6px;
- white-space: pre-wrap;
- }
+ <div class='controls-panel'>
+ <div class='control'>
+ <div id="actions">
+ <div class="select-div">
+ <label id="ActionGroupLabel">Feature: </label>
+ <select aria-labelledby="ActionGroupLabel" name="ActionGroup"
+ id="actionGroup" class="md-select"
+ on-change="updateActionsSelect">
+ <template is="dom-repeat" items="[[featuresList]]">
+ <option
+ value="[[item.value]]">
+ [[item.name]]
+ </option>
+ </template>
+ </select>
+ </div>
- np-object:last-child {
- border-bottom: var(--standard-border);
- }
+ <div class="select-div">
+ <label id="ActionSelectLabel">Action: </label>
+ <select aria-labelledby="ActionSelectLabel" name="Action"
+ id="actionSelect" class="md-select">
+ <template is="dom-repeat" items="{{actionsSelectList}}"
+ as="action">
+ <option value="{{action.value}}">
+ {{action.name}}
+ </option>
+ </template>
+ </select>
+ </div>
+ <cr-button id="executeButton" class="internals-button"
+ on-click="perform_action">Execute
+ </cr-button>
+ </div>
+ </div>
+ </div>
- np-object {
- border-inline-end: var(--standard-border);
- border-inline-start: var(--standard-border);
- border-top: var(--standard-border);
- height: 50px;
- }
+ <div class='control' id='remoteDevicesControl'>
+ <div class='control-title'>Returned Objects</div>
+ <iron-list items="[[npDiscoveredDevicesList_]]" as="generic-object"
+ id="devicesSection">
+ <template>
+ <np-object device="[[generic-object]]">
+ </np-object>
+ </template>
+ </iron-list>
+ </div>
+ </section>
-</style>
-
-<div id="buttons">
- <cr-button class="internals-button" on-click="onStartScanClicked">
- Presence: Start scan
- </cr-button>
-
- <cr-button class="internals-button" on-click="onSyncCredentialsClicked">
- Presence: Sync Credentials
- </cr-button>
-
- <cr-button class="internals-button" on-click="onFirstTimeFlowClicked">
- Presence: First time flow
- </cr-button>
-</div>
-
-<iron-list items="[[npDiscoveredDevicesList_]]" as="generic-object"
- id="devices-section">
- <template>
- <np-object item="[[generic-object]]">
- </np-object>
- </template>
-</iron-list>
+ <div id="logsPanel">
+ <iron-list id="log-iron-list" items="[[filteredLogList_]]" as="log">
+ <template>
+ <log-object log-message="[[log]]">
+ </log-object>
+ </template>
+ </iron-list>
+ </div>
+</body>
diff --git a/chromium/chrome/browser/resources/nearby_internals/cross_device_internals.js b/chromium/chrome/browser/resources/nearby_internals/cross_device_internals.js
index b12fad0780f..33beee89fc3 100644
--- a/chromium/chrome/browser/resources/nearby_internals/cross_device_internals.js
+++ b/chromium/chrome/browser/resources/nearby_internals/cross_device_internals.js
@@ -6,13 +6,51 @@ import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import './shared_style.css.js';
import './np_list_object.js';
+import './logging_tab.js';
+import './log_object.js';
+import './log_types.js';
+import '//resources/cr_elements/md_select.css.js';
+import '//resources/cr_elements/chromeos/cros_color_overrides.css.js';
+import 'chrome://resources/polymer/v3_0/iron-location/iron-location.js';
+import 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js';
import {WebUIListenerBehavior} from 'chrome://resources/ash/common/web_ui_listener_behavior.js';
import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './cross_device_internals.html.js';
+import {NearbyLogsBrowserProxy} from './cross_device_logs_browser_proxy.js';
import {NearbyPresenceBrowserProxy} from './nearby_presence_browser_proxy.js';
-import {PresenceDevice} from './types.js';
+import {ActionValues, FeatureValues, LogMessage, LogProvider, PresenceDevice, SelectOption, Severity} from './types.js';
+
+
+/**
+ * Converts log message to string format for saved download file.
+ * @param {!LogMessage} log
+ * @return {string}
+ */
+function logToSavedString_(log) {
+ // Convert to string value for |line.severity|.
+ let severity;
+ switch (log.severity) {
+ case Severity.INFO:
+ severity = 'INFO';
+ break;
+ case Severity.WARNING:
+ severity = 'WARNING';
+ break;
+ case Severity.ERROR:
+ severity = 'ERROR';
+ break;
+ case Severity.VERBOSE:
+ severity = 'VERBOSE';
+ break;
+ }
+
+ // Reduce the file path to just the file name for logging simplification.
+ const file = log.file.substring(log.file.lastIndexOf('/') + 1);
+
+ return `[${log.time} ${severity} ${file} (${log.line})] ${log.text}\n`;
+}
Polymer({
is: 'cross-device-internals',
@@ -32,14 +70,119 @@ Polymer({
type: Array,
value: [],
},
+
+ /** @private {!Array<!SelectOption>} */
+ featuresList: {
+ type: Array,
+ value: [
+ {name: 'Nearby Presence', value: FeatureValues.NearbyPresence},
+ {name: 'Nearby Share', value: FeatureValues.NearbyShare},
+ {name: 'Nearby Connections', value: FeatureValues.NearbyConnections},
+ {name: 'Fast Pair', value: FeatureValues.FastPair},
+ ],
+ },
+
+ /** @private {!Array<!SelectOption>} */
+ nearbyPresenceActionList: {
+ type: Array,
+ value: [
+ {name: 'Start Scan', value: ActionValues.STARTSCAN},
+ {name: 'Stop Scan', value: ActionValues.STOPSCAN},
+ {name: 'Sync Credentials', value: ActionValues.SYNCCREDENTIALS},
+ {name: 'First time flow', value: ActionValues.FIRSTTIMEFLOW},
+ ],
+ },
+
+ /** @private {!Array<!SelectOption>} */
+ logLevelList: {
+ type: Array,
+ value: [
+ {name: 'VERBOSE', value: Severity.VERBOSE},
+ {name: 'INFO', value: Severity.INFO},
+ {name: 'WARNING', value: Severity.WARNING},
+ {name: 'ERROR', value: Severity.ERROR},
+ ],
+ },
+
+ /** @private {!Array<!SelectOption>} */
+ nearbyShareActionList: {
+ type: Array,
+ value: [],
+ },
+
+ /** @private {!Array<!SelectOption>} */
+ nearbyConnectionsActionList: {
+ type: Array,
+ value: [],
+ },
+
+ /** @private {!Array<!SelectOption>} */
+ fastPairActionList: {
+ type: Array,
+ value: [],
+ },
+
+ /** @private {!Array<!SelectOption>} */
+ actionsSelectList: {
+ type: Array,
+ value: [],
+ },
+
+ /**
+ * @private {!Array<!LogMessage>}
+ */
+ logList_: {
+ type: Array,
+ value: [],
+ },
+
+ /**
+ * @private {!Array<!LogMessage>}
+ */
+ filteredLogList_: {
+ type: Array,
+ value: [],
+ },
+
+ /** @private {!string} */
+ feature: {
+ type: String,
+ },
+
+ /** @private {!string} */
+ currentFilter: {
+ type: String,
+ },
+
+ /** @private {!Severity} */
+ currentSeverity: {
+ type: Severity,
+ value: Severity.VERBOSE,
+ },
+
+ /** @private {!Array<FeatureValues>} */
+ currentLogTypes: {
+ type: FeatureValues,
+ value: [
+ FeatureValues.NearbyShare,
+ FeatureValues.NearbyConnections,
+ FeatureValues.NearbyPresence,
+ FeatureValues.FastPair,
+ ],
+ },
},
+
+ /** @private {?LogProvider}*/
+ logProvider_: null,
+
created() {
this.browserProxy_ = NearbyPresenceBrowserProxy.getInstance();
},
/**
- * When the page is initialized, notify the C++ layer to allow JavaScript.
+ * When the page is initialized, notify the C++ layer and load in the
+ * contents of its log buffer. Initialize WebUI Listeners.
* @override
*/
attached() {
@@ -51,12 +194,70 @@ Polymer({
device => this.onPresenceDeviceChanged_(device));
this.addWebUIListener(
'presence-device-lost', device => this.onPresenceDeviceLost_(device));
+ this.set('actionsSelectList', this.nearbyPresenceActionList);
+
+ this.logProvider_ = {
+ messageAddedEventName: 'log-message-added',
+ bufferClearedEventName: 'log-buffer-cleared',
+ logFilePrefix: 'cross_device_logs_',
+ getLogMessages: () =>
+ NearbyLogsBrowserProxy.getInstance().getLogMessages(),
+ };
+ this.addWebUIListener(
+ this.logProvider_.messageAddedEventName,
+ log => this.onLogMessageAdded_(log));
+ this.addWebUIListener(
+ this.logProvider_.bufferClearedEventName,
+ () => this.onWebUILogBufferCleared_());
+ this.logProvider_.getLogMessages().then(
+ logs => this.onGetLogMessages_(logs));
},
onStartScanClicked() {
this.browserProxy_.SendStartScan();
},
+ updateActionsSelect() {
+ switch (Number(this.$.actionGroup.value)) {
+ case FeatureValues.NearbyPresence:
+ this.set('actionsSelectList', this.nearbyPresenceActionList);
+ break;
+ case FeatureValues.NearbyConnections:
+ this.set('actionsSelectList', this.nearbyConnectionsActionList);
+ break;
+ case FeatureValues.NearbyShare:
+ this.set('actionsSelectList', this.nearbyShareActionList);
+ break;
+ case FeatureValues.FastPair:
+ this.set('actionsSelectList', this.fastPairActionList);
+ break;
+ }
+ },
+
+ perform_action() {
+ switch (Number(this.$.actionSelect.value)) {
+ case ActionValues.STARTSCAN:
+ this.browserProxy_.SendStartScan();
+ break;
+ case ActionValues.STOPSCAN:
+ this.browserProxy_.SendStopScan();
+ break;
+ case ActionValues.SYNCCREDENTIALS:
+ this.browserProxy_.SendSyncCredentials();
+ break;
+ case ActionValues.FIRSTTIMEFLOW:
+ this.browserProxy_.SendFirstTimeFlow();
+ break;
+ default:
+ break;
+ }
+ },
+
+
+ onStopScanClicked() {
+ this.browserProxy_.SendStopScan();
+ },
+
onSyncCredentialsClicked() {
this.browserProxy_.SendSyncCredentials();
},
@@ -68,6 +269,7 @@ Polymer({
onPresenceDeviceFound_(device) {
const type = device['type'];
const endpointId = device['endpoint_id'];
+ const actions = device['actions'];
// If there is not a device with this endpoint_id currently in the devices
// list, add it.
@@ -77,6 +279,7 @@ Polymer({
'connectable': true,
'type': type,
'endpoint_id': endpointId,
+ 'actions': actions,
});
}
},
@@ -86,6 +289,7 @@ Polymer({
onPresenceDeviceChanged_(device) {
const type = device['type'];
const endpointId = device['endpoint_id'];
+ const actions = device['actions'];
const index = this.npDiscoveredDevicesList_.findIndex(
list_device => list_device.endpoint_id === endpointId);
@@ -97,6 +301,7 @@ Polymer({
'connectable': true,
'type': type,
'endpoint_id': endpointId,
+ 'actions': actions,
});
return;
}
@@ -105,12 +310,14 @@ Polymer({
'connectable': true,
'type': type,
'endpoint_id': endpointId,
+ 'actions': actions,
};
},
onPresenceDeviceLost_(device) {
const type = device['type'];
const endpointId = device['endpoint_id'];
+ const actions = device['actions'];
const index = this.npDiscoveredDevicesList_.findIndex(
list_device => list_device.endpoint_id === endpointId);
@@ -124,6 +331,154 @@ Polymer({
'connectable': false,
'type': type,
'endpoint_id': endpointId,
+ 'actions': actions,
};
},
+
+
+ /**
+ * Clears javascript logs displayed, but c++ log buffer remains.
+ * @private
+ */
+ onClearLogsButtonClicked_() {
+ this.clearLogBuffer_();
+ },
+
+ /**
+ * Saves and downloads all javascript logs.
+ * @private
+ */
+ onSaveUnfilteredLogsButtonClicked_() {
+ this.onSaveLogsButtonClicked_(false);
+ },
+
+ /**
+ * Saves and downloads javascript logs that currently appear on the page.
+ * @private
+ */
+ onSaveFilteredLogsButtonClicked_() {
+ this.onSaveLogsButtonClicked_(true);
+ },
+
+ /**
+ * Saves and downloads javascript logs.
+ * @param {!boolean} filtered
+ * @private
+ */
+ onSaveLogsButtonClicked_(filtered) {
+ let blob;
+ if (filtered) {
+ blob = new Blob(
+ this.filteredLogList_.map(logToSavedString_).reverse(),
+ {type: 'text/plain;charset=utf-8'});
+ } else {
+ blob = new Blob(
+ this.logList_.map(logToSavedString_).reverse(),
+ {type: 'text/plain;charset=utf-8'});
+ }
+ const url = URL.createObjectURL(blob);
+
+ const anchorElement = document.createElement('a');
+ anchorElement.href = url;
+ anchorElement.download =
+ this.logProvider_.logFilePrefix + new Date().toJSON() + '.txt';
+ document.body.appendChild(anchorElement);
+ anchorElement.click();
+
+ window.setTimeout(function() {
+ document.body.removeChild(anchorElement);
+ window.URL.revokeObjectURL(url);
+ }, 0);
+ },
+
+ /**
+ * Adds a log message to the javascript log list displayed. Called from the
+ * C++ WebUI handler when a log message is added to the log buffer.
+ * @param {!LogMessage} log
+ * @private
+ */
+ onLogMessageAdded_(log) {
+ this.push('logList_', log);
+ if ((log.text.match(this.currentFilter) ||
+ log.file.match(this.currentFilter)) &&
+ log.severity >= this.currentSeverity &&
+ this.currentLogTypes.includes(log.feature)) {
+ this.push('filteredLogList_', log);
+ }
+ },
+
+ addLogFilter() {
+ switch (Number(this.$.logLevelSelector.value)) {
+ case Severity.VERBOSE:
+ this.set(
+ 'filteredLogList_',
+ this.logList_.filter((log) => log.severity >= Severity.VERBOSE));
+ this.currentSeverity = Severity.VERBOSE;
+ break;
+ case Severity.INFO:
+ this.set(
+ 'filteredLogList_',
+ this.logList_.filter((log) => log.severity >= Severity.INFO));
+ this.currentSeverity = Severity.INFO;
+ break;
+ case Severity.WARNING:
+ this.set(
+ 'filteredLogList_',
+ this.logList_.filter((log) => log.severity >= Severity.WARNING));
+ this.currentSeverity = Severity.WARNING;
+ break;
+ case Severity.ERROR:
+ this.set(
+ 'filteredLogList_',
+ this.logList_.filter((log) => log.severity >= Severity.ERROR));
+ this.currentSeverity = Severity.ERROR;
+ break;
+ }
+
+ this.set(
+ 'currentLogTypes',
+ this.$.logType.currentLogTypes,
+ );
+
+ this.set(
+ 'filteredLogList_',
+ this.filteredLogList_.filter(
+ (log) => this.currentLogTypes.includes(log.feature)));
+
+ this.currentFilter = this.$.logSearch.value;
+ this.set(
+ 'filteredLogList_',
+ this.filteredLogList_.filter(
+ (log) =>
+ (log.text.match(this.currentFilter) ||
+ log.file.match(this.currentFilter))));
+ },
+
+ /**
+ * Called in response to WebUI handler clearing log buffer.
+ * @private
+ */
+ onWebUILogBufferCleared_() {
+ this.clearLogBuffer_();
+ },
+
+ /**
+ * Parses an array of log messages and adds to the javascript list sent in
+ * from the initial page load.
+ * @param {!Array<!LogMessage>} logs
+ * @private
+ */
+ onGetLogMessages_(logs) {
+ this.logList_ = logs.concat(this.logList_);
+ this.filteredLogList_ = logs.slice();
+ },
+
+ /**
+ * Clears the javascript log buffer.
+ * @private
+ */
+ clearLogBuffer_() {
+ this.logList_ = [];
+ this.filteredLogList_ = [];
+ },
});
diff --git a/chromium/chrome/browser/resources/nearby_internals/nearby_logs_browser_proxy.js b/chromium/chrome/browser/resources/nearby_internals/cross_device_logs_browser_proxy.js
index ef9fc7860c9..8166aeb88cf 100644
--- a/chromium/chrome/browser/resources/nearby_internals/nearby_logs_browser_proxy.js
+++ b/chromium/chrome/browser/resources/nearby_internals/cross_device_logs_browser_proxy.js
@@ -4,6 +4,7 @@
import {sendWithPromise} from 'chrome://resources/ash/common/cr.m.js';
import {addSingletonGetter} from 'chrome://resources/ash/common/cr_deprecated.js';
+
import {LogMessage} from './types.js';
/**
diff --git a/chromium/chrome/browser/resources/nearby_internals/index.html b/chromium/chrome/browser/resources/nearby_internals/index.html
index eb4fd150fc2..c1c5e69b393 100644
--- a/chromium/chrome/browser/resources/nearby_internals/index.html
+++ b/chromium/chrome/browser/resources/nearby_internals/index.html
@@ -5,13 +5,21 @@
<base href="chrome://nearby-internals">
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
<style>
- body {
+ html, body {
margin: 0;
+ height: 100%;
+ }
+
+ cross-device-internals {
+ display: flex;
+ flex-direction: row;
+ height: 100%;
+ width: 100%;
}
</style>
</head>
<body>
- <nearby-internals></nearby-internals>
- <script type="module" src="nearby_internals.js"></script>
+ <cross-device-internals></cross-device-internals>
+ <script type="module" src="cross_device_internals.js"></script>
</body>
</html>
diff --git a/chromium/chrome/browser/resources/nearby_internals/log_types.html b/chromium/chrome/browser/resources/nearby_internals/log_types.html
new file mode 100644
index 00000000000..8bb4176353c
--- /dev/null
+++ b/chromium/chrome/browser/resources/nearby_internals/log_types.html
@@ -0,0 +1,28 @@
+
+<style include="shared-style"></style>
+
+<div class='control-title'>Log Type</div>
+<div class="input-div">
+ <input aria-labelledby="np-label" type="checkbox"
+ on-click="nearbyPresenceCheckboxClicked"
+ id="nearbyPresenceCheckbox" checked>
+ <label id="np-label">Nearby Presence</label>
+</div>
+<div class="input-div">
+ <input aria-labelledby="ns-label" type="checkbox"
+ on-click="nearbyShareCheckboxClicked"
+ id="nearbyShareCheckbox" checked>
+ <label id="ns-label">Nearby Share</label>
+</div>
+<div class="input-div">
+ <input aria-labelledby="nc-label" type="checkbox"
+ on-click="nearbyConnectionsCheckboxClicked"
+ id="nearbyConnectionsCheckbox" checked>
+ <label id="nc-label">Nearby Connections</label>
+</div>
+<div class="input-div">
+ <input aria-labelledby="fp-label" type="checkbox"
+ on-click="fastPairCheckboxClicked"
+ id="fastPairCheckbox" checked>
+ <label id="fp-label">Fast Pair</label>
+</div> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/nearby_internals/log_types.js b/chromium/chrome/browser/resources/nearby_internals/log_types.js
new file mode 100644
index 00000000000..c7441f5e75e
--- /dev/null
+++ b/chromium/chrome/browser/resources/nearby_internals/log_types.js
@@ -0,0 +1,81 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import './shared_style.css.js';
+import 'chrome://resources/polymer/v3_0/iron-location/iron-location.js';
+import 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js';
+
+import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './log_types.html.js';
+import {FeatureValues} from './types.js';
+
+Polymer({
+ is: 'log-types',
+
+ _template: getTemplate(),
+
+
+ properties: {
+ /** @private {!Array<FeatureValues>} */
+ currentLogTypes: {
+ type: FeatureValues,
+ value: [
+ FeatureValues.NearbyShare,
+ FeatureValues.NearbyConnections,
+ FeatureValues.NearbyPresence,
+ FeatureValues.FastPair,
+ ],
+ },
+ },
+
+ nearbyPresenceCheckboxClicked() {
+ if (this.$.nearbyPresenceCheckbox.checked &&
+ !this.currentLogTypes.includes(FeatureValues.NearbyPresence)) {
+ this.currentLogTypes.push(FeatureValues.NearbyPresence);
+ }
+ if (!this.$.nearbyPresenceCheckbox.checked &&
+ this.currentLogTypes.includes(FeatureValues.NearbyPresence)) {
+ this.currentLogTypes.splice(
+ this.currentLogTypes.lastIndexOf(FeatureValues.NearbyPresence), 1);
+ }
+ },
+
+ nearbyShareCheckboxClicked() {
+ if (this.$.nearbyShareCheckbox.checked &&
+ !this.currentLogTypes.includes(FeatureValues.NearbyShare)) {
+ this.currentLogTypes.push(FeatureValues.NearbyShare);
+ }
+ if (!this.$.nearbyShareCheckbox.checked &&
+ this.currentLogTypes.includes(FeatureValues.NearbyShare)) {
+ this.currentLogTypes.splice(
+ this.currentLogTypes.lastIndexOf(FeatureValues.NearbyShare), 1);
+ }
+ },
+
+ nearbyConnectionsCheckboxClicked() {
+ if (this.$.nearbyConnectionsCheckbox.checked &&
+ !this.currentLogTypes.includes(FeatureValues.NearbyConnections)) {
+ this.currentLogTypes.push(FeatureValues.NearbyConnections);
+ }
+ if (!this.$.nearbyConnectionsCheckbox.checked &&
+ this.currentLogTypes.includes(FeatureValues.NearbyConnections)) {
+ this.currentLogTypes.splice(
+ this.currentLogTypes.lastIndexOf(FeatureValues.NearbyConnections), 1);
+ }
+ },
+
+ fastPairCheckboxClicked() {
+ if (this.$.fastPairCheckbox.checked &&
+ !this.currentLogTypes.includes(FeatureValues.FastPair)) {
+ this.currentLogTypes.push(FeatureValues.FastPair);
+ }
+ if (!this.$.fastPairCheckbox.checked &&
+ this.currentLogTypes.includes(FeatureValues.FastPair)) {
+ this.currentLogTypes.splice(
+ this.currentLogTypes.lastIndexOf(FeatureValues.FastPair), 1);
+ }
+ },
+});
diff --git a/chromium/chrome/browser/resources/nearby_internals/logging_tab.html b/chromium/chrome/browser/resources/nearby_internals/logging_tab.html
index 4b9873f5c54..ea99e36fb82 100644
--- a/chromium/chrome/browser/resources/nearby_internals/logging_tab.html
+++ b/chromium/chrome/browser/resources/nearby_internals/logging_tab.html
@@ -1,34 +1,64 @@
-<style include="shared-style">
- :host {
- --standard-border: 1px solid black;
- }
+<style include="shared-style"></style>
- #logs-list {
- display: flex;
- flex-direction: column;
- height: 100%;
- white-space: pre-wrap;
- }
-
- log-object {
- border-inline-end: var(--standard-border);
- border-inline-start: var(--standard-border);
- border-top: var(--standard-border);
- }
-
- log-object:last-child {
- border-bottom: var(--standard-border);
- }
-</style>
-<cr-button disabled="[[!logList_.length]]" class="internals-button"
- on-click="onSaveLogsButtonClicked_">
- Save Logs
-</cr-button>
-<cr-button disabled="[[!logList_.length]]" class="internals-button"
- on-click="onClearLogsButtonClicked_">
- Clear Logs
-</cr-button>
-<iron-list items="[[logList_]]" as="log" id="logs-list">
+<div id='controlsPanel'>
+ <div class="control">
+ <div id='logControlSplit'>
+ <div id="logType">
+ <div class='control-title'>Log Type</div>
+ <div class="input-div">
+ <input aria-labelledby="nearbyPresenceLabel" type="checkbox">
+ <label id="nearbyPresenceLabel">Nearby Presence</label>
+ </div>
+ <div class="input-div">
+ <input aria-labelledby="nearbyShareLabel" type="checkbox">
+ <label id="nearbyShareLabel">Nearby Share</label>
+ </div>
+ <div class="input-div">
+ <input aria-labelledby="nearbyConnectionsLabel" type="checkbox">
+ <label id="nearbyConnectionsLabel">Nearby Connections</label>
+ </div>
+ <div class="input-div">
+ <input aria-labelledby="fastPairLabel" type="checkbox">
+ <label id="fastPairLabel">Fast Pair</label>
+ </div>
+ </div>
+ <div id="logFilters">
+ <div class="input-div">
+ <label id="logSearchLabel">Search</label>
+ <input aria-labelledby="logSearchLabel" id="logSearch" type="text">
+ </div>
+ <label id="logLevelLabel">Min Log Level</label>
+ <select aria-labelledby="logLevelLabel" id="logLevelSelector">
+ <template is="dom-repeat" items="[[logLevelList]]">
+ <option
+ value="[[item.value]]">
+ [[item.name]]
+ </option>
+ </template>
+ </select>
+ <div id="filterApplyDiv">
+ <cr-button class="internals-button" id="filterApply"
+ on-click="addLogFilter">Apply Filters</cr-button>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div id="buttons">
+ <cr-button disabled="[[!logList_.length]]" class="internals-button"
+ on-click="onSaveUnfilteredLogsButtonClicked_">
+ Save All Logs
+ </cr-button>
+ <cr-button disabled="[[!filteredLogList_.length]]" class="internals-button"
+ on-click="onSaveFilteredLogsButtonClicked_">
+ Save Filtered Logs
+ </cr-button>
+ <cr-button disabled="[[!logList_.length]]" class="internals-button"
+ on-click="onClearLogsButtonClicked_">
+ Clear Logs
+ </cr-button>
+</div>
+<iron-list id="logIronList" items="[[filteredLogList_]]" as="log">
<template>
<log-object log-message="[[log]]">
</log-object>
diff --git a/chromium/chrome/browser/resources/nearby_internals/logging_tab.js b/chromium/chrome/browser/resources/nearby_internals/logging_tab.js
index 94640980f18..03b848f37bb 100644
--- a/chromium/chrome/browser/resources/nearby_internals/logging_tab.js
+++ b/chromium/chrome/browser/resources/nearby_internals/logging_tab.js
@@ -11,9 +11,9 @@ import './shared_style.css.js';
import {WebUIListenerBehavior} from 'chrome://resources/ash/common/web_ui_listener_behavior.js';
import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {NearbyLogsBrowserProxy} from './cross_device_logs_browser_proxy.js';
import {getTemplate} from './logging_tab.html.js';
-import {NearbyLogsBrowserProxy} from './nearby_logs_browser_proxy.js';
-import {LogMessage, LogProvider, Severity} from './types.js';
+import {LogMessage, LogProvider, SelectOption, Severity} from './types.js';
/**
* Converts log message to string format for saved download file.
@@ -95,10 +95,40 @@ Polymer({
value: [],
},
+ /**
+ * @private {!Array<!LogMessage>}
+ */
+ filteredLogList_: {
+ type: Array,
+ value: [],
+ },
+
/** @private {!string} */
feature: {
type: String,
},
+
+ /** @private {!string} */
+ currentFilter: {
+ type: String,
+ },
+
+ /** @private {!Severity} */
+ currentSeverity: {
+ type: Severity,
+ value: Severity.VERBOSE,
+ },
+
+ /** @private {!Array<!SelectOption>} */
+ logLevelList: {
+ type: Array,
+ value: [
+ {name: 'VERBOSE', value: Severity.VERBOSE},
+ {name: 'INFO', value: Severity.INFO},
+ {name: 'WARNING', value: Severity.WARNING},
+ {name: 'ERROR', value: Severity.ERROR},
+ ],
+ },
},
/** @private {?LogProvider}*/
@@ -130,12 +160,37 @@ Polymer({
},
/**
- * Saves and downloads javascript logs that appear on the page.
+ * Saves and downloads all javascript logs.
+ * @private
+ */
+ onSaveUnfilteredLogsButtonClicked_() {
+ this.onSaveLogsButtonClicked_(false);
+ },
+
+ /**
+ * Saves and downloads javascript logs that currently appear on the page.
+ * @private
+ */
+ onSaveFilteredLogsButtonClicked_() {
+ this.onSaveLogsButtonClicked_(true);
+ },
+
+ /**
+ * Saves and downloads javascript logs.
+ * @param {!boolean} filtered
* @private
*/
- onSaveLogsButtonClicked_() {
- const blob = new Blob(
- this.getSerializedLogStrings_(), {type: 'text/plain;charset=utf-8'});
+ onSaveLogsButtonClicked_(filtered) {
+ let blob;
+ if (filtered) {
+ blob = new Blob(
+ this.filteredLogList_.map(logToSavedString_),
+ {type: 'text/plain;charset=utf-8'});
+ } else {
+ blob = new Blob(
+ this.logList_.map(logToSavedString_),
+ {type: 'text/plain;charset=utf-8'});
+ }
const url = URL.createObjectURL(blob);
const anchorElement = document.createElement('a');
@@ -152,24 +207,55 @@ Polymer({
},
/**
- * Iterates through log messages in |logList_| and prepares them for download.
- * @private
- * @return {!Array<string>}
- */
- getSerializedLogStrings_() {
- // Reverse the logs so that the oldest logs appear first and the newest logs
- // appear last.
- return this.logList_.map(logToSavedString_).reverse();
- },
-
- /**
* Adds a log message to the javascript log list displayed. Called from the
* C++ WebUI handler when a log message is added to the log buffer.
- * @param {!Array<!LogMessage>} log
+ * @param {!LogMessage} log
* @private
*/
onLogMessageAdded_(log) {
- this.unshift('logList_', log);
+ this.push('logList_', log);
+ if ((log.text.match(this.currentFilter) ||
+ log.file.match(this.currentFilter)) &&
+ log.severity >= this.currentSeverity) {
+ this.push('filteredLogList_', log);
+ }
+ },
+
+ addLogFilter() {
+ switch (Number(this.$.logLevelSelector.value)) {
+ case Severity.VERBOSE:
+ this.set(
+ 'filteredLogList_',
+ this.logList_.filter((log) => log.severity >= Severity.VERBOSE));
+ this.currentSeverity = Severity.VERBOSE;
+ break;
+ case Severity.INFO:
+ this.set(
+ 'filteredLogList_',
+ this.logList_.filter((log) => log.severity >= Severity.INFO));
+ this.currentSeverity = Severity.INFO;
+ break;
+ case Severity.WARNING:
+ this.set(
+ 'filteredLogList_',
+ this.logList_.filter((log) => log.severity >= Severity.WARNING));
+ this.currentSeverity = Severity.WARNING;
+ break;
+ case Severity.ERROR:
+ this.set(
+ 'filteredLogList_',
+ this.logList_.filter((log) => log.severity >= Severity.ERROR));
+ this.currentSeverity = Severity.ERROR;
+ break;
+ }
+
+ this.currentFilter = this.$.logSearch.value;
+ this.set(
+ 'filteredLogList_',
+ this.filteredLogList_.filter(
+ (log) =>
+ (log.text.match(this.currentFilter) ||
+ log.file.match(this.currentFilter))));
},
/**
@@ -187,7 +273,8 @@ Polymer({
* @private
*/
onGetLogMessages_(logs) {
- this.logList_ = logs.reverse().concat(this.logList_);
+ this.logList_ = logs.concat(this.logList_);
+ this.filteredLogList_ = logs.concat(this.filteredLogList_);
},
/**
@@ -196,5 +283,6 @@ Polymer({
*/
clearLogBuffer_() {
this.logList_ = [];
+ this.filteredLogList_ = [];
},
});
diff --git a/chromium/chrome/browser/resources/nearby_internals/nearby_presence_browser_proxy.js b/chromium/chrome/browser/resources/nearby_internals/nearby_presence_browser_proxy.js
index cb5e5849af3..3290fc5eaf1 100644
--- a/chromium/chrome/browser/resources/nearby_internals/nearby_presence_browser_proxy.js
+++ b/chromium/chrome/browser/resources/nearby_internals/nearby_presence_browser_proxy.js
@@ -23,6 +23,14 @@ export class NearbyPresenceBrowserProxy {
}
/**
+ * Triggers NearbyPresenceService to stop a scan if a scan is currently
+ * running.
+ */
+ SendStopScan() {
+ chrome.send('StopPresenceScan');
+ }
+
+ /**
* Tells NearbyPresenceService to sync Presence credentials.
*/
SendSyncCredentials() {
@@ -36,6 +44,10 @@ export class NearbyPresenceBrowserProxy {
SendFirstTimeFlow() {
chrome.send('FirstTimePresenceFlow');
}
+
+ ConnectToPresenceDevice(endpointId) {
+ chrome.send('ConnectToPresenceDevice', [endpointId]);
+ }
}
addSingletonGetter(NearbyPresenceBrowserProxy);
diff --git a/chromium/chrome/browser/resources/nearby_internals/nearby_ui_trigger_browser_proxy.js b/chromium/chrome/browser/resources/nearby_internals/nearby_ui_trigger_browser_proxy.js
index 793034a221a..75a6537760f 100644
--- a/chromium/chrome/browser/resources/nearby_internals/nearby_ui_trigger_browser_proxy.js
+++ b/chromium/chrome/browser/resources/nearby_internals/nearby_ui_trigger_browser_proxy.js
@@ -121,6 +121,13 @@ export class NearbyUiTriggerBrowserProxy {
}
/**
+ * Tells C++ side to trigger a Nearby Share received notification.
+ */
+ showNearbyShareReceivedNotification() {
+ chrome.send('showNearbyShareReceivedNotification');
+ }
+
+ /**
* Tells C++ side to trigger a Fast Pair error notification.
*/
notifyFastPairError() {
diff --git a/chromium/chrome/browser/resources/nearby_internals/np_list_object.html b/chromium/chrome/browser/resources/nearby_internals/np_list_object.html
index dff413e435d..7c457db8178 100644
--- a/chromium/chrome/browser/resources/nearby_internals/np_list_object.html
+++ b/chromium/chrome/browser/resources/nearby_internals/np_list_object.html
@@ -1,5 +1,5 @@
-<style>
- #type {
+<style include="shared-style">
+ .info-text {
display: inline-block;
text-align: start;
width: 100%;
@@ -7,13 +7,38 @@
#item {
padding: 6px;
+ display: flex;
+ }
+
+ #deviceInfo {
+ width: 75%;
+ height: 100%;
+ }
+
+ #buttonContainer {
+ width: 15%;
+ }
+
+ cr-button {
+ height: 35px;
+ padding: 5px;
+ width: 150px;
}
</style>
<div id="item">
- <span id="status_text_connectable"
- hidden="[[!item.connectable]]">Connectable Device</span>
- <span id="status_text_lost"
- hidden="[[item.connectable]]">Not Connectable Device</span>
- <span id="type">[[item.type]]</span>
- <span id="endpoint_id">[[item.endpoint_id]]</span>
+ <div id="deviceInfo">
+ <span id="statusTextConnectable"
+ hidden="[[!device.connectable]]">Connectable Device</span>
+ <span id="statusTextLost"
+ hidden="[[device.connectable]]">Not Connectable Device</span>
+ <span id="type" class="info-text">[[device.type]]</span>
+ <span id="endpointId"
+ class="info-text">Endpoint ID: [[device.endpoint_id]]</span>
+ <span id="actions"
+ class="info-text">Associated Actions: [[device.actions]]</span>
+ </div>
+ <div id="buttonContainer">
+ <cr-button class="internals-button"
+ on-click="onConnectClicked_">Connect</cr-button>
+ </div>
</div>
diff --git a/chromium/chrome/browser/resources/nearby_internals/np_list_object.js b/chromium/chrome/browser/resources/nearby_internals/np_list_object.js
index 6cf0905ac5e..274d34b9728 100644
--- a/chromium/chrome/browser/resources/nearby_internals/np_list_object.js
+++ b/chromium/chrome/browser/resources/nearby_internals/np_list_object.js
@@ -4,6 +4,7 @@
import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {NearbyPresenceBrowserProxy} from './nearby_presence_browser_proxy.js';
import {getTemplate} from './np_list_object.html.js';
import {PresenceDevice} from './types.js';
@@ -16,9 +17,21 @@ Polymer({
/**
* Type: {!PresenceDevice}
*/
- PresenceDevice: {
+ device: {
type: Object,
},
},
+ /**
+ * Set |browserProxy_|.
+ * @override
+ */
+ created() {
+ this.browserProxy_ = NearbyPresenceBrowserProxy.getInstance();
+ },
+
+ onConnectClicked_() {
+ this.browserProxy_.ConnectToPresenceDevice(this.device.endpoint_id);
+ },
+
});
diff --git a/chromium/chrome/browser/resources/nearby_internals/shared_style.css b/chromium/chrome/browser/resources/nearby_internals/shared_style.css
index 6958db63c8a..0237dc2edad 100644
--- a/chromium/chrome/browser/resources/nearby_internals/shared_style.css
+++ b/chromium/chrome/browser/resources/nearby_internals/shared_style.css
@@ -12,6 +12,7 @@
cursor: default;
font-family: monospace;
font-size: 12px;
+ --standard-border: 1px solid black;
}
.internals-button {
@@ -20,3 +21,167 @@
font-family: Roboto;
margin: 5px;
}
+
+.flex {
+ flex: 1;
+}
+
+html, body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ font-family: "Roboto", sans-serif;
+ display: flex;
+ flex-direction: row;
+}
+
+header {
+ min-height: 50px;
+ font-size: 24px;
+ background-color: #069BDE;
+ display: flex;
+ padding: 0 20px;
+ color: #f4f4f4;
+ text-align: center;
+ align-items: center;
+ border-bottom: 1px solid rgba(0,0,0,0.12);
+}
+
+.hidden {
+ display: none;
+}
+
+cr-button {
+ height: 35px;
+ padding: 5px;
+ width: 150px;
+}
+
+#buttons {
+ margin-top: 5px;
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+#executeButton {
+ margin-bottom: 0px;
+ padding-bottom: 6px;
+}
+
+/** CSS for controls panel */
+#controls {
+ width: 40%;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.logo {
+ font-size: 30px;
+ margin-right: 12px;
+}
+
+.controls-panel {
+ padding: 40px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.control {
+ margin: 5px;
+ padding: 20px;
+ box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.2),
+ 0 3px 10px 0 rgba(0, 0, 0, 0.2),
+ 4px 4px 6px 0 rgba(0, 0, 0, 0.5)
+}
+
+.control-title {
+ font-size: 18px;
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+#actions {
+ display: flex;
+ flex-direction: row;
+}
+
+np-object:last-child {
+ border-bottom: var(--standard-border);
+}
+
+np-object {
+ border-inline-end: var(--standard-border);
+ border-inline-start: var(--standard-border);
+ border-top: var(--standard-border);
+}
+
+/** CSS for logs panel */
+#logsPanel {
+ width: 60%;
+ height: 100%;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ border-left: 1px solid rgba(0,0,0,0.12);
+}
+
+#devicesSection {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ padding: 6px;
+ white-space: pre-wrap;
+}
+
+.select-div {
+ padding: 6px;
+ width: 30%;
+ display: flex;
+ flex-direction: column;
+}
+
+#logType, #logFilters {
+ width: 50%;
+ display: flex;
+ flex-direction: column;
+}
+
+#logControlSplit {
+ display: flex;
+ flex-direction: row;
+}
+
+label {
+ width: 80%;
+}
+
+input {
+ width: 10%;
+}
+
+#logSearch {
+ margin-left: 5px;
+ width: 75%;
+}
+
+#logSearchLabel {
+ width: 15%;
+}
+
+#filterApplyDiv {
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+log-object {
+ border-inline-end: var(--standard-border);
+ border-inline-start: var(--standard-border);
+ border-top: var(--standard-border);
+}
+
+log-object:last-child {
+ border-bottom: var(--standard-border);
+}
diff --git a/chromium/chrome/browser/resources/nearby_internals/types.js b/chromium/chrome/browser/resources/nearby_internals/types.js
index 98d180b83c1..09355b1188e 100644
--- a/chromium/chrome/browser/resources/nearby_internals/types.js
+++ b/chromium/chrome/browser/resources/nearby_internals/types.js
@@ -3,6 +3,30 @@
// found in the LICENSE file.
/**
+ * Enum of values to use for the feature select dropdown. If a new feature is
+ * added, add it here.
+ * @enum {number}
+ */
+export const FeatureValues = {
+ NearbyShare: 0,
+ NearbyConnections: 1,
+ NearbyPresence: 2,
+ FastPair: 3,
+};
+
+/**
+ * Enum of values to use for the action select dropdown. If a new action is
+ * added, add it here.
+ * @enum {number}
+ */
+export const ActionValues = {
+ STARTSCAN: 0,
+ STOPSCAN: 1,
+ SYNCCREDENTIALS: 2,
+ FIRSTTIMEFLOW: 3,
+};
+
+/**
* Severity enum based on LogMessage format. Needs to stay in sync with the
* NearbyInternalsLogsHandler.
* @enum {number}
@@ -19,6 +43,7 @@ export const Severity = {
* chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.cc:
* LogMessageToDictionary()
* @typedef {{text: string,
+ * feature: FeatureValues,
* time: string,
* file: string,
* line: number,
@@ -105,7 +130,8 @@ export let TimestampedMessage;
* during testing.
* @typedef {{connectable: boolean,
* type: string,
- * endpoint_id: string}}
+ * endpoint_id: string,
+ * actions: string}}
*/
export let PresenceDevice;
@@ -156,3 +182,11 @@ export let NearbyShareStates;
* getLogMessages: function(): Promise<!Array<!LogMessage>> }}
*/
export let LogProvider;
+
+/**
+ * Select object is used by the arrays which populate the actions drop down with
+ * a list of actions specific to each feature.
+ * @typedef {{name: string,
+ * value: string}}
+ */
+export let SelectOption;
diff --git a/chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.html b/chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.html
index f0a5f89e745..2e5b5908a98 100644
--- a/chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.html
+++ b/chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.html
@@ -109,6 +109,12 @@
</div>
<div class="buttons">
<cr-button class="internals-button"
+ on-click="onNearbyShareReceivedNotificationClicked_">
+ Nearby Share Received Notification
+ </cr-button>
+ </div>
+ <div class="buttons">
+ <cr-button class="internals-button"
on-click="onFastPairErrorNotificationClicked_">
Fast Pair Error Notification
</cr-button>
diff --git a/chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.js b/chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.js
index c3866dbc9c6..900746f6bbd 100644
--- a/chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.js
+++ b/chromium/chrome/browser/resources/nearby_internals/ui_trigger_tab.js
@@ -253,6 +253,15 @@ Polymer({
},
/**
+ * Triggers ShowNearbyShareReceivedNotification which displays a Nearby Share
+ * "Received" notification.
+ * @private
+ */
+ onNearbyShareReceivedNotificationClicked_() {
+ this.browserProxy_.showNearbyShareReceivedNotification();
+ },
+
+ /**
* Updates |selectedShareTargetId_| with the new selected option.
* @private
*/
diff --git a/chromium/chrome/browser/resources/nearby_share/BUILD.gn b/chromium/chrome/browser/resources/nearby_share/BUILD.gn
index aadec04e4b7..c7a534264b8 100644
--- a/chromium/chrome/browser/resources/nearby_share/BUILD.gn
+++ b/chromium/chrome/browser/resources/nearby_share/BUILD.gn
@@ -22,6 +22,8 @@ build_webui("build") {
ts_deps = [
"./shared:build_ts",
+ "//third_party/cros-components:cros_components_ts",
+ "//third_party/material_web_components:library",
"//third_party/polymer/v3_0:library",
"//ui/webui/resources/cr_components/color_change_listener:build_ts",
"//ui/webui/resources/cr_elements:build_ts",
diff --git a/chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.html b/chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.html
index b729d1e6929..57a4105f200 100644
--- a/chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.html
+++ b/chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.html
@@ -14,17 +14,26 @@
}
#confirmationToken {
- color: var(--cros-text-color-disabled);
flex-grow: 1;
- font-size: 9px;
- letter-spacing: 0.3px;
- line-height: 12px;
margin-top: 46px;
padding-top: 6px;
text-align: center;
}
- cr-lottie {
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #confirmationToken {
+ color: var(--cros-text-color-disabled);
+ font-size: 9px;
+ letter-spacing: 0.3px;
+ line-height: 12px;
+ }
+
+ :host-context(body.jelly-enabled) #confirmationToken {
+ font: var(--cros-label-2-font);
+ color: var(--cros-sys-secondary);
+ }
+
+ #animation {
bottom: 0;
height: 100%;
left: 0;
@@ -115,13 +124,14 @@
</div>
</div>
+ <!-- TODO(b/279623883): Remove dark mode handling. -->
<iron-media-query query="(prefers-color-scheme: dark)"
query-matches="{{isDarkModeActive_}}">
</iron-media-query>
- <template is="dom-if" if="[[!errorTitle_]]">
- <cr-lottie animation-url="[[getAnimationUrl_(isDarkModeActive_)]]"
- autoplay="true">
- </cr-lottie>
+ <template is="dom-if" if="[[!errorTitle_]]" restamp>
+ <cros-lottie-renderer id="animation" asset-url="[[getAnimationUrl_(isDarkModeActive_, isJellyEnabled_)]]"
+ autoplay dynamic aria-hidden>
+ </cros-lottie-renderer>
</template>
<template is="dom-if" if="[[contactName_(shareTarget, errorTitle_)]]">
diff --git a/chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.ts b/chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.ts
index 216dda6c869..3cc8ebaedfa 100644
--- a/chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.ts
+++ b/chromium/chrome/browser/resources/nearby_share/nearby_confirmation_page.ts
@@ -11,6 +11,7 @@
import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
import 'chrome://resources/cr_elements/cr_lottie/cr_lottie.js';
+import 'chrome://resources/cros_components/lottie_renderer/lottie-renderer.js';
import 'chrome://resources/polymer/v3_0/iron-media-query/iron-media-query.js';
import '/shared/nearby_page_template.js';
import '/shared/nearby_preview.js';
@@ -20,6 +21,7 @@ import './strings.m.js';
import {ConfirmationManagerInterface, PayloadPreview, ShareTarget, TransferStatus, TransferUpdateListenerInterface, TransferUpdateListenerPendingReceiver, TransferUpdateListenerReceiver} from '/shared/nearby_share.mojom-webui.js';
import {CloseReason} from '/shared/types.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getDiscoveryManager} from './discovery_manager.js';
@@ -48,6 +50,8 @@ class TransferUpdateListener implements TransferUpdateListenerInterface {
}
}
+// TODO(TODO(b/279623883): Remove dark mode handling.
+
/**
* The progress bar asset URL for light mode
*/
@@ -58,6 +62,11 @@ const PROGRESS_BAR_URL_LIGHT: string = 'nearby_share_progress_bar_light.json';
*/
const PROGRESS_BAR_URL_DARK: string = 'nearby_share_progress_bar_dark.json';
+/**
+ * The progress bar asset URL for jelly mode.
+ */
+const PROGRESS_BAR_URL_JELLY: string = 'nearby_share_progress_bar_jelly.json';
+
const NearbyConfirmationPageElementBase = I18nMixin(PolymerElement);
@@ -166,6 +175,18 @@ export class NearbyConfirmationPageElement extends
value: false,
},
+ /**
+ * Return true if the Jelly feature flag is enabled.
+ */
+ isJellyEnabled_: {
+ type: Boolean,
+ readOnly: true,
+ value() {
+ return loadTimeData.valueExists('isJellyEnabled') &&
+ loadTimeData.getBoolean('isJellyEnabled');
+ },
+ },
+
};
}
@@ -180,6 +201,7 @@ export class NearbyConfirmationPageElement extends
private needsConfirmation_: boolean;
private lastTransferStatus_: TransferStatus;
private isDarkModeActive_: boolean;
+ private isJellyEnabled_: boolean;
private transferUpdateListener_: TransferUpdateListener|null = null;
@@ -347,6 +369,10 @@ export class NearbyConfirmationPageElement extends
* progress bar.
*/
private getAnimationUrl_(): string {
+ if (this.isJellyEnabled_) {
+ return PROGRESS_BAR_URL_JELLY;
+ }
+
return this.isDarkModeActive_ ? PROGRESS_BAR_URL_DARK :
PROGRESS_BAR_URL_LIGHT;
}
diff --git a/chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.html b/chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.html
index eac8178c1c6..eebacce2e7f 100644
--- a/chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.html
+++ b/chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.html
@@ -28,36 +28,74 @@
align-items: flex-start;
display: flex;
flex-direction: column;
+ }
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #error {
font-size: 12px;
}
- #errorTitle {
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #errorTitle {
color: var(--cros-text-color-alert);
font-weight: bold;
margin: 3px 0;
}
- #errorDescription {
+ :host-context(body.jelly-enabled) #errorTitle {
+ color: var(--cros-sys-error);
+ font: var(--cros-button-2-font);
+ margin-bottom: 3px;
+ }
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #errorDescription {
color: var(--cros-text-color-secondary);
line-height: 13px;
}
+ :host-context(body.jelly-enabled) #errorDescription {
+ color: var(--cros-sys-secondary);
+ font: var(--cros-annotation-2-font);
+ }
+
#errorIcon {
- fill: var(--cros-icon-color-alert);
flex-shrink: 0;
- height: 20px;
+ }
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #errorIcon {
+ fill: var(--cros-icon-color-alert);
margin-inline-end: 12px;
+ height: 20px;
width: 20px;
}
+ :host-context(body.jelly-enabled) #errorIcon {
+ fill: var(--cros-sys-error);
+ margin-inline-end: 8px;
+ margin-top: 2px;
+ height: 16px;
+ width: 16px;
+ }
+
#help {
align-items: flex-start;
- color: var(--cros-text-color-secondary);
display: flex;
flex-direction: row;
+ margin-block-end: 8px;
+ }
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #help {
+ color: var(--cros-text-color-secondary);
font-size: 9px;
line-height: 12px;
- margin-block-end: 8px;
+ }
+
+ :host-context(body.jelly-enabled) #help {
+ color: var(--cros-sys-secondary);
+ font: var(--cros-annotation-2-font);
}
#helpText {
@@ -71,19 +109,39 @@
#infoIcon {
flex-shrink: 0;
- height: 20px;
+ }
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #infoIcon {
margin-inline-end: 12px;
+ height: 20px;
width: 20px;
}
+ :host-context(body.jelly-enabled) #infoIcon {
+ fill: var(--cros-sys-secondary);
+ margin-inline-end: 8px;
+ height: 16px;
+ width: 16px;
+ }
+
#process-row {
align-items: flex-start;
display: flex;
flex-grow: 1;
justify-content: space-between;
+ overflow: hidden;
+ overflow-y: scroll;
+ }
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #process-row {
margin-block-end: 24px;
margin-block-start: 24px;
- overflow: hidden;
+ }
+
+ :host-context(body.jelly-enabled) #process-row {
+ margin-block-end: 40px;
}
.device-list-container {
@@ -94,7 +152,19 @@
width: 200px;
}
- cr-lottie {
+ .device-list-container#selfShareDevices {
+ margin-bottom: 3px;
+ }
+
+ .device-list-container#nonSelfShareDevices {
+ margin-top: 0px;
+ }
+
+ #deviceLists {
+ flex-direction: column;
+ }
+
+ #animation {
bottom: 0;
height: 100px;
left: 0;
@@ -105,10 +175,19 @@
#placeholder {
align-self: center;
+ margin-inline-end: var(--nearby-page-space-large-inline);
+ }
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #placeholder {
color: var(--cros-text-color-secondary);
font-size: 13px;
line-height: 20px;
- margin-inline-end: var(--nearby-page-space-large-inline);
+ }
+
+ :host-context(body.jelly-enabled) #placeholder {
+ color: var(--cros-sys-on_surface);
+ font: var(--cros-body-2-font);
}
nearby-device {
@@ -116,6 +195,20 @@
animation: 200ms linear fade-in;
}
+ .device-list-container nearby-device:first-of-type {
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+ border-top-style: solid;
+ margin-top: 3px;
+ }
+
+ .device-list-container nearby-device:last-of-type {
+ border-bottom-left-radius: 8px;
+ border-bottom-right-radius: 8px;
+ border-bottom-style: solid;
+ margin-bottom: 3px;
+ }
+
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
@@ -136,12 +229,13 @@
cancel-button-event-name="close"
close-only="[[errorTitle_]]">
<div id="centerContent" slot="content">
+ <!-- TODO(b/279623883): Remove dark mode handling. -->
<iron-media-query query="(prefers-color-scheme: dark)"
query-matches="{{isDarkModeActive_}}">
</iron-media-query>
- <cr-lottie animation-url="[[getAnimationUrl_(isDarkModeActive_)]]"
- autoplay="true">
- </cr-lottie>
+ <cros-lottie-renderer id="animation" asset-url="[[getAnimationUrl_(isDarkModeActive_, isJellyEnabled_)]]"
+ autoplay dynamic aria-hidden>
+ </cros-lottie-renderer>
<div id="process-row">
<nearby-preview payload-preview="[[payloadPreview]]"
disabled="[[errorTitle_]]">
@@ -152,21 +246,53 @@
$i18n{nearbyShareDiscoveryPagePlaceholder}
</div>
<template is="dom-if" if="[[!isShareTargetsEmpty_(shareTargets_.*)]]">
- <div class="device-list-container" aria-live="polite">
- <array-selector id="selector" items="{{shareTargets_}}"
- selected-item="{{selectedShareTarget}}">
- </array-selector>
- <template is="dom-repeat" items="[[shareTargets_]]" id="deviceList">
- <nearby-device tabindex$="[[getTabIndexOfShareTarget_(item, selectedShareTarget, shareTargets_.*)]]"
- share-target="[[item]]"
- is-selected="[[isShareTargetSelected_(item, selectedShareTarget)]]"
- role="radio"
- aria-checked$="[[isShareTargetSelectedToString_(item, selectedShareTarget)]]"
- on-click="onShareTargetClicked_"
- on-keydown="onKeyDownForShareTarget_">
- </nearby-device>
- </template>
- </div>
+ <template is="dom-if" if="[[showSelfShareUi_(isSelfShareEnabled)]]">
+ <div id="deviceLists">
+ <array-selector class="selector" items="{{shareTargets_}}" selected-item="{{selectedShareTarget}}">
+ </array-selector>
+ <div id="selfShareDevices" class="device-list-container" aria-live="polite">
+ <template is="dom-repeat" items="[[selfShareTargets_]]">
+ <nearby-device tabindex$="[[getTabIndexOfShareTarget_(item, selectedShareTarget, shareTargets_.*)]]"
+ share-target="[[item]]"
+ is-selected="[[isShareTargetSelected_(item, selectedShareTarget)]]"
+ role="radio"
+ aria-checked$="[[isShareTargetSelectedToString_(item, selectedShareTarget)]]"
+ on-click="onShareTargetClicked_"
+ on-keydown="onKeyDownForShareTarget_">
+ </nearby-device>
+ </template>
+ </div>
+ <div id="nonSelfShareDevices" class="device-list-container" aria-live="polite">
+ <template is="dom-repeat" items="[[nonSelfShareTargets_]]">
+ <nearby-device tabindex$="[[getTabIndexOfShareTarget_(item, selectedShareTarget, shareTargets_.*)]]"
+ share-target="[[item]]"
+ is-selected="[[isShareTargetSelected_(item, selectedShareTarget)]]"
+ role="radio"
+ aria-checked$="[[isShareTargetSelectedToString_(item, selectedShareTarget)]]"
+ on-click="onShareTargetClicked_"
+ on-keydown="onKeyDownForShareTarget_">
+ </nearby-device>
+ </template>
+ </div>
+ </div>
+ </template>
+ <template is="dom-if" if="[[!showSelfShareUi_(isSelfShareEnabled)]]">
+ <div class="device-list-container" aria-live="polite">
+ <array-selector class="selector" items="{{shareTargets_}}"
+ selected-item="{{selectedShareTarget}}">
+ </array-selector>
+ <template is="dom-repeat" items="[[shareTargets_]]">
+ <nearby-device tabindex$="[[getTabIndexOfShareTarget_(item, selectedShareTarget, shareTargets_.*)]]"
+ share-target="[[item]]"
+ is-selected="[[isShareTargetSelected_(item, selectedShareTarget)]]"
+ role="radio"
+ aria-checked$="[[isShareTargetSelectedToString_(item, selectedShareTarget)]]"
+ on-click="onShareTargetClicked_"
+ on-keydown="onKeyDownForShareTarget_">
+ </nearby-device>
+ </template>
+ </div>
+ </template>
</template>
</template>
</div>
diff --git a/chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.ts b/chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.ts
index b523329b0c6..506caff4dd0 100644
--- a/chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.ts
+++ b/chromium/chrome/browser/resources/nearby_share/nearby_discovery_page.ts
@@ -9,6 +9,7 @@
import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_lottie/cr_lottie.js';
+import 'chrome://resources/cros_components/lottie_renderer/lottie-renderer.js';
import 'chrome://resources/polymer/v3_0/iron-media-query/iron-media-query.js';
import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
import '/shared/nearby_device.js';
@@ -42,6 +43,8 @@ function tokensEqual(a: UnguessableToken, b: UnguessableToken): boolean {
return a.high === b.high && a.low === b.low;
}
+// TODO(TODO(b/279623883): Remove dark mode handling.
+
/**
* The pulse animation asset URL for light mode.
*/
@@ -54,6 +57,12 @@ const PULSE_ANIMATION_URL_LIGHT: string =
const PULSE_ANIMATION_URL_DARK: string =
'nearby_share_pulse_animation_dark.json';
+/**
+ * The pulse animation asset URL for jelly mode.
+ */
+const PULSE_ANIMATION_URL_JELLY: string =
+ 'nearby_share_pulse_animation_jelly.json';
+
const NearbyDiscoveryPageElementBase = I18nMixin(PolymerElement);
@@ -157,6 +166,19 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
type: Boolean,
value: false,
},
+
+ /**
+ * Return true if the Jelly feature flag is enabled.
+ */
+ isJellyEnabled_: {
+ type: Boolean,
+ readOnly: true,
+ value() {
+ return loadTimeData.valueExists('isJellyEnabled') &&
+ loadTimeData.getBoolean('isJellyEnabled');
+ },
+ },
+
/**
* Return true if the Nearby Share Self Share feature flag is enabled.
*/
@@ -183,6 +205,7 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
private errorTitle_: string|null;
private errorDescription_: string|null;
private isDarkModeActive_: boolean;
+ private isJellyEnabled_: boolean;
private mojoEventTarget_: ShareTargetListenerCallbackRouter|null = null;
private listenerIds_: number[]|null = null;
@@ -360,7 +383,12 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
* @param index in |shareTargets_| and also the dom-repeat list.
*/
private focusShareTarget_(index: number) {
- const container = this.shadowRoot!.querySelector('.device-list-container');
+ let container;
+ if (this.isSelfShareEnabled) {
+ container = this.shadowRoot!.querySelector('#deviceLists');
+ } else {
+ container = this.shadowRoot!.querySelector('.device-list-container');
+ }
assert(container);
const nearbyDeviceElements = container.querySelectorAll('nearby-device');
@@ -390,7 +418,7 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
}
this.selectedShareTarget = shareTarget;
- const selector = this.shadowRoot!.querySelector<ArraySelector>('#selector');
+ const selector = this.shadowRoot!.querySelector<ArraySelector>('.selector');
assert(selector);
selector.select(this.selectedShareTarget);
}
@@ -404,38 +432,40 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
}
private onShareTargetDiscovered_(shareTarget: ShareTarget) {
- const shareTargetId = tokenToString(shareTarget.id);
assert(this.shareTargetMap_);
- if (!this.shareTargetMap_.has(shareTargetId)) {
- if (this.isSelfShareEnabled) {
- if (shareTarget.forSelfShare) {
- this.push('selfShareTargets_', shareTarget);
- } else {
- this.push('nonSelfShareTargets_', shareTarget);
- }
- this.shareTargets_ =
- this.selfShareTargets_.concat(this.nonSelfShareTargets_);
+ if (this.isSelfShareEnabled) {
+ if (shareTarget.forSelfShare) {
+ this.updateShareTargetList_(
+ this.selfShareTargets_, 'selfShareTargets_', shareTarget);
} else {
- this.push('shareTargets_', shareTarget);
+ this.updateShareTargetList_(
+ this.nonSelfShareTargets_, 'nonSelfShareTargets_', shareTarget);
}
-
+ this.shareTargets_ =
+ this.selfShareTargets_.concat(this.nonSelfShareTargets_);
} else {
- const index = this.shareTargets_.findIndex(
+ this.updateShareTargetList_(
+ this.shareTargets_, 'shareTargets_', shareTarget);
+ }
+ this.shareTargetMap_.set(tokenToString(shareTarget.id), shareTarget);
+ }
+
+ private updateShareTargetList_(
+ shareTargetList: ShareTarget[], shareTargetListString: string,
+ shareTarget: ShareTarget) {
+ assert(this.shareTargetMap_);
+ if (this.shareTargetMap_.has(tokenToString(shareTarget.id))) {
+ const index = shareTargetList.findIndex(
(target) => tokensEqual(target.id, shareTarget.id));
assert(index !== -1);
- this.splice('shareTargets_', index, 1, shareTarget);
+ this.splice(shareTargetListString, index, 1, shareTarget);
this.updateSelectedShareTarget_(shareTarget.id, shareTarget);
+ } else {
+ this.push(shareTargetListString, shareTarget);
}
- this.shareTargetMap_.set(shareTargetId, shareTarget);
}
private onShareTargetLost_(shareTarget: ShareTarget) {
- // Remove target from `shareTargets_`.
- const shareTargetsIdx = this.shareTargets_.findIndex(
- (target) => tokensEqual(target.id, shareTarget.id));
- assert(shareTargetsIdx !== -1);
- this.splice('shareTargets_', shareTargetsIdx, 1);
-
if (this.isSelfShareEnabled) {
if (shareTarget.forSelfShare) {
// Remove target from `selfShareTargets_`.
@@ -450,6 +480,16 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
assert(index !== -1);
this.splice('nonSelfShareTargets_', index, 1);
}
+
+ this.set(
+ 'shareTargets_',
+ this.selfShareTargets_.concat(this.nonSelfShareTargets_));
+ } else {
+ // Remove target from `shareTargets_`.
+ const shareTargetsIdx = this.shareTargets_.findIndex(
+ (target) => tokensEqual(target.id, shareTarget.id));
+ assert(shareTargetsIdx !== -1);
+ this.splice('shareTargets_', shareTargetsIdx, 1);
}
assert(this.shareTargetMap_);
@@ -490,7 +530,7 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
return;
}
- const selector = this.shadowRoot!.querySelector<ArraySelector>('#selector');
+ const selector = this.shadowRoot!.querySelector<ArraySelector>('.selector');
assert(selector);
this.selectedShareTarget = selector.selectedItem as (ShareTarget | null);
}
@@ -512,7 +552,7 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
tokensEqual(this.selectedShareTarget.id, id)) {
this.selectedShareTarget = shareTarget;
const selector =
- this.shadowRoot!.querySelector<ArraySelector>('#selector');
+ this.shadowRoot!.querySelector<ArraySelector>('.selector');
assert(selector);
selector.select(this.selectedShareTarget);
}
@@ -526,10 +566,28 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
* so users will not navigate to by tab.
*/
private getTabIndexOfShareTarget_(shareTarget: ShareTarget|null): string {
- if ((!this.selectedShareTarget && shareTarget === this.shareTargets_[0]) ||
- (shareTarget === this.selectedShareTarget)) {
+ if (this.selectedShareTarget && shareTarget === this.selectedShareTarget) {
+ return '0';
+ }
+
+ if (this.isSelfShareEnabled) {
+ if (this.selfShareTargets_.length !== 0 &&
+ shareTarget === this.selfShareTargets_[0]) {
+ return '0';
+ }
+
+ if (this.nonSelfShareTargets_.length !== 0 &&
+ shareTarget === this.nonSelfShareTargets_[0]) {
+ return '0';
+ }
+
+ return '-1';
+ }
+
+ if (shareTarget === this.shareTargets_[0]) {
return '0';
}
+
return '-1';
}
@@ -596,9 +654,22 @@ export class NearbyDiscoveryPageElement extends NearbyDiscoveryPageElementBase {
* pulsing background animation
*/
private getAnimationUrl_(): string {
+ if (this.isJellyEnabled_) {
+ return PULSE_ANIMATION_URL_JELLY;
+ }
+
+ // TODO(b/279623883): Clean up dark mode logic and duplicate assets after
+ // Jelly is launched.
return this.isDarkModeActive_ ? PULSE_ANIMATION_URL_DARK :
PULSE_ANIMATION_URL_LIGHT;
}
+
+ /**
+ * Returns a boolean indicating whether to show Self Share UI.
+ */
+ private showSelfShareUi_(): boolean {
+ return this.isSelfShareEnabled;
+ }
}
customElements.define(
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/BUILD.gn b/chromium/chrome/browser/resources/nearby_share/shared/BUILD.gn
index f2a4f4bf973..456c281a686 100644
--- a/chromium/chrome/browser/resources/nearby_share/shared/BUILD.gn
+++ b/chromium/chrome/browser/resources/nearby_share/shared/BUILD.gn
@@ -68,7 +68,9 @@ generate_grd("build_json_grdp") {
input_files = [
"nearby_share_progress_bar_dark.json",
"nearby_share_progress_bar_light.json",
+ "nearby_share_progress_bar_jelly.json",
"nearby_share_pulse_animation_dark.json",
"nearby_share_pulse_animation_light.json",
+ "nearby_share_pulse_animation_jelly.json",
]
}
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.html b/chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.html
index fce5c671228..8d848938650 100644
--- a/chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.html
+++ b/chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.html
@@ -160,6 +160,7 @@
#contactList {
border-top: var(--cr-separator-line);
padding-block-start: 8px;
+ padding-right: 12px;
}
#contactsHeading {
@@ -223,14 +224,46 @@
flex: 1;
justify-content: space-between;
}
+
+ #nearbyVisibilityAllContactsToggle {
+ display: flex;
+ flex-direction: row;
+ flex: 1;
+ justify-content: space-between;
+ margin-block-end: 8px;
+ margin-block-start: 8px;
+ }
</style>
<div id="main">
<cr-radio-group id="visibilityRadioGroup"
disabled="[[disableRadioGroup_(contactsState)]]"
selected="{{selectedVisibility}}">
+ <template is="dom-if" if="[[showSelfShareUi_(isSelfShareEnabled_)]]">
+ <cr-card-radio-button id="contacts" class="flex" name="contacts"
+ aria-labelledby="contactsLabel" aria-describedby="explanation">
+ <div class="card-contents">
+ <iron-icon icon="nearby20:contact-all" class="card-icon">
+ </iron-icon>
+ <div id="contactsLabel" class="card-label" aria-hidden="true">
+ $i18n{nearbyShareContactVisiblityContactsButton}
+ </div>
+ </div>
+ </cr-card-radio-button>
+ <cr-card-radio-button id="yourDevices" class="flex" name="yourDevices"
+ aria-labelledby="yourDevicesLabel" aria-describedby="explanation">
+ <div class="card-contents">
+ <iron-icon icon="nearby20:your-devices" class="card-icon">
+ </iron-icon>
+ <div id="yourDevicesLabel" class="card-label" aria-hidden="true">
+ $i18n{nearbyShareContactVisibilityYourDevices}
+ </div>
+ </div>
+ </cr-card-radio-button>
+ </template>
+ <template is="dom-if" if="[[!showSelfShareUi_(isSelfShareEnabled_)]]">
<cr-card-radio-button id="allContacts" class="flex" name="all"
- aria-labelledby="allContactsLabel">
+ aria-labelledby="allContactsLabel" aria-describedby="explanation">
<div class="card-contents">
<iron-icon icon="nearby20:contact-all" class="card-icon">
</iron-icon>
@@ -240,7 +273,7 @@
</div>
</cr-card-radio-button>
<cr-card-radio-button id="someContacts" class="flex" name="some"
- aria-labelledby="someContactsLabel">
+ aria-labelledby="someContactsLabel" aria-describedby="explanation">
<div class="card-contents">
<iron-icon icon="nearby20:contact-group" class="card-icon">
</iron-icon>
@@ -249,8 +282,9 @@
</div>
</div>
</cr-card-radio-button>
+ </template>
<cr-card-radio-button id="noContacts" class="flex" name="none"
- aria-labelledby="noContactsLabel">
+ aria-labelledby="noContactsLabel" aria-describedby="explanation">
<div class="card-contents">
<iron-icon icon="nearby20:visibility-off" class="card-icon">
</iron-icon>
@@ -327,7 +361,7 @@
contactsState)]]">
<div id="explanation">
<div class="cr-secondary-text"
- inner-h-t-m-l="[[getVisibilityDescription_(selectedVisibility)]]">
+ inner-h-t-m-l="[[getVisibilityDescription_(selectedVisibility, isSelectedContactsToggledOn_, profileEmail)]]">
</div>
</div>
</template>
@@ -337,6 +371,19 @@
if="[[showContactList_(selectedVisibility,
contactsState)]]">
<div id="contactList">
+ <!-- All Contacts toggle is shown only if the Contacts tab is selected and Self Share is enabled -->
+ <template is="dom-if" if="[[showAllContactsToggle_(selectedVisibility, contactsState)]]">
+ <div id="nearbyVisibilityAllContactsToggle">
+ <div id="nearbyVisibilityAllContactsToggleTitle">
+ Visible to all contacts
+ </div>
+ <cr-toggle id="AllContactsToggle"
+ checked="[[isAllContactsToggledOn_]]"
+ on-change="toggleAllContacts_"
+ aria-describedby="nearbyVisibilityAllContactsToggleTitle">
+ </cr-toggle>
+ </div>
+ </template>
<template is="dom-if"
if="[[showUnreachableContactsMessage_(
numUnreachable_)]]">
@@ -372,13 +419,11 @@
</div>
</div>
<template is="dom-if"
- if="[[showContactCheckBoxes_(selectedVisibility)]]">
+ if="[[showContactCheckBoxes_(isAllContactsToggledOn_, selectedVisibility)]]">
<cr-toggle class="contact-toggle"
checked="{{item.checked}}"
aria-labelledby$="contact-name-[[itemsIndex]]"
- aria-describedby$="contact-description-[[itemsIndex]]"
- disabled="[[!isVisibility_(
- selectedVisibility,'some')]]">
+ aria-describedby$="contact-description-[[itemsIndex]]">
</cr-toggle>
</template>
</div>
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.ts b/chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.ts
index faabed3e5ad..bf02cbc9c2c 100644
--- a/chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.ts
+++ b/chromium/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.ts
@@ -41,39 +41,6 @@ enum ContactsState {
ZERO_CONTACTS = 'zerocontacts',
}
-/**
- * Maps visibility string to the mojo enum
- */
-function visibilityStringToValue(visibilityString: string|null): Visibility|
- null {
- switch (visibilityString) {
- case 'all':
- return Visibility.kAllContacts;
- case 'some':
- return Visibility.kSelectedContacts;
- case 'none':
- return Visibility.kNoOne;
- default:
- return null;
- }
-}
-
-/**
- * Maps visibility mojo enum to a string for the radio button selection
- */
-function visibilityValueToString(visibility: Visibility|null): string|null {
- switch (visibility) {
- case Visibility.kAllContacts:
- return 'all';
- case Visibility.kSelectedContacts:
- return 'some';
- case Visibility.kNoOne:
- return 'none';
- default:
- return null;
- }
-}
-
function isHtmlAnchorElement(node: ChildNode): node is HTMLAnchorElement {
return node.nodeType === Node.ELEMENT_NODE && node.nodeName === 'A';
}
@@ -130,8 +97,10 @@ export class NearbyContactVisibilityElement extends
},
/**
- * Which of visibility setting is selected as a string or
- * null for no selection. ('all', 'some', 'none', null).
+ * Which visibility setting is selected as a string or
+ * null for no selection.
+ * If self share is enabled: ('contacts', 'yourDevices', 'none', null).
+ * If self share is disabled: ('all', 'some', 'none', null).
*/
selectedVisibility: {
type: String,
@@ -184,6 +153,33 @@ export class NearbyContactVisibilityElement extends
loadTimeData.getBoolean('isJellyEnabled');
},
},
+
+ /**
+ * Return true if the Self Share feature flag is enabled.
+ */
+ isSelfShareEnabled_: {
+ type: Boolean,
+ readOnly: true,
+ value() {
+ return loadTimeData.valueExists('isSelfShareEnabled') &&
+ loadTimeData.getBoolean('isSelfShareEnabled');
+ },
+ },
+
+ /**
+ * True if the user toggles All Contacts visibility.
+ */
+ isAllContactsToggledOn_: {
+ type: Boolean,
+ value() {
+ return true;
+ },
+ },
+
+ profileEmail: {
+ type: String,
+ value: '',
+ },
};
}
@@ -201,6 +197,8 @@ export class NearbyContactVisibilityElement extends
isVisibilitySelected: boolean;
selectedVisibility: string|null;
settings: NearbySettings|null;
+ isSelectedContactsToggled: boolean;
+ profileEmail: string;
private contactManager_: ContactManagerInterface|null;
private downloadContactsObserverReceiver_: DownloadContactsObserverReceiver|
@@ -208,6 +206,8 @@ export class NearbyContactVisibilityElement extends
private downloadTimeoutId_: number|null;
private isDarkModeActive_: boolean;
private isJellyEnabled_: boolean;
+ private isAllContactsToggledOn_: boolean;
+ private isSelfShareEnabled_: boolean;
private numUnreachable_: number;
private numUnreachableMessage_: string;
@@ -256,6 +256,70 @@ export class NearbyContactVisibilityElement extends
}
/**
+ * Maps visibility string to the mojo enum
+ */
+ private visibilityStringToValue(visibilityString: string|null): Visibility
+ |null {
+ if (this.isSelfShareEnabled_) {
+ switch (visibilityString) {
+ case 'contacts':
+ if (this.isAllContactsToggledOn_) {
+ return Visibility.kAllContacts;
+ }
+ return Visibility.kSelectedContacts;
+ case 'yourDevices':
+ return Visibility.kYourDevices;
+ case 'none':
+ return Visibility.kNoOne;
+ default:
+ return null;
+ }
+ } else {
+ switch (visibilityString) {
+ case 'all':
+ return Visibility.kAllContacts;
+ case 'some':
+ return Visibility.kSelectedContacts;
+ case 'none':
+ return Visibility.kNoOne;
+ default:
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Maps visibility mojo enum to a string for the radio button selection
+ */
+ private visibilityValueToString(visibility: Visibility|null): string|null {
+ if (this.isSelfShareEnabled_) {
+ switch (visibility) {
+ case Visibility.kAllContacts:
+ return 'contacts';
+ case Visibility.kSelectedContacts:
+ return 'contacts';
+ case Visibility.kYourDevices:
+ return 'yourDevices';
+ case Visibility.kNoOne:
+ return 'none';
+ default:
+ return null;
+ }
+ } else {
+ switch (visibility) {
+ case Visibility.kAllContacts:
+ return 'all';
+ case Visibility.kSelectedContacts:
+ return 'some';
+ case Visibility.kNoOne:
+ return 'none';
+ default:
+ return null;
+ }
+ }
+ }
+
+ /**
* Makes a mojo request to download the latest version of contacts.
*/
private downloadContacts_(): void {
@@ -337,8 +401,7 @@ export class NearbyContactVisibilityElement extends
* @return true when checkboxes should be shown for contacts.
*/
private showContactCheckBoxes_(): boolean {
- return this.selectedVisibility === 'some' ||
- this.selectedVisibility === 'none';
+ return this.getSelectedVisibility() === Visibility.kSelectedContacts;
}
/**
@@ -365,7 +428,9 @@ export class NearbyContactVisibilityElement extends
private settingsChanged_(): void {
if (this.settings && this.settings.visibility !== null) {
this.selectedVisibility =
- visibilityValueToString(this.settings.visibility);
+ this.visibilityValueToString(this.settings.visibility);
+ this.isAllContactsToggledOn_ =
+ this.settings.visibility === Visibility.kAllContacts;
} else {
this.selectedVisibility = null;
}
@@ -401,16 +466,36 @@ export class NearbyContactVisibilityElement extends
private showEmptyState_(selectedVisibility: string, contactsState: string):
boolean {
- return (selectedVisibility === 'all' || selectedVisibility === 'some') &&
- contactsState === ContactsState.ZERO_CONTACTS;
+ if (this.isSelfShareEnabled_) {
+ return selectedVisibility === 'contacts' &&
+ contactsState === ContactsState.ZERO_CONTACTS;
+ } else {
+ return (selectedVisibility === 'all' || selectedVisibility === 'some') &&
+ contactsState === ContactsState.ZERO_CONTACTS;
+ }
}
private showContactList_(selectedVisibility: string, contactsState: string):
boolean {
- return (selectedVisibility === 'all' || selectedVisibility === 'some') &&
+ if (this.isSelfShareEnabled_) {
+ return selectedVisibility === 'contacts' &&
+ contactsState === ContactsState.HAS_CONTACTS;
+ } else {
+ return (selectedVisibility === 'all' || selectedVisibility === 'some') &&
+ contactsState === ContactsState.HAS_CONTACTS;
+ }
+ }
+
+ private showAllContactsToggle_(
+ selectedVisibility: string, contactsState: ContactsState): boolean {
+ return selectedVisibility === 'contacts' &&
contactsState === ContactsState.HAS_CONTACTS;
}
+ private toggleAllContacts_(): void {
+ this.isAllContactsToggledOn_ = !this.isAllContactsToggledOn_;
+ }
+
/**
* Builds the html for the download retry message, applying the appropriate
* aria labels, and adding an event listener to the link. This function is
@@ -558,17 +643,39 @@ export class NearbyContactVisibilityElement extends
});
}
- private getVisibilityDescription_(selectedVisibility: string): TrustedHTML {
- switch (visibilityStringToValue(selectedVisibility)) {
- case Visibility.kAllContacts:
- return this.i18nAdvanced('nearbyShareContactVisibilityOwnAll');
- case Visibility.kSelectedContacts:
- return this.i18nAdvanced('nearbyShareContactVisibilityOwnSome');
- case Visibility.kNoOne:
- return this.i18nAdvanced('nearbyShareContactVisibilityOwnNone');
- default:
- assert(window.trustedTypes);
- return window.trustedTypes.emptyHTML;
+ private getVisibilityDescription_(): TrustedHTML {
+ if (this.isSelfShareEnabled_) {
+ switch (this.getSelectedVisibility()) {
+ case Visibility.kAllContacts:
+ return this.i18nAdvanced(
+ 'nearbyShareContactVisibilityOwnAllSelfShare',
+ {substitutions: [this.profileEmail]});
+ case Visibility.kSelectedContacts:
+ return this.i18nAdvanced(
+ 'nearbyShareContactVisibilityOwnSomeSelfShare',
+ {substitutions: [this.profileEmail]});
+ case Visibility.kYourDevices:
+ return this.i18nAdvanced(
+ 'nearbyShareContactVisibilityOwnYourDevices',
+ {substitutions: [this.profileEmail]});
+ case Visibility.kNoOne:
+ return this.i18nAdvanced('nearbyShareContactVisibilityOwnNone');
+ default:
+ assert(window.trustedTypes);
+ return window.trustedTypes.emptyHTML;
+ }
+ } else {
+ switch (this.getSelectedVisibility()) {
+ case Visibility.kAllContacts:
+ return this.i18nAdvanced('nearbyShareContactVisibilityOwnAll');
+ case Visibility.kSelectedContacts:
+ return this.i18nAdvanced('nearbyShareContactVisibilityOwnSome');
+ case Visibility.kNoOne:
+ return this.i18nAdvanced('nearbyShareContactVisibilityOwnNone');
+ default:
+ assert(window.trustedTypes);
+ return window.trustedTypes.emptyHTML;
+ }
}
}
@@ -576,18 +683,33 @@ export class NearbyContactVisibilityElement extends
* Save visibility setting and sync allowed contacts with contact manager.
*/
saveVisibilityAndAllowedContacts(): void {
- const visibility = visibilityStringToValue(this.selectedVisibility);
+ const visibility = this.getSelectedVisibility();
if (visibility) {
this.set('settings.visibility', visibility);
}
+ if (!this.contacts) {
+ this.contactManager_!.setAllowedContacts([]);
+ return;
+ }
+
const allowedContacts: string[] = [];
- if (this.contacts) {
- for (const contact of this.contacts) {
- if (contact.checked) {
+
+ switch (visibility) {
+ case Visibility.kAllContacts:
+ for (const contact of this.contacts) {
allowedContacts.push(contact.id);
}
- }
+ break;
+ case Visibility.kSelectedContacts:
+ for (const contact of this.contacts) {
+ if (contact.checked) {
+ allowedContacts.push(contact.id);
+ }
+ }
+ break;
+ default:
+ break;
}
this.contactManager_!.setAllowedContacts(allowedContacts);
}
@@ -597,7 +719,7 @@ export class NearbyContactVisibilityElement extends
* logging metric to avoid potential race condition
*/
getSelectedVisibility(): Visibility|null {
- return visibilityStringToValue(this.selectedVisibility);
+ return this.visibilityStringToValue(this.selectedVisibility);
}
/**
@@ -623,6 +745,13 @@ export class NearbyContactVisibilityElement extends
return this.isJellyEnabled_ ? CONTACTS_FAILED_JELLY_ICON :
CONTACTS_FAILED_ICON;
}
+
+ /**
+ * Returns a boolean indicating whether to show Self Share UI.
+ */
+ private showSelfShareUi_(): boolean {
+ return this.isSelfShareEnabled_;
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/nearby_device.html b/chromium/chrome/browser/resources/nearby_share/shared/nearby_device.html
index b9bd1abc153..58414cef579 100644
--- a/chromium/chrome/browser/resources/nearby_share/shared/nearby_device.html
+++ b/chromium/chrome/browser/resources/nearby_share/shared/nearby_device.html
@@ -1,46 +1,85 @@
<style>
#wrapper {
align-items: center;
- background-color: var(--cr-card-background-color);
- border: 1px solid var(--cros-button-stroke-color-secondary);
+ display: flex;
border-radius: 8px;
- box-sizing: border-box;
+ outline: none;
+ padding-block-end: 3px;
+ padding-block-start: 3px;
+ padding-inline-end: 9px;
+ padding-inline-start: 6px;
cursor: pointer;
- display: flex;
- height: 40px;
margin: 3px;
- padding-block-end: 6px;
- padding-block-start: 6px;
- padding-inline-end: 9px;
- padding-inline-start: 9px;
+ height: 40px;
+ box-sizing: border-box;
+ overflow: hidden;
+ }
+
+ :host {
+ background-color: var(--cr-card-background-color);
+ border: 1px solid var(--cros-button-stroke-color-secondary);
+ border-bottom-style: hidden;
+ align-items: center;
+ display: flex;
}
- :host(:focus) {
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)):host(:focus) {
outline: none;
+ box-shadow: inset 0 0 0 2px var(--cros-focus-aura-color);
+ }
+
+ :host-context(body.jelly-enabled):host(:focus) {
+ border: 2px solid var(--cros-sys-focus_ring);
}
- :host(:focus) #wrapper {
- box-shadow: 0 0 0 2px var(--cros-focus-aura-color);
+ :host-context(body.jelly-enabled):host(:hover) {
+ background-color: var(--cros-sys-hover_on_subtle);
}
- :host([is-selected]) #wrapper {
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)):host([is-selected]) {
border-color: var(--cros-button-icon-color-secondary);
}
- :host([is-selected]) #done {
- display: flex;
+ :host-context(body.jelly-enabled):host([is-selected]) {
+ background-color: var(--cros-sys-primary_container);
}
- :host([is-selected]) #name {
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)):host([is-selected]) #name {
color: var(--cros-text-color-prominent);
}
- #done {
- color: var(--cros-text-color-prominent);
+ :host-context(body.jelly-enabled):host([is-selected]) #name {
+ color: var(--cros-sys-primary);
+ }
+
+ :host-context(body.jelly-enabled):host([is-selected]) #share-target-image {
+ border: 1px solid var(--cros-sys-primary);
+ }
+
+ :host([is-selected]) #checked {
+ display: flex;
+ }
+
+ #checked {
display: none;
flex-shrink: 0;
- height: 17px;
- width: 17px;
+ height: 20px;
+ padding-left: 2px;
+ padding-top: 2px;
+ vertical-align: middle;
+ width: 20px;
+ }
+
+ :host-context(body.jelly-enabled) #checked {
+ color: var(--cros-sys-primary);
+ }
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #checked {
+ color: var(--cros-icon-color-prominent);
}
#icon {
@@ -50,6 +89,16 @@
width: var(--target-image-size);
}
+ :host-context(body.jelly-enabled) #icon {
+ --nearby-device-icon-color: var(--cros-sys-on_primary_container);
+ --nearby-device-icon-background-color: var(--cros-sys-primary_container);
+ }
+
+ :host-context(body.jelly-enabled):host([is-selected]) #icon {
+ --nearby-device-icon-color: var(--cros-sys-on_primary);
+ --nearby-device-icon-background-color: var(--cros-sys-primary);
+ }
+
#share-target-image {
border-radius: 50%;
/* Hide the image until it's downloaded. */
@@ -59,17 +108,31 @@
width: var(--target-image-size);
}
+ :host-context(body.jelly-enabled) #share-target-image {
+ /* Have a placeholder so that the image doesn't enlarge when focused. */
+ border: 1px solid var(--cr-card-background-color);
+ }
+
#name {
- color: var(--cros-text-color-secondary);
flex-grow: 1;
- font-size: 11px;
- font-weight: 500;
- letter-spacing: 0.3px;
margin-inline-start: 11px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #name {
+ color: var(--cros-text-color-secondary);
+ font-size: 11px;
+ font-weight: 500;
+ letter-spacing: 0.3px;
+ }
+
+ :host-context(body.jelly-enabled) #name {
+ color: var(--cros-sys-secondary);
+ font: var(--cros-button-2-font);
+ }
</style>
<div id="wrapper" title="[[shareTarget.name]]">
@@ -79,5 +142,5 @@
auto-src="[[getTargetImageUrl_(shareTarget)]]">
</img>
<div id="name">[[shareTarget.name]]</div>
- <iron-icon id="done" icon="nearby-share:done"></iron-icon>
+ <iron-icon id="checked" icon="nearby-share:checked"></iron-icon>
</div>
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/nearby_page_template.html b/chromium/chrome/browser/resources/nearby_share/shared/nearby_page_template.html
index 1de92daa685..808850a5838 100644
--- a/chromium/chrome/browser/resources/nearby_share/shared/nearby_page_template.html
+++ b/chromium/chrome/browser/resources/nearby_share/shared/nearby_page_template.html
@@ -49,18 +49,38 @@
}
#pageTitle {
- color: var(--cros-text-color-primary);
- /* TODO(b/279623883): Remove backup CSS properties once Jelly is launched. */
- font: var(--cros-title-1-font, normal 16px/24px 'Google Sans');
margin: 0;
}
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #pageTitle {
+ font-family: 'Google Sans';
+ font-size: 16px;
+ font-weight: normal;
+ line-height: 24px;
+ color: var(--cros-text-color-primary);
+ }
+
+ :host-context(body.jelly-enabled) #pageTitle {
+ color: var(--cros-sys-on_surface);
+ font: var(--cros-title-1-font);
+ }
+
#pageSubTitle {
+ margin: 0;
+ }
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #pageSubTitle {
color: var(--cros-text-color-secondary);
font-size: 14px;
font-weight: inherit;
line-height: 20px;
- margin: 0;
+ }
+
+ :host-context(body.jelly-enabled) #pageSubTitle {
+ color: var(--cros-sys-on_surface_variant);
+ font: var(--cros-body-2-font);
}
#a11yAnnouncedPageSubTitle {
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/nearby_preview.html b/chromium/chrome/browser/resources/nearby_share/shared/nearby_preview.html
index 49309785360..23102b07a79 100644
--- a/chromium/chrome/browser/resources/nearby_share/shared/nearby_preview.html
+++ b/chromium/chrome/browser/resources/nearby_share/shared/nearby_preview.html
@@ -13,16 +13,25 @@
}
#title {
- color: var(--cr-primary-text-color);
- font-size: 13px;
height: 60px;
letter-spacing: 0.2px;
- line-height: 20px;
overflow: hidden;
overflow-wrap: break-word;
text-align: center;
width: 116px;
}
+
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #title {
+ color: var(--cr-primary-text-color);
+ font-size: 13px;
+ line-height: 20px;
+ }
+
+ :host-context(body.jelly-enabled) #title {
+ color: var(--cros-sys-on_surface_variant);
+ font: var(--cros-body-2-font);
+ }
</style>
<iron-icon class$="[[getIconClass_(disabled)]]"
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/nearby_progress.html b/chromium/chrome/browser/resources/nearby_share/shared/nearby_progress.html
index 170e6f3c164..8e5328a285d 100644
--- a/chromium/chrome/browser/resources/nearby_share/shared/nearby_progress.html
+++ b/chromium/chrome/browser/resources/nearby_share/shared/nearby_progress.html
@@ -1,16 +1,25 @@
<style>
#device-name {
-webkit-box-orient: vertical;
- color: var(--cros-text-color-primary);
display: -webkit-box;
- letter-spacing: 0.25px;
- line-height: 154%;
overflow: hidden;
overflow-wrap: break-word;
text-align: center;
width: 116px;
}
+ /* TODO(b/279623883): Remove once Jelly is launched. */
+ :host-context(body:not(.jelly-enabled)) #device-name {
+ color: var(--cros-text-color-primary);
+ line-height: 154%;
+ letter-spacing: 0.25px;
+ }
+
+ :host-context(body.jelly-enabled) #device-name {
+ font: var(--cros-body-2-font);
+ color: var(--cros-sys-on_surface_variant);
+ }
+
#icon {
height: var(--target-image-size);
margin: auto;
@@ -46,7 +55,7 @@
/* TODO(b/279623883): Remove old colors when Jelly is launched. */
#wheel {
fill: none;
- stroke: var(--cros-sys-progress, var(--cros-icon-color-prominent));
+ stroke: var(--cros-sys-primary, var(--cros-icon-color-prominent));
stroke-dasharray: 100;
stroke-linecap: round;
stroke-width: 2px;
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/nearby_share_progress_bar_jelly.json b/chromium/chrome/browser/resources/nearby_share/shared/nearby_share_progress_bar_jelly.json
new file mode 100644
index 00000000000..990a464620e
--- /dev/null
+++ b/chromium/chrome/browser/resources/nearby_share/shared/nearby_share_progress_bar_jelly.json
@@ -0,0 +1 @@
+{"layers":[{"nm":"dotted line Outlines 3","ddd":0,"ty":4,"ind":1,"sr":1,"ip":0,"op":150,"st":0,"ks":{"a":{"k":[640,360,0],"ix":1,"a":0},"p":{"k":[459.5,322.5,0],"ix":2,"a":0},"s":{"k":[200,200,100],"ix":6,"a":0},"r":{"k":0,"ix":10,"a":0},"o":{"k":100,"ix":11,"a":0}},"ao":0,"bm":0,"shapes":[{"nm":"Group 33","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":1,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[563.512,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":1,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":2,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":4,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":5,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 34","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":2,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[567.554,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":1,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":2,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":3,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":4,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":5,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":8,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 35","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":3,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[571.597,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":1,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":2,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":3,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":4,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":5,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":6,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":8,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 36","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":4,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[575.639,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":1,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":2,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":3,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":4,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":6,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":8,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 37","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":5,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[579.682,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":1,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":2,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":3,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":4,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":6,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":9,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 38","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":6,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[583.724,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":2,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":3,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":4,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":9,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 39","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":7,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[587.766,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":2,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":4,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":9,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 40","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":8,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[591.809,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":2,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":3,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":4,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":5,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":6,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 41","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":9,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[595.851,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":2,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":4,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":6,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":11,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 42","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":10,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[599.893,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":2,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":4,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":6,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":9,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":11,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 43","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":11,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[603.936,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":2,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":4,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":8,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":9,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 44","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":12,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[607.978,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":8,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":9,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 45","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":13,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[612.02,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":6,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":8,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 46","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":14,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[616.063,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":6,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":8,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":13,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":14,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 47","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":15,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[620.105,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":6,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":9,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":14,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 48","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":16,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[624.148,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":9,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":14,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 49","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":17,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[628.19,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":9,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":14,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 50","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":18,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[632.233,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":14,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 51","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":19,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[636.275,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":11,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":14,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":16,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 52","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":20,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[640.317,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":8,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":9,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":11,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":16,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 53","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":21,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[644.359,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":8,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":9,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":14,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":16,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 1","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":22,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[648.402,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":8,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":9,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":14,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 2","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":23,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[652.444,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 3","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":24,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[656.487,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":13,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":14,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":18,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 4","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":25,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[660.529,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":14,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":18,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 5","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":26,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[664.572,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":14,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":19,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 6","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":27,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[668.614,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":11,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":14,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":19,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 7","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":28,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[672.656,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":14,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":19,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 8","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":29,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[676.699,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":14,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":16,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 9","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":30,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[680.741,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":16,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":20,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 10","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":31,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[684.784,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":13,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":14,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":16,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":21,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 11","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":32,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[688.826,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":10,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":12,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":14,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":21,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 12","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":33,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[692.868,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":11,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":13,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":14,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":21,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":25,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":28,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 13","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":34,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[696.911,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":18,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 14","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":35,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[700.953,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":18,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":23,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 15","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":36,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[704.996,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":19,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":21,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":23,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 16","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":37,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[709.038,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":16,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":19,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":21,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":24,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 17","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":38,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[713.081,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":19,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":21,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":24,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 18","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":39,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[717.123,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":24,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 19","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":40,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[721.165,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 32","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":41,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[725.208,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":21,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 31","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":42,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[729.25,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":5,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":7,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":16,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":18,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":25,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 30","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":43,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[733.293,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":18,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":21,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":25,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":26,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":27,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":30,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 29","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":44,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[737.335,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":17,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":18,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":19,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":23,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":25,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":27,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":30,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 28","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":45,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[741.377,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":23,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":25,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":27,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":28,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":30,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 27","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":46,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[745.42,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":21,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":23,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":25,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":27,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":28,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":30,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 26","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":47,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[749.462,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":21,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":24,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":25,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":27,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":28,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":30,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 25","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":48,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[753.505,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":21,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":24,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":25,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":26,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":27,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":28,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":29,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":30,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 24","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":49,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[757.547,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":24,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":25,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":27,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":28,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":29,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":30,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 23","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":50,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[761.589,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":25,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":27,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":28,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":29,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":30,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 22","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":51,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[765.632,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[60]},{"t":25,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":27,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":28,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":30,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 21","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":52,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[769.674,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":25,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":27,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":28,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":30,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":31,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 20","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":53,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[773.717,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":[{"t":0,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":15,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":20,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":22,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[0]},{"t":23,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[0]},{"t":24,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[20]},{"t":25,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":26,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":27,"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"s":[100]},{"t":28,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[100]},{"t":29,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[80]},{"t":30,"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"s":[40]},{"t":31,"s":[0]}],"ix":7,"a":1},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]}]},{"nm":"dotted line Outlines","ddd":0,"ty":4,"ind":2,"sr":1,"ip":0,"op":150,"st":0,"ks":{"a":{"k":[640,360,0],"ix":1,"a":0},"p":{"k":[459.5,322.5,0],"ix":2,"a":0},"s":{"k":[200,200,100],"ix":6,"a":0},"r":{"k":0,"ix":10,"a":0},"o":{"k":85,"ix":11,"a":0}},"ao":0,"bm":0,"shapes":[{"nm":"Group 1","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":1,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[648.402,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 2","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":2,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[652.444,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 3","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":3,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[656.487,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 4","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":4,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[660.529,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 5","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":5,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[664.572,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 6","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":6,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[668.614,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 7","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":7,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[672.656,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 8","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":8,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[676.699,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 9","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":9,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[680.741,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 10","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":10,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[684.784,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 11","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":11,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[688.826,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 12","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":12,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[692.868,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 13","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":13,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[696.911,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 14","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":14,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[700.953,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 15","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":15,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[704.996,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 16","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":16,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[709.038,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 17","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":17,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[713.081,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 18","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":18,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[717.123,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 19","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":19,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[721.165,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 20","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":20,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[773.717,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 21","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":21,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[769.674,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 22","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":22,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[765.632,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 23","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":23,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[761.589,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 24","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":24,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[757.547,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 25","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":25,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[753.505,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 26","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":26,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[749.462,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 27","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":27,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[745.42,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 28","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":28,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[741.377,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 29","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":29,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[737.335,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 30","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":30,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[733.293,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 31","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":31,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[729.25,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 32","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":32,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[725.208,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 33","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":33,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[563.512,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 34","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":34,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[567.554,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 35","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":35,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[571.597,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 36","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":36,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[575.639,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 37","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":37,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[579.682,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 38","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":38,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[583.724,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 39","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":39,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[587.766,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 40","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":40,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[591.809,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 41","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":41,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[595.851,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 42","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":42,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[599.893,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 43","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":43,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[603.936,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 44","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":44,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[607.978,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 45","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":45,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[612.02,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 46","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":46,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[616.063,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 47","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":47,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[620.105,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 48","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":48,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[624.148,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 49","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":49,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[628.19,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 50","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":50,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[632.233,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 51","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":51,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[636.275,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 52","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":52,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[640.317,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]},{"nm":"Group 53","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":53,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1.086,0.517],[-1.086,0.517],[-1.086,-0.518],[1.086,-0.518]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.secondary","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.8862745098039215,0.8823529411764706,0.9254901960784314,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[644.359,368.385],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]}]}],"nm":"Group 5693","v":"5.9.0","fr":30,"ip":0,"op":31,"w":1056,"h":872,"ddd":0,"assets":[],"markers":[]}
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/nearby_share_pulse_animation_jelly.json b/chromium/chrome/browser/resources/nearby_share/shared/nearby_share_pulse_animation_jelly.json
new file mode 100644
index 00000000000..9a979edc300
--- /dev/null
+++ b/chromium/chrome/browser/resources/nearby_share/shared/nearby_share_pulse_animation_jelly.json
@@ -0,0 +1 @@
+{"layers":[{"nm":"Layer 2 Outlines","ddd":0,"ty":4,"ind":1,"sr":1,"ip":0,"op":1440,"st":0,"ks":{"a":{"k":[64,64,0],"ix":1,"a":0},"p":{"k":[391.562,256.875,0],"ix":2,"a":0},"s":{"k":[303,303,100],"ix":6,"a":0},"r":{"k":0,"ix":10,"a":0},"o":{"k":10,"ix":11,"a":0}},"ao":0,"bm":0,"shapes":[{"nm":"Group 1","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":1,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":true,"i":[[13.254,0],[0,-13.255],[0,0],[0,0]],"o":[[-13.255,0],[0,0],[0,0],[0,-13.255]],"v":[[0,-12],[-24,12],[2.187,12],[24,12]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Fill","hd":false,"ty":"fl","bm":0,"o":{"k":100,"ix":5,"a":0},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":4,"a":0},"r":1},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[63.999,52],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]}]},{"nm":"Layer 1 Outlines 3","ddd":0,"ty":4,"ind":2,"sr":1,"ip":59,"op":1496,"st":56,"ks":{"a":{"k":[64,64,0],"ix":1,"a":0},"p":{"k":[391.562,256.875,0],"ix":2,"a":0},"s":{"ix":6,"a":1,"k":[{"t":58,"i":{"x":[0.4],"y":[1]},"o":{"x":[0.001],"y":[0]},"s":[113,113,100]},{"t":189,"s":[352,352,100]}]},"r":{"k":0,"ix":10,"a":0},"o":{"k":10,"ix":11,"a":0}},"ao":0,"bm":0,"shapes":[{"nm":"Group 1","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":1,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":false,"i":[[0,0],[34.518,0],[0,-34.518]],"o":[[0,-34.518],[-34.519,0],[0,0]],"v":[[62.5,31.25],[0,-31.25],[-62.5,31.25]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Stroke","hd":false,"ty":"st","bm":0,"lc":1,"lj":1,"ml":10,"o":{"k":100,"ix":4,"a":0},"w":{"k":[{"t":58,"i":{"x":[0.4],"y":[1]},"o":{"x":[0.001],"y":[0]},"s":[3]},{"t":189,"s":[0]}],"ix":5,"a":1},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":3,"a":0}},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[64,32.75],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]}]},{"nm":"Layer 1 Outlines 2","ddd":0,"ty":4,"ind":3,"sr":1,"ip":31,"op":1468,"st":28,"ks":{"a":{"k":[64,64,0],"ix":1,"a":0},"p":{"k":[391.562,256.875,0],"ix":2,"a":0},"s":{"ix":6,"a":1,"k":[{"t":30,"i":{"x":[0.4],"y":[1]},"o":{"x":[0.001],"y":[0]},"s":[113,113,100]},{"t":161,"s":[352,352,100]}]},"r":{"k":0,"ix":10,"a":0},"o":{"k":10,"ix":11,"a":0}},"ao":0,"bm":0,"shapes":[{"nm":"Group 1","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":1,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":false,"i":[[0,0],[34.518,0],[0,-34.518]],"o":[[0,-34.518],[-34.519,0],[0,0]],"v":[[62.5,31.25],[0,-31.25],[-62.5,31.25]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Stroke","hd":false,"ty":"st","bm":0,"lc":1,"lj":1,"ml":10,"o":{"k":100,"ix":4,"a":0},"w":{"k":[{"t":30,"i":{"x":[0.4],"y":[1]},"o":{"x":[0.001],"y":[0]},"s":[3]},{"t":161,"s":[0]}],"ix":5,"a":1},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":3,"a":0}},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[64,32.75],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]}]},{"nm":"Layer 1 Outlines","ddd":0,"ty":4,"ind":4,"sr":1,"ip":3,"op":1440,"st":0,"ks":{"a":{"k":[64,64,0],"ix":1,"a":0},"p":{"k":[391.562,256.875,0],"ix":2,"a":0},"s":{"ix":6,"a":1,"k":[{"t":2,"i":{"x":[0.4],"y":[1]},"o":{"x":[0.001],"y":[0]},"s":[113,113,100]},{"t":133,"s":[352,352,100]}]},"r":{"k":0,"ix":10,"a":0},"o":{"k":10,"ix":11,"a":0}},"ao":0,"bm":0,"shapes":[{"nm":"Group 1","mn":"ADBE Vector Group","hd":false,"ty":"gr","bm":0,"ix":1,"np":2,"it":[{"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false,"ty":"sh","ix":1,"ks":{"k":{"c":false,"i":[[0,0],[34.518,0],[0,-34.518]],"o":[[0,-34.518],[-34.519,0],[0,0]],"v":[[62.5,31.25],[0,-31.25],[-62.5,31.25]]},"ix":2,"a":0},"ind":0},{"nm":"cros.sys.illo.color1","mn":"ADBE Vector Graphic - Stroke","hd":false,"ty":"st","bm":0,"lc":1,"lj":1,"ml":10,"o":{"k":100,"ix":4,"a":0},"w":{"k":[{"t":2,"i":{"x":[0.4],"y":[1]},"o":{"x":[0.001],"y":[0]},"s":[3]},{"t":133,"s":[0]}],"ix":5,"a":1},"c":{"k":[0.24705882352941178,0.35294117647058826,0.6627450980392157,1],"ix":3,"a":0}},{"nm":"Transform","ty":"tr","a":{"k":[0,0],"ix":1,"a":0},"p":{"k":[64,32.75],"ix":2,"a":0},"s":{"k":[100,100],"ix":3,"a":0},"r":{"k":0,"ix":6,"a":0},"o":{"k":100,"ix":7,"a":0},"sk":{"k":0,"ix":4,"a":0},"sa":{"k":0,"ix":5,"a":0}}]}]}],"nm":"ripple","v":"5.6.4","fr":60,"ip":0,"op":189,"w":783,"h":257,"ddd":0,"assets":[],"markers":[]} \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html b/chromium/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html
index 09dc42af9a3..a462a7542b9 100644
--- a/chromium/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html
+++ b/chromium/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html
@@ -32,6 +32,10 @@
<path d="M16.1839 13.9904C17.3593 13.0152 18.298 11.6851 19.0002 10C17.0002 6.00003 14.0002 4.00003 10.0002 4.00003C8.81854 4.00003 7.7242 4.17455 6.71711 4.5236L8.34623 6.15272C8.86951 6.05045 9.42035 6.00003 10.0002 6.00003C12.9375 6.00003 15.1317 7.29405 16.7643 10.0696C16.2217 11.117 15.5593 11.9465 14.7617 12.5682L16.1839 13.9904Z"></path>
<path d="M12.9133 10.7198L9.28043 7.08692C9.511 7.03014 9.75206 7.00003 10.0002 7.00003C11.657 7.00003 13.0002 8.34317 13.0002 10C13.0002 10.2481 12.97 10.4892 12.9133 10.7198Z"></path>
</g>
+ <g id="your-devices">
+ <path d="M11 15H5V5.00208L19 4.96094V4C19 3.72386 18.8881 3.47386 18.7071 3.29289C18.5261 3.11193 18.2761 3 18 3H4C3.44772 3 3 3.44772 3 4V15H2C1.44772 15 1 15.4477 1 16C1 16.5523 1.44772 17 2 17H11V15Z"></path>
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M13.1667 6L17.8333 6.00611C18.475 6.00611 19 6.55 19 7.22222V15.7778C19 16.45 18.475 17 17.8333 17H13.1667C12.525 17 12 16.45 12 15.7778V7.22222C12 6.55 12.525 6 13.1667 6ZM13.5 15H17.5V7.5H13.5V15Z"></path>
+ </g>
</defs>
</svg>
</iron-iconset-svg>
@@ -39,7 +43,9 @@
<svg>
<defs>
<!-- Nearby Share icons -->
- <g id="done"><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"></path></g>
+ <g id="checked">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M19 10C19 14.9706 14.9706 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1C14.9706 1 19 5.02944 19 10ZM14.1705 6.9545C13.7312 6.51516 13.0188 6.51516 12.5795 6.9545L8.875 10.659L7.4205 9.2045C6.98116 8.76517 6.26884 8.76517 5.8295 9.2045C5.39016 9.64384 5.39016 10.3562 5.8295 10.7955L8.0795 13.0455C8.51884 13.4848 9.23116 13.4848 9.6705 13.0455L14.1705 8.5455C14.6098 8.10616 14.6098 7.39384 14.1705 6.9545Z"></path>
+ </g>
<!--
These icons are copied from Polymer's iron-icons and kept in sorted order.
diff --git a/chromium/chrome/browser/resources/net_internals/BUILD.gn b/chromium/chrome/browser/resources/net_internals/BUILD.gn
index b0701fd700b..57b3f8a1a69 100644
--- a/chromium/chrome/browser/resources/net_internals/BUILD.gn
+++ b/chromium/chrome/browser/resources/net_internals/BUILD.gn
@@ -37,6 +37,7 @@ generate_grd("build_grd") {
"index.js",
"main.css",
"proxy_view.js",
+ "shared_dictionary_view.js",
"sockets_view.js",
"tab_switcher_view.js",
"util.js",
diff --git a/chromium/chrome/browser/resources/net_internals/browser_bridge.js b/chromium/chrome/browser/resources/net_internals/browser_bridge.js
index 1de067f0566..471e772ad75 100644
--- a/chromium/chrome/browser/resources/net_internals/browser_bridge.js
+++ b/chromium/chrome/browser/resources/net_internals/browser_bridge.js
@@ -57,6 +57,25 @@ export class BrowserBridge {
chrome.send('setNetworkDebugMode', [subsystem]);
}
+ sendClearSharedDictionary() {
+ return sendWithPromise('clearSharedDictionary');
+ }
+
+ sendClearSharedDictionaryCacheForIsolationKey(frame_origin, top_frame_site) {
+ return sendWithPromise(
+ 'clearSharedDictionaryCacheForIsolationKey', frame_origin,
+ top_frame_site);
+ }
+
+ getSharedDictionaryUsageInfo() {
+ return sendWithPromise('getSharedDictionaryUsageInfo');
+ }
+
+ getSharedDictionaryInfo(frame_origin, top_frame_site) {
+ return sendWithPromise(
+ 'getSharedDictionaryInfo', frame_origin, top_frame_site);
+ }
+
static getInstance() {
return instance || (instance = new BrowserBridge());
}
diff --git a/chromium/chrome/browser/resources/net_internals/index.html b/chromium/chrome/browser/resources/net_internals/index.html
index d72c2ba3450..1fb63739664 100644
--- a/chromium/chrome/browser/resources/net_internals/index.html
+++ b/chromium/chrome/browser/resources/net_internals/index.html
@@ -14,92 +14,90 @@ found in the LICENSE file.
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="stylesheet" href="main.css">
</head>
- <body id=events-view-drop-target>
+ <body id="events-view-drop-target">
<div id="tab-list">
</div>
- <div id=main-tab-contents>
- <div id=events-view-tab-content class=content-box>
+ <div id="main-tab-contents">
+ <div id="events-view-tab-content" class="content-box">
The net-internals events viewer and related functionality has been removed.
Please use <a href="chrome://net-export">chrome://net-export</a> to save netlogs and the external <a href="https://netlog-viewer.appspot.com/">netlog_viewer</a> to view them.
</div>
- <div id=proxy-view-tab-content class=content-box>
- <input type=button value="Re-apply settings"
- id=proxy-view-reload-settings>
- <input type=button value="Clear bad proxies"
- id=proxy-view-clear-bad-proxies>
+ <div id="proxy-view-tab-content" class="content-box">
+ <input type="button" value="Re-apply settings"
+ id="proxy-view-reload-settings">
+ <input type="button" value="Clear bad proxies"
+ id="proxy-view-clear-bad-proxies">
</div>
- <div id=dns-view-tab-content class=content-box>
+ <div id="dns-view-tab-content" class="content-box">
<h4>DNS lookup</h4>
<p>Input a domain name to look up:</p>
- <form id=dns-view-dns-lookup-form>
- Domain: <input type=text id=dns-view-dns-lookup-input type="url"
+ <form id="dns-view-dns-lookup-form">
+ Domain: <input type="text" id="dns-view-dns-lookup-input" type="url"
placeholder="example.com">
- <input type=submit value="Lookup" id=dns-view-dns-lookup-submit>
+ <input type="submit" value="Lookup" id="dns-view-dns-lookup-submit">
</form>
- <div style="margin-top: 1em; margin-left: 2em;"
- id=dns-view-dns-lookup-output>
+ <div class="output-box" id="dns-view-dns-lookup-output">
</div>
</h4>
<h4>
Host resolver cache
- <input type=button value="Clear host cache" id=dns-view-clear-cache>
+ <button id="dns-view-clear-cache">Clear host cache</button>
</h4>
</div>
- <div id=sockets-view-tab-content class=content-box>
- <ul style='margin-top:0'>
+ <div id="sockets-view-tab-content" class="content-box">
+ <ul>
<li>
- <input type=button value="Close idle sockets"
- id=sockets-view-close-idle-button>
+ <input type="button" value="Close idle sockets"
+ id="sockets-view-close-idle-button">
</li>
<li>
- <input type=button value="Flush socket pools"
- id=sockets-view-flush-button>
- <span class=warning-text>
+ <input type="button" value="Flush socket pools"
+ id="sockets-view-flush-button">
+ <span class="warning-text">
May break pages with active connections
</span>
</li>
</ul>
</div>
- <div id=domain-security-policy-view-tab-content class=content-box>
+ <div id="domain-security-policy-view-tab-content" class="content-box">
<!-- This UI allows a user to query and update the browser's list of
HSTS/PKP domains. -->
<h3>HSTS/PKP</h3>
- <div class=deindent-header>
+ <div class="deindent-header">
HSTS is HTTP Strict Transport Security: a way for sites to elect to
always use HTTPS. See
- <a href="https://www.chromium.org/hsts" target=_blank>
+ <a href="https://www.chromium.org/hsts" target="_blank">
https://www.chromium.org/hsts</a>. PKP is Public Key Pinning: Chrome
"pins" certain public keys for certain sites in official builds.</div>
<h4>Add HSTS domain</h4>
<p>Input a domain name to add it to the HSTS set:</p>
- <form id=hsts-view-add-form>
- Domain: <input type=text id=hsts-view-add-input type="url"
+ <form id="hsts-view-add-form">
+ Domain: <input type="text" id=hsts-view-add-input type="url"
placeholder="example.com">
<label>Include subdomains for STS: <input type="checkbox"
- id=hsts-view-check-sts-input>
+ id="hsts-view-check-sts-input">
</label>
- <input type=submit value="Add" id=hsts-view-add-submit>
+ <input type="submit" value="Add" id="hsts-view-add-submit">
</form>
<h4>Query HSTS/PKP domain</h4>
<p>Input a domain name to query the current HSTS/PKP set:</p>
- <form id=hsts-view-query-form>
- Domain: <input type=text id=hsts-view-query-input type="url"
+ <form id="hsts-view-query-form">
+ Domain: <input type="text" id="hsts-view-query-input" type="url"
placeholder="example.com">
- <input type=submit value="Query" id=hsts-view-query-submit>
+ <input type="submit" value="Query" id="hsts-view-query-submit">
</form>
- <div style="margin-top: 1em; margin-left: 2em;"
- id=hsts-view-query-output>
+ <div class="output-box" id="hsts-view-query-output">
</div>
<h3>Delete domain security policies</h3>
@@ -108,20 +106,30 @@ found in the LICENSE file.
Input a domain name to delete its dynamic HSTS policy.
(<i>You cannot delete preloaded entries.</i>):
</p>
- <form id=domain-security-policy-view-delete-form>
- <label>Domain: <input type=text
- id=domain-security-policy-view-delete-input
+ <form id="domain-security-policy-view-delete-form">
+ <label>Domain: <input type="text"
+ id="domain-security-policy-view-delete-input"
type="url"
placeholder="example.com"></label>
- <input type=submit value="Delete"
- id=domain-security-policy-view-delete-submit>
+ <input type="submit" value="Delete"
+ id="domain-security-policy-view-delete-submit">
</form>
</div>
+ <div id="shared-dictionary-view-tab-content" class="content-box">
+ <h4>Shared Dictionary</h4>
+ <div>
+ <button value="Reload" id="shared-dictionary-reload">Reload</button>
+ <button id="shared-dictionary-view-clear-all">Clear all</button>
+ <div class="output-box" id="shared-dictionary-output">
+ </div>
+ </div>
+ </div>
+
<if expr="chromeos_ash">
- <div id="chromeos-view-tab-content" class=content-box>
- <h4 style='margin-top:0'>Import ONC file</h4>
+ <div id="chromeos-view-tab-content" class="content-box">
+ <h4 class="tab-title">Import ONC file</h4>
<div>
Import ONC File has moved to
<a href="chrome://network#general">chrome://network#general</a>.
diff --git a/chromium/chrome/browser/resources/net_internals/main.css b/chromium/chrome/browser/resources/net_internals/main.css
index 4c64fff35b4..17667b80ac1 100644
--- a/chromium/chrome/browser/resources/net_internals/main.css
+++ b/chromium/chrome/browser/resources/net_internals/main.css
@@ -90,3 +90,16 @@ ul {
input + label {
margin-inline-start: 10px;
}
+
+ul {
+ margin-top:0;
+}
+
+.output-box {
+ margin-top: 1em;
+ margin-left: 2em;
+}
+
+.content-box > *:first-child {
+ margin-top:0
+}
diff --git a/chromium/chrome/browser/resources/net_internals/main.js b/chromium/chrome/browser/resources/net_internals/main.js
index bf44047f233..fa82fbf7865 100644
--- a/chromium/chrome/browser/resources/net_internals/main.js
+++ b/chromium/chrome/browser/resources/net_internals/main.js
@@ -10,6 +10,7 @@ import {DnsView} from './dns_view.js';
import {DomainSecurityPolicyView} from './domain_security_policy_view.js';
import {EventsView} from './events_view.js';
import {ProxyView} from './proxy_view.js';
+import {SharedDictionaryView} from './shared_dictionary_view.js';
import {SocketsView} from './sockets_view.js';
import {TabSwitcherView} from './tab_switcher_view.js';
import {hasTouchScreen} from './util.js';
@@ -82,6 +83,7 @@ export class MainView extends WindowView {
addTab(DnsView);
addTab(SocketsView);
addTab(DomainSecurityPolicyView);
+ addTab(SharedDictionaryView);
// <if expr="chromeos_ash">
addTab(CrosView);
// </if>
diff --git a/chromium/chrome/browser/resources/net_internals/shared_dictionary_view.js b/chromium/chrome/browser/resources/net_internals/shared_dictionary_view.js
new file mode 100644
index 00000000000..f6b300f6ab5
--- /dev/null
+++ b/chromium/chrome/browser/resources/net_internals/shared_dictionary_view.js
@@ -0,0 +1,90 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {$} from 'chrome://resources/js/util_ts.js';
+
+import {BrowserBridge} from './browser_bridge.js';
+import {DivView} from './view.js';
+
+/** @type {?SharedDictionaryView} */
+let instance = null;
+
+/**
+ * This view displays information about Shared Dictionary:
+ */
+export class SharedDictionaryView extends DivView {
+ constructor() {
+ super(SharedDictionaryView.MAIN_BOX_ID);
+
+ this.browserBridge_ = BrowserBridge.getInstance();
+
+ this.outputElement = $(SharedDictionaryView.OUTPUT_ID);
+
+ const loadDictionary = async () => {
+ this.outputElement.innerHTML = window.trustedTypes.emptyHTML;
+ const result = await this.browserBridge_.getSharedDictionaryUsageInfo();
+ if (result.length === 0) {
+ this.outputElement.appendChild(document.createTextNode('no data'));
+ return;
+ }
+ const resultDiv = document.createElement('div');
+ for (const item of result) {
+ const isolationKeyDiv = document.createElement('div');
+ isolationKeyDiv.appendChild(document.createTextNode(
+ `Isolation key : {frame_origin: ${item.frame_origin}, ` +
+ `top_frame_site: ${item.top_frame_site}}`));
+ resultDiv.appendChild(isolationKeyDiv);
+
+ const diskUsageDiv = document.createElement('div');
+ diskUsageDiv.appendChild(document.createTextNode(
+ `Total usage: ${item.total_size_bytes} bytes`));
+ resultDiv.appendChild(diskUsageDiv);
+
+ const buttonDiv = document.createElement('div');
+ const clearButton = document.createElement('button');
+ clearButton.innerText = 'Clear';
+ clearButton.classList.add(
+ 'clear-shared-dictionary-button-for-isolation');
+ clearButton.addEventListener('click', async () => {
+ await this.browserBridge_
+ .sendClearSharedDictionaryCacheForIsolationKey(
+ item.frame_origin, item.top_frame_site);
+ loadDictionary();
+ });
+ buttonDiv.appendChild(clearButton);
+ resultDiv.appendChild(buttonDiv);
+
+ const dictsPre = document.createElement('pre');
+ const dicts = await this.browserBridge_.getSharedDictionaryInfo(
+ item.frame_origin, item.top_frame_site);
+ dictsPre.appendChild(
+ document.createTextNode(JSON.stringify(dicts, undefined, 2)));
+ resultDiv.appendChild(dictsPre);
+ }
+ this.outputElement.appendChild(resultDiv);
+ };
+
+ $(SharedDictionaryView.CLEAR_ALL_BUTTON_ID).onclick = async () => {
+ await this.browserBridge_.sendClearSharedDictionary();
+ loadDictionary();
+ };
+ $(SharedDictionaryView.RELOAD_BUTTON_ID).onclick = loadDictionary;
+ loadDictionary();
+ }
+
+ static getInstance() {
+ return instance || (instance = new SharedDictionaryView());
+ }
+}
+
+SharedDictionaryView.TAB_ID = 'tab-handle-shared-dictionary';
+SharedDictionaryView.TAB_NAME = 'Shared Dictionaries';
+SharedDictionaryView.TAB_HASH = '#sharedDictionary';
+
+// IDs for special HTML elements in index.html
+SharedDictionaryView.MAIN_BOX_ID = 'shared-dictionary-view-tab-content';
+
+SharedDictionaryView.CLEAR_ALL_BUTTON_ID = 'shared-dictionary-view-clear-all';
+SharedDictionaryView.RELOAD_BUTTON_ID = 'shared-dictionary-reload';
+SharedDictionaryView.OUTPUT_ID = 'shared-dictionary-output';
diff --git a/chromium/chrome/browser/resources/new_tab_page/BUILD.gn b/chromium/chrome/browser/resources/new_tab_page/BUILD.gn
index c40417c9ffb..db4708a51c5 100644
--- a/chromium/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chromium/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -12,6 +12,8 @@ assert(!is_android)
grd_prefix = "new_tab_page"
build_webui("build") {
+ icons_html_files = [ "modules/v2/icons.html" ]
+
static_files = [
"new_tab_page.html",
"shared_vars.css",
@@ -55,8 +57,10 @@ build_webui("build") {
"//chrome/browser/new_tab_page/modules/feed:mojo_bindings_ts__generator",
"//chrome/browser/new_tab_page/modules/history_clusters:mojo_bindings_ts__generator",
"//chrome/browser/new_tab_page/modules/history_clusters/cart:mojo_bindings_ts__generator",
+ "//chrome/browser/new_tab_page/modules/history_clusters/discount:mojo_bindings_ts__generator",
"//chrome/browser/new_tab_page/modules/photos:mojo_bindings_ts__generator",
"//chrome/browser/new_tab_page/modules/recipes:mojo_bindings_ts__generator",
+ "//chrome/browser/new_tab_page/modules/v2/history_clusters:mojo_bindings_ts__generator",
"//chrome/browser/ui/webui/new_tab_page:mojo_bindings_ts__generator",
"//components/history_clusters/public/mojom:mojo_bindings_ts__generator",
]
@@ -65,7 +69,10 @@ build_webui("build") {
"$root_gen_dir/chrome/browser/new_tab_page/modules/drive/drive.mojom-webui.ts",
"$root_gen_dir/chrome/browser/new_tab_page/modules/feed/feed.mojom-webui.ts",
"$root_gen_dir/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom-webui.ts",
+ "$root_gen_dir/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_layout_type.mojom-webui.ts",
+ "$root_gen_dir/chrome/browser/new_tab_page/modules/v2/history_clusters/history_clusters_v2.mojom-webui.ts",
"$root_gen_dir/chrome/browser/new_tab_page/modules/history_clusters/cart/cart.mojom-webui.ts",
+ "$root_gen_dir/chrome/browser/new_tab_page/modules/history_clusters/discount/discount.mojom-webui.ts",
"$root_gen_dir/chrome/browser/new_tab_page/modules/photos/photos.mojom-webui.ts",
"$root_gen_dir/chrome/browser/new_tab_page/modules/recipes/recipes.mojom-webui.ts",
"$root_gen_dir/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom-webui.ts",
diff --git a/chromium/chrome/browser/resources/new_tab_page/COMMON_METADATA b/chromium/chrome/browser/resources/new_tab_page/COMMON_METADATA
new file mode 100644
index 00000000000..eb6250a8719
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/COMMON_METADATA
@@ -0,0 +1,3 @@
+monorail: {
+ component: "UI>Browser>NewTabPage"
+}
diff --git a/chromium/chrome/browser/resources/new_tab_page/DIR_METADATA b/chromium/chrome/browser/resources/new_tab_page/DIR_METADATA
index eb6250a8719..4331278e715 100644
--- a/chromium/chrome/browser/resources/new_tab_page/DIR_METADATA
+++ b/chromium/chrome/browser/resources/new_tab_page/DIR_METADATA
@@ -1,3 +1 @@
-monorail: {
- component: "UI>Browser>NewTabPage"
-}
+mixins: "//chrome/browser/resources/new_tab_page/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/new_tab_page/app.html b/chromium/chrome/browser/resources/new_tab_page/app.html
index 6d4ecb2f94d..e60d3e9de81 100644
--- a/chromium/chrome/browser/resources/new_tab_page/app.html
+++ b/chromium/chrome/browser/resources/new_tab_page/app.html
@@ -34,6 +34,13 @@
}
}
+ :host-context([chrome-refresh-2023]) cr-most-visited {
+ --add-shortcut-background-color:
+ var(--color-new-tab-page-add-shortcut-background);
+ --add-shortcut-foreground-color:
+ var(--color-new-tab-page-add-shortcut-foreground);
+ }
+
:host([modules-redesigned-enabled_]) {
--ntp-module-border-radius: 16px;
--ntp-module-item-border-radius: 12px;
@@ -42,10 +49,6 @@
}
:host([show-background-image_]) {
- --ntp-theme-text-shadow: 0 0 16px rgba(0, 0, 0, .3);
- }
-
- :host([show-background-image_][remove-scrim_]) {
--ntp-theme-text-shadow: 0.5px 0.5px 1px rgba(0, 0, 0, 0.5),
0px 0px 2px rgba(0, 0, 0, 0.2), 0px 0px 10px rgba(0, 0, 0, 0.1);
--ntp-protected-icon-background-color: rgba(0, 0, 0, .6);
@@ -53,8 +56,11 @@
}
#oneGoogleBarScrim {
- background: linear-gradient(rgba(0, 0, 0, 0.25), transparent);
- height: 64px;
+ background: linear-gradient(rgba(0,0,0,0.25) 0%,
+ rgba(0,0,0,0.12) 45%,
+ rgba(0,0,0,0.05) 65%,
+ transparent 100%);
+ height: 80px;
position: absolute;
top: 0;
width: 100%;
@@ -99,7 +105,7 @@
width: var(--ntp-module-layout-width);
}
- ntp-modules:not([hidden]) {
+ #modules:not([hidden]) {
/* We use animation instead of transition to allow a fade-in out of
display: none. */
animation: 300ms ease-in-out fade-in-animation;
@@ -135,7 +141,7 @@
}
/* ~ because the dom-if results in a template between the middle-slot-promo
- and ntp-modules / ntp-modules-lanchpad. */
+ and ntp-modules. */
ntp-middle-slot-promo:not([hidden]) ~ #modules {
margin-top: 16px;
}
@@ -176,6 +182,8 @@
box-shadow: 0 3px 6px rgba(0, 0, 0, .16), 0 1px 2px rgba(0, 0, 0, .23);
font-weight: 400;
min-width: 32px;
+ padding-inline-start: 16px;
+ padding-inline-end: 16px;
}
:host([show-background-image_]) #customizeButton {
@@ -183,6 +191,11 @@
padding: 0;
}
+ :host-context([chrome-refresh-2023]):host([show-background-image_])
+ #customizeButton {
+ padding-inline-start: 8px;
+ }
+
:host-context(.focus-outline-visible) #customizeButton:focus {
box-shadow: var(--ntp-focus-shadow);
}
@@ -193,22 +206,32 @@
-webkit-mask-size: 100%;
background-color: var(--text-color);
height: 16px;
- margin-inline-end: 8px;
width: 16px;
}
+ @media (forced-colors: active) {
+ #customizeIcon {
+ background-color: ButtonText;
+ }
+ }
+
+ :host-context([chrome-refresh-2023]) #customizeButton {
+ --cr-button-height: 32px;
+ }
+
:host([show-background-image_]) #customizeIcon {
background-color: white;
margin: 0;
}
@media (max-width: 550px) {
- #customizeButton {
- padding: 0;
+ :host-context([chrome-refresh-2023]) #customizeButton {
+ padding-inline-start: 8px;
}
- #customizeIcon {
- margin: 0;
+ #customizeButton {
+ padding-inline-start: 0;
+ padding-inline-end: 0;
}
#customizeText {
@@ -216,13 +239,32 @@
}
}
+ @media (max-width: 1110px) {
+ :host-context([chrome-refresh-2023]):host([modules-redesigned-enabled_][modules-shown-to-user])
+ #customizeButton {
+ padding-inline-start: 8px;
+ }
+
+ :host([modules-redesigned-enabled_][modules-shown-to-user]) #customizeText {
+ display: none;
+ }
+
+ :host([modules-redesigned-enabled_][modules-shown-to-user])
+ #customizeButton {
+ padding-inline-start: 0;
+ padding-inline-end: 0;
+ }
+ }
+
@media (max-width: 970px) {
- :host([modules-shown-to-user]) #customizeButton {
- padding: 0;
+ :host-context([chrome-refresh-2023]):host([modules-shown-to-user])
+ #customizeButton {
+ padding-inline-start: 8px;
}
- :host([modules-shown-to-user]) #customizeIcon {
- margin: 0;
+ :host([modules-shown-to-user]) #customizeButton {
+ padding-inline-start: 0;
+ padding-inline-end: 0;
}
:host([modules-shown-to-user]) #customizeText {
@@ -231,12 +273,14 @@
}
@media (max-width: 1020px) {
- :host([modules-fre-shown]) #customizeButton {
- padding: 0;
+ :host-context([chrome-refresh-2023]):host([modules-fre-shown])
+ #customizeButton {
+ padding-inline-start: 8px;
}
- :host([modules-fre-shown]) #customizeIcon {
- margin: 0;
+ :host([modules-fre-shown]) #customizeButton {
+ padding-inline-start: 0;
+ padding-inline-end: 0;
}
:host([modules-fre-shown]) #customizeText {
@@ -260,17 +304,14 @@
max-width: 50vw;
padding: 8px;
position: fixed;
- text-shadow: var(--ntp-theme-text-shadow);
z-index: -1;
- }
-
- :host([remove-scrim_]) #backgroundImageAttribution {
background-color: var(--ntp-protected-icon-background-color);
text-shadow: none;
}
- :host([remove-scrim_]) #backgroundImageAttribution:hover {
+ #backgroundImageAttribution:hover {
background-color: var(--ntp-protected-icon-background-color-hovered);
+ background: rgba(var(--google-grey-900-rgb), .1);
}
:host-context([dir='ltr']) #backgroundImageAttribution {
@@ -281,10 +322,6 @@
right: 16px;
}
- #backgroundImageAttribution:hover {
- background: rgba(var(--google-grey-900-rgb), .1);
- }
-
#backgroundImageAttribution1Container {
align-items: center;
display: flex;
@@ -333,6 +370,10 @@
top: 0;
z-index: 101;
}
+
+ #webstoreToast {
+ padding: 16px;
+ }
</style>
<div id="content" style="
--color-new-tab-page-attribution-foreground: [[rgbaOrInherit_(theme_.textColor)]];
@@ -340,7 +381,7 @@
--ntp-logo-color: [[rgbaOrInherit_(logoColor_)]];">
<template is="dom-if" if="[[lazyRender_]]">
<template is="dom-if" if="[[oneGoogleBarEnabled_]]">
- <div id="oneGoogleBarScrim" hidden$="[[!showOneGoogleBarScrim_]]"
+ <div id="oneGoogleBarScrim" hidden$="[[!showBackgroundImage_]]"
fixed$="[[scrolledToTop_]]"></div>
<ntp-iframe id="oneGoogleBar" src="[[oneGoogleBarIframePath_]]"
hidden$="[[!oneGoogleBarLoaded_]]"
@@ -368,6 +409,14 @@
</ntp-lens-upload-dialog>
</template>
</div>
+ <template is="dom-if" if="[[lazyRender_]]" >
+ <cr-toast id="webstoreToast" duration="10000" hidden>
+ <div>$i18n{webstoreThemesToastMessage}</div>
+ <cr-button on-click="onWebstoreToastButtonClick_">
+ $i18n{webstoreThemesToastButtonText}
+ </cr-button>
+ </cr-toast>
+ </template>
<dom-if if="[[lazyRender_]]" on-dom-change="onLazyRendered_">
<template>
<template is="dom-if" if="[[shortcutsEnabled_]]">
@@ -394,6 +443,7 @@
</template>
<template is="dom-if" if="[[modulesRedesignedEnabled_]]">
<ntp-modules-v2 id="modules"
+ modules-shown-to-user="{{modulesShownToUser}}"
on-customize-module="onCustomizeModule_"
on-modules-loaded="onModulesLoaded_"
hidden="[[!promoAndModulesLoaded_]]">
@@ -420,7 +470,7 @@
<div id="customizeButtonContainer">
<cr-button id="customizeButton" on-click="onCustomizeClick_"
title="$i18n{customizeThisPage}" aria-pressed="[[showCustomize_]]">
- <div id="customizeIcon"></div>
+ <div id="customizeIcon" slot="prefix-icon"></div>
<div id="customizeText" hidden$="[[showBackgroundImage_]]">
$i18n{customizeButton}
</div>
diff --git a/chromium/chrome/browser/resources/new_tab_page/app.ts b/chromium/chrome/browser/resources/new_tab_page/app.ts
index 51b1e7ca657..4cc4702e4c5 100644
--- a/chromium/chrome/browser/resources/new_tab_page/app.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/app.ts
@@ -10,6 +10,7 @@ import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
import {HelpBubbleMixin, HelpBubbleMixinInterface} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js';
+import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
import {ClickInfo, Command} from 'chrome://resources/js/browser_command.mojom-webui.js';
import {BrowserCommandProxy} from 'chrome://resources/js/browser_command/browser_command_proxy.js';
import {hexColorToSkColor, skColorToRgba} from 'chrome://resources/js/color_utils.js';
@@ -25,7 +26,7 @@ import {CustomizeDialogPage} from './customize_dialog_types.js';
import {loadTimeData} from './i18n_setup.js';
import {IframeElement} from './iframe.js';
import {LogoElement} from './logo.js';
-import {recordLoadDuration} from './metrics_utils.js';
+import {recordDuration, recordLoadDuration} from './metrics_utils.js';
import {CustomizeChromeSection, NtpBackgroundImageSource, PageCallbackRouter, PageHandlerRemote, Theme} from './new_tab_page.mojom-webui.js';
import {NewTabPageProxy} from './new_tab_page_proxy.js';
import {$$} from './utils.js';
@@ -98,7 +99,6 @@ function ensureLazyLoaded() {
document.body.appendChild(script);
}
-
const AppElementBase = HelpBubbleMixin(PolymerElement) as
{new (): PolymerElement & HelpBubbleMixinInterface};
@@ -295,18 +295,6 @@ export class AppElement extends AppElementBase {
observer: 'onPromoAndModulesLoadedChange_',
},
- removeScrim_: {
- type: Boolean,
- value: () => loadTimeData.getBoolean('removeScrim'),
- reflectToAttribute: true,
- },
-
- showOneGoogleBarScrim_: {
- type: Boolean,
- computed:
- 'computeShowOneGoogleBarScrim_(removeScrim_, showBackgroundImage_)',
- },
-
showLensUploadDialog_: Boolean,
/**
@@ -324,7 +312,7 @@ export class AppElement extends AppElementBase {
static get observers() {
return [
- 'udpateOneGoogleBarAppearance_(oneGoogleBarLoaded_, theme_)',
+ 'updateOneGoogleBarAppearance_(oneGoogleBarLoaded_, theme_)',
];
}
@@ -357,7 +345,6 @@ export class AppElement extends AppElementBase {
private modulesLoaded_: boolean;
private modulesShownToUser: boolean;
private promoAndModulesLoaded_: boolean;
- private removeScrim_: boolean;
private lazyRender_: boolean;
private scrolledToTop_: boolean;
@@ -370,6 +357,7 @@ export class AppElement extends AppElementBase {
private shouldPrintPerformance_: boolean;
private backgroundImageLoadStartEpoch_: number;
private backgroundImageLoadStart_: number = 0;
+ private showWebstoreToastListenerId_: number|null = null;
constructor() {
performance.mark('app-creation-start');
@@ -423,6 +411,18 @@ export class AppElement extends AppElementBase {
(visible: boolean) => {
this.showCustomize_ = visible;
});
+ this.showWebstoreToastListenerId_ =
+ NewTabPageProxy.getInstance()
+ .callbackRouter.showWebstoreToast.addListener(() => {
+ if (this.showCustomize_) {
+ const toast = $$<CrToastElement>(this, '#webstoreToast');
+ if (toast) {
+ toast!.hidden = false;
+ toast!.show();
+ }
+ }
+ });
+
// Open Customize Chrome if there are Customize Chrome URL params.
if (this.showCustomize_) {
this.setCustomizeChromeSidePanelVisible_(this.showCustomize_);
@@ -445,22 +445,23 @@ export class AppElement extends AppElementBase {
this.eventTracker_.add(document, 'scroll', () => {
this.scrolledToTop_ = document.documentElement.scrollTop <= 0;
});
- if (this.shouldPrintPerformance_) {
- // It is possible that the background image has already loaded by now.
- // If it has, we request it to re-send the load time so that we can
- // actually catch the load time.
+ if (loadTimeData.getString('backgroundImageUrl')) {
this.backgroundManager_.getBackgroundImageLoadTime().then(
time => {
const duration = time - this.backgroundImageLoadStartEpoch_;
- this.printPerformanceDatum_(
- 'background-image-load', this.backgroundImageLoadStart_,
- duration);
- this.printPerformanceDatum_(
- 'background-image-loaded',
- this.backgroundImageLoadStart_ + duration);
+ recordDuration(
+ 'NewTabPage.Images.ShownTime.BackgroundImage', duration);
+ if (this.shouldPrintPerformance_) {
+ this.printPerformanceDatum_(
+ 'background-image-load', this.backgroundImageLoadStart_,
+ duration);
+ this.printPerformanceDatum_(
+ 'background-image-loaded',
+ this.backgroundImageLoadStart_ + duration);
+ }
},
() => {
- console.error('Failed to capture background image load time');
+ // Ignore. Failed to capture background image load time.
});
}
FocusOutlineManager.forDocument(document);
@@ -477,7 +478,7 @@ export class AppElement extends AppElementBase {
override ready() {
super.ready();
this.pageHandler_.onAppRendered(WindowProxy.getInstance().now());
- // Let the browser breath and then render remaining elements.
+ // Let the browser breathe and then render remaining elements.
WindowProxy.getInstance().waitForLazyRender().then(() => {
ensureLazyLoaded();
this.lazyRender_ = true;
@@ -487,7 +488,7 @@ export class AppElement extends AppElementBase {
}
// Called to update the OGB of relevant NTP state changes.
- private udpateOneGoogleBarAppearance_() {
+ private updateOneGoogleBarAppearance_() {
if (this.oneGoogleBarLoaded_) {
const isNtpDarkTheme =
this.theme_ && (!!this.theme_.backgroundImage || this.theme_.isDark);
@@ -529,10 +530,6 @@ export class AppElement extends AppElementBase {
(!loadTimeData.getBoolean('modulesEnabled') || this.modulesLoaded_);
}
- private computeShowOneGoogleBarScrim_(): boolean {
- return this.removeScrim_ && this.showBackgroundImage_;
- }
-
private async onLazyRendered_() {
// Integration tests use this attribute to determine when lazy load has
// completed.
@@ -628,7 +625,6 @@ export class AppElement extends AppElementBase {
theme.backgroundImageCollectionId ?? '');
}
-
private onPromoAndModulesLoadedChange_() {
if (this.promoAndModulesLoaded_ &&
loadTimeData.getBoolean('modulesEnabled')) {
@@ -838,6 +834,12 @@ export class AppElement extends AppElementBase {
});
}
+ private onWebstoreToastButtonClick_() {
+ window.location.assign(
+ `https://chrome.google.com/webstore/category/collection/chrome_color_themes?hl=${
+ window.navigator.language}`);
+ }
+
private onWindowClick_(e: Event) {
if (e.composedPath() && e.composedPath()[0] === $$(this, '#content')) {
recordClick(NtpElement.BACKGROUND);
diff --git a/chromium/chrome/browser/resources/new_tab_page/background_manager.ts b/chromium/chrome/browser/resources/new_tab_page/background_manager.ts
index f47b812d2ee..455866ff019 100644
--- a/chromium/chrome/browser/resources/new_tab_page/background_manager.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/background_manager.ts
@@ -110,9 +110,6 @@ export class BackgroundManager {
if (image.positionY) {
url.searchParams.append('positionY', image.positionY);
}
- if (image.scrimDisplay) {
- url.searchParams.append('scrimDisplay', image.scrimDisplay);
- }
if (url.href === this.url_) {
return;
}
diff --git a/chromium/chrome/browser/resources/new_tab_page/image_processor.ts b/chromium/chrome/browser/resources/new_tab_page/image_processor.ts
new file mode 100644
index 00000000000..5ae87542957
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/image_processor.ts
@@ -0,0 +1,203 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {checkTransparency} from './transparency.js';
+
+export const SUPPORTED_FILE_TYPES = [
+ 'image/bmp',
+ 'image/heic',
+ 'image/heif',
+ 'image/jpeg',
+ 'image/png',
+ 'image/tiff',
+ 'image/webp',
+ 'image/x-icon',
+];
+
+type MimeType = typeof SUPPORTED_FILE_TYPES[number];
+
+const MIME_TYPE_TO_EXTENSION_MAP: ReadonlyMap<MimeType, string> =
+ new Map<MimeType, string>([
+ ['image/png', '.png'],
+ ['image/webp', '.webp'],
+ ['image/bmp', '.bmp'],
+ ['image/heif', '.heif'],
+ ['image/jpeg', '.jpg'],
+ ['image/tiff', '.tif'],
+ ['image/heic', '.heic'],
+ ['image/x-icon', '.ico'],
+ ]);
+
+const MAX_LONGEST_EDGE_PIXELS = 1000;
+
+const TRANSPARENCY_FILL_BG_COLOR = '#ffffff';
+
+const JPEG_QUALITY = 40;
+
+const DEFAULT_MIME_TYPE = 'image/jpeg' as MimeType;
+
+export interface ProcessedFile {
+ processedFile: File;
+ imageWidth?: number;
+ imageHeight?: number;
+}
+
+// Takes an image file and does the following:
+// 1. Downscales the image so that the longest edge is maxLongestEdgePixels
+// (default 1000 px). Aspect ratio is preserved.
+// 2. Transcodes the image to jpeg, filling in the background with white if
+// the original image had transparency.
+// The processed image is returned along with the new image dimensions.
+export async function processFile(
+ file: File, maxLongestEdgePixels: number = MAX_LONGEST_EDGE_PIXELS):
+ Promise<ProcessedFile> {
+ const image = await readImageFile(file);
+ if (!image) {
+ return {processedFile: file};
+ }
+
+ const originalImageWidth = image.width;
+ const originalImageHeight = image.height;
+
+ const hasTransparency = checkTransparency(await file.arrayBuffer());
+
+ const blobInfo = await processImage(
+ image, DEFAULT_MIME_TYPE, hasTransparency, maxLongestEdgePixels);
+
+ if (!blobInfo || !blobInfo.blob) {
+ return {
+ processedFile: file,
+ imageWidth: originalImageWidth,
+ imageHeight: originalImageHeight,
+ };
+ }
+
+ const processedImage = blobInfo.blob;
+ let imageWidth = blobInfo.imageWidth;
+ let imageHeight = blobInfo.imageHeight;
+
+ const lastDot = file.name.lastIndexOf('.');
+
+ const fileName = `${lastDot > 0 ? file.name.slice(0, lastDot) : file.name}${
+ MIME_TYPE_TO_EXTENSION_MAP.get(processedImage.type as MimeType)}`;
+
+ let processedFile = new File(
+ [processedImage], fileName,
+ {lastModified: Date.now(), type: processedImage.type});
+
+ if (processedFile.size > file.size) {
+ processedFile = file;
+ imageWidth = originalImageWidth;
+ imageHeight = originalImageHeight;
+ }
+ return {processedFile, imageWidth, imageHeight};
+}
+
+async function readImageFile(file: File): Promise<HTMLImageElement|null> {
+ const dataUrl = await readAsDataURL(file);
+
+ if (!dataUrl || dataUrl instanceof ArrayBuffer) {
+ return null;
+ }
+
+ return createImageFromDataUrl(dataUrl);
+}
+
+function processImage(
+ image: HTMLImageElement, mimeType: MimeType, hasTransparency: boolean,
+ maxLongestEdgePixels?: number) {
+ const [width, height] = getDimensions(image, maxLongestEdgePixels);
+
+ const canvas = document.createElement('canvas');
+ canvas.width = width;
+ canvas.height = height;
+
+ const context = canvas.getContext('2d', {alpha: false, desynchronized: true});
+
+ if (!context) {
+ return null;
+ }
+
+ if (hasTransparency) {
+ fillBackground(
+ context, canvas.width, canvas.height, TRANSPARENCY_FILL_BG_COLOR);
+ }
+
+ context.drawImage(image, /*dx=*/ 0, /*dy=*/ 0, width, height);
+
+ return toBlob(canvas, mimeType, JPEG_QUALITY, width, height);
+}
+
+function getDimensions(image: HTMLImageElement, maxLongestEdgePixels?: number):
+ [width: number, height: number] {
+ let width = image.width;
+ let height = image.height;
+
+ if (maxLongestEdgePixels &&
+ (width > maxLongestEdgePixels || height > maxLongestEdgePixels)) {
+ const downscaleRatio =
+ Math.min(maxLongestEdgePixels / width, maxLongestEdgePixels / height);
+ width *= downscaleRatio;
+ height *= downscaleRatio;
+ }
+
+ return [Math.floor(width), Math.floor(height)];
+}
+
+function fillBackground(
+ context: CanvasRenderingContext2D, canvasWidth: number,
+ canvasHeight: number, backgroundColor: string) {
+ context.fillStyle = backgroundColor;
+ context.fillRect(0, 0, canvasWidth, canvasHeight);
+}
+
+function toBlob(
+ canvas: HTMLCanvasElement, type: MimeType, encodingCompressionRatio: number,
+ imageWidth: number, imageHeight: number) {
+ return new Promise<
+ {blob: Blob | null, imageWidth: number, imageHeight: number}>(
+ (resolve) => {
+ canvas.toBlob((result) => {
+ if (result) {
+ resolve({blob: result, imageWidth, imageHeight});
+ } else {
+ resolve({blob: null, imageWidth, imageHeight});
+ }
+ }, type, encodingCompressionRatio);
+ });
+}
+
+function readAsDataURL(file: File) {
+ const fileReader = new FileReader();
+
+ const promise = new Promise<string|ArrayBuffer|null>((resolve) => {
+ fileReader.onloadend = () => {
+ resolve(fileReader.result);
+ };
+ fileReader.onerror = () => {
+ // Failed to read file.
+ resolve(null);
+ };
+ });
+
+ fileReader.readAsDataURL(file);
+ return promise;
+}
+
+function createImageFromDataUrl(dataUrl: string) {
+ const image = new Image();
+
+ const promise = new Promise<HTMLImageElement|null>((resolve) => {
+ image.onload = () => {
+ resolve(image);
+ };
+ image.onerror = () => {
+ // Failed to load image from data url.
+ resolve(null);
+ };
+ });
+
+ image.src = dataUrl;
+ return promise;
+}
diff --git a/chromium/chrome/browser/resources/new_tab_page/lazy_load.ts b/chromium/chrome/browser/resources/new_tab_page/lazy_load.ts
index 8463d79f3ba..e3d1541f361 100644
--- a/chromium/chrome/browser/resources/new_tab_page/lazy_load.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/lazy_load.ts
@@ -53,6 +53,10 @@ export {driveDescriptor as driveV2Descriptor, DriveModuleElement as DriveV2Modul
export {FooProxy} from './modules/v2/dummy/foo_proxy.js';
export {DummyModuleElement, dummyV2Descriptor} from './modules/v2/dummy/module.js';
// </if>
+export {CartTileModuleElementV2} from './modules/v2/history_clusters/cart/cart_tile.js';
+export {HistoryClustersProxyImpl as HistoryClustersProxyImplV2} from './modules/v2/history_clusters/history_clusters_proxy.js';
export {historyClustersDescriptor as historyClustersV2Descriptor, HistoryClustersModuleElement as HistoryClustersV2ModuleElement} from './modules/v2/history_clusters/module.js';
-export {ModulesV2Element} from './modules/v2/modules.js';
+export {VisitTileModuleElement} from './modules/v2/history_clusters/visit_tile.js';
+export {ModuleHeaderElementV2} from './modules/v2/module_header.js';
+export {DismissModuleInstanceEvent, ModulesV2Element, NamedWidth, SUPPORTED_MODULE_WIDTHS} from './modules/v2/modules.js';
export {VoiceSearchOverlayElement} from './voice_search_overlay.js';
diff --git a/chromium/chrome/browser/resources/new_tab_page/lens_form.ts b/chromium/chrome/browser/resources/new_tab_page/lens_form.ts
index c2269c9292d..aaf2da36757 100644
--- a/chromium/chrome/browser/resources/new_tab_page/lens_form.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/lens_form.ts
@@ -5,10 +5,12 @@
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {loadTimeData} from './i18n_setup.js';
+import {ProcessedFile, processFile, SUPPORTED_FILE_TYPES} from './image_processor.js';
import {getTemplate} from './lens_form.html.js';
/** Lens service endpoint for the Upload by File action. */
-const UPLOAD_FILE_ACTION: string = 'https://lens.google.com/upload';
+const SCOTTY_UPLOAD_FILE_ACTION: string = 'https://lens.google.com/upload';
+const DIRECT_UPLOAD_FILE_ACTION: string = 'https://lens.google.com/v3/upload';
/** Entrypoint for the upload by file action. */
const UPLOAD_FILE_ENTRYPOINT: string = 'cntpubb';
@@ -28,17 +30,6 @@ const CHROMIUM_SURFACE: string = '4';
/** Max length for encoded input URL. */
const MAX_URL_LENGTH: number = 2000;
-const SUPPORTED_FILE_TYPES: string[] = [
- 'image/bmp',
- 'image/heic',
- 'image/heif',
- 'image/jpeg',
- 'image/png',
- 'image/tiff',
- 'image/webp',
- 'image/x-icon',
-];
-
/** Maximum file size support by Lens in bytes. */
const MAX_FILE_SIZE_BYTES: number = 20 * 1024 * 1024; // 20MB
@@ -98,6 +89,11 @@ export class LensFormElement extends PolymerElement {
readOnly: true,
value: CHROMIUM_SURFACE,
},
+ useDirectUpload_: {
+ type: Boolean,
+ readOnly: true,
+ value: loadTimeData.getBoolean('realboxLensDirectUpload'),
+ },
uploadFileAction_: String,
uploadUrlAction_: {
type: String,
@@ -124,10 +120,11 @@ export class LensFormElement extends PolymerElement {
}
private language_: string;
- private uploadFileAction_: string = UPLOAD_FILE_ACTION;
+ private uploadFileAction_: string = SCOTTY_UPLOAD_FILE_ACTION;
private uploadUrl_: string = '';
private startTime_: string|null = null;
private clientData_: string;
+ private useDirectUpload_: boolean;
openSystemFilePicker() {
this.$.fileInput.click();
@@ -154,7 +151,7 @@ export class LensFormElement extends PolymerElement {
return this.submitFile_(file);
}
- private submitFile_(file: File) {
+ private async submitFile_(file: File) {
if (!SUPPORTED_FILE_TYPES.includes(file.type)) {
this.dispatchError_(LensErrorType.FILE_TYPE);
return;
@@ -165,19 +162,35 @@ export class LensFormElement extends PolymerElement {
return;
}
+ if (this.useDirectUpload_) {
+ this.uploadFileAction_ = DIRECT_UPLOAD_FILE_ACTION;
+ }
+
+ let processedFile: ProcessedFile = {processedFile: file};
+
+ if (this.useDirectUpload_) {
+ processedFile = await processFile(file);
+ }
+
const dataTransfer = new DataTransfer();
- dataTransfer.items.add(file);
+ dataTransfer.items.add(processedFile.processedFile);
this.$.fileInput.files = dataTransfer.files;
this.startTime_ = Date.now().toString();
- const action = new URL(UPLOAD_FILE_ACTION);
+ const action = new URL(this.uploadFileAction_);
action.searchParams.set('ep', UPLOAD_FILE_ENTRYPOINT);
action.searchParams.set('hl', this.language_);
action.searchParams.set('st', this.startTime_.toString());
action.searchParams.set('cd', this.clientData_);
action.searchParams.set('re', RENDERING_ENVIRONMENT);
action.searchParams.set('s', CHROMIUM_SURFACE);
+ action.searchParams.set(
+ 'vph',
+ processedFile.imageHeight ? processedFile.imageHeight.toString() : '');
+ action.searchParams.set(
+ 'vpw',
+ processedFile.imageWidth ? processedFile.imageWidth.toString() : '');
this.uploadFileAction_ = action.toString();
this.dispatchLoading_(LensSubmitType.FILE);
diff --git a/chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.html b/chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.html
index b61351465b6..71e6ef74b96 100644
--- a/chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.html
+++ b/chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.html
@@ -72,6 +72,7 @@
}
#closeButton {
+ --cr-icon-button-fill-color: var(--color-new-tab-page-primary-foreground);
align-self: flex-end;
cursor: pointer;
display: flex;
diff --git a/chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.ts b/chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.ts
index b2819d16c85..14409cad242 100644
--- a/chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/lens_upload_dialog.ts
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -431,10 +431,17 @@ export class LensUploadDialogElement extends LensUploadDialogElementBase {
}
private onFocusOut_(event: FocusEvent) {
+ // If the focus event is occurring during a drag into the upload dialog,
+ // do nothing. See b/284201957#6 for scenario in which this is necessary.
+ if (this.dragCount === 1) {
+ return;
+ }
+
// Focus ensures that the file picker pop-up does not close dialog.
const outsideDialog = document.hasFocus() &&
(!event.relatedTarget ||
!this.$.dialog.contains(event.relatedTarget as Node));
+
if (outsideDialog) {
this.closeDialog();
}
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/cart/discount_consent_card.html b/chromium/chrome/browser/resources/new_tab_page/modules/cart/discount_consent_card.html
index b1966f5b6c9..9666f0feb5d 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/cart/discount_consent_card.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/cart/discount_consent_card.html
@@ -16,6 +16,10 @@
width: 244px;
}
+ :host-context([chrome-refresh-2023]) #consentCardContainer {
+ background-color: var(--color-sys-base-container-elevated);
+ }
+
:host([color-consent-container_]) #consentCardContainer {
background-color: var(--google-blue-100);
}
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/cart/module.html b/chromium/chrome/browser/resources/new_tab_page/modules/cart/module.html
index 7cac6816a97..c99b5c5aa4f 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/cart/module.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/cart/module.html
@@ -72,6 +72,11 @@
margin: 0 4px;
}
+ :host-context([chrome-refresh-2023]) #consentCard,
+ :host-context([chrome-refresh-2023]) .cart-container {
+ background-color: var(--color-sys-base-container-elevated);
+ }
+
.cart-container {
outline: none;
position: relative;
@@ -337,6 +342,9 @@
disable-text="[[i18nRecursive('',
'modulesDisableButtonText',
'modulesCartLower')]]"
+ more-actions-text="[[i18nRecursive('',
+ 'modulesMoreActions',
+ 'modulesCartLowerYour')]]"
show-info-button on-info-button-click="onInfoButtonClick_"
show-dismiss-button on-dismiss-button-click="onDismissButtonClick_"
on-disable-button-click="onDisableButtonClick_"
@@ -441,7 +449,8 @@
</div>
</a>
<cr-icon-button class="icon-more-vert"
- title="$i18n{moreActions}" on-click="onCartMenuButtonClick_">
+ title="[[i18n('modulesMoreActions', item.merchant)]]"
+ on-click="onCartMenuButtonClick_">
</cr-icon-button>
</div>
</template>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/drive/module.html b/chromium/chrome/browser/resources/new_tab_page/modules/drive/module.html
index 32c1c2020fd..7523c21386b 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/drive/module.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/drive/module.html
@@ -74,6 +74,9 @@
disable-text="[[i18nRecursive('',
'modulesDisableButtonText',
'modulesDriveSentence2')]]"
+ more-actions-text="[[i18nRecursive('',
+ 'modulesMoreActions',
+ 'modulesDriveSentence')]]"
show-info-button on-info-button-click="onInfoButtonClick_"
show-dismiss-button on-dismiss-button-click="onDismissButtonClick_"
on-disable-button-click="onDisableButtonClick_"
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html b/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html
index 36b430478b6..3696a0c78b9 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html
@@ -39,8 +39,8 @@
.layout {
display: grid;
grid-gap: var(--gap-size) var(--gap-size);
- grid-template-columns: repeat(2, 1fr);
- grid-template-rows: repeat(2, 1fr);
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ grid-template-rows: repeat(2, minmax(0, 1fr));
padding: 0 16px 16px;
}
@@ -97,6 +97,7 @@
'modulesDisableButtonText',
'modulesJourneyDisable')]]"
dismiss-text="[[i18n('modulesDismissButtonText', cluster.label)]]"
+ more-actions-text="[[i18n('modulesMoreActions', cluster.label)]]"
show-info-button show-dismiss-button
on-disable-button-click="onDisableButtonClick_"
on-dismiss-button-click="onDismissButtonClick_"
@@ -118,8 +119,8 @@
<template is="dom-if" if="[[isLayout_(1, layoutType)]]" restamp>
<div id="layout1" class="layout">
<ntp-history-clusters-tile class="main-tile" large-format
- visit="[[cluster.visits.0]]" on-click="onVisitTileClick_"
- on-aux-click="onVisitTileClick_">
+ visit="[[cluster.visits.0]]" discount="[[discounts.0]]"
+ on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_">
</ntp-history-clusters-tile>
<template is="dom-if" if="[[shouldShowCartTile_(cart)]]" restamp>
<ntp-history-clusters-cart-tile
@@ -129,8 +130,8 @@
</template>
<template is="dom-if" if="[[!shouldShowCartTile_(cart)]]" restamp>
<ntp-history-clusters-tile class="secondary-tile" medium-format
- visit="[[cluster.visits.1]]" on-click="onVisitTileClick_"
- on-aux-click="onVisitTileClick_">
+ visit="[[cluster.visits.1]]" discount="[[discounts.1]]"
+ on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_">
</ntp-history-clusters-tile>
</template>
<ntp-history-clusters-suggest-tile class="related-searches-tile"
@@ -142,8 +143,8 @@
<template is="dom-if" if="[[isLayout_(2, layoutType)]]" restamp>
<div id="layout2" class="layout">
<ntp-history-clusters-tile class="main-tile" large-format
- visit="[[cluster.visits.0]]" on-click="onVisitTileClick_"
- on-aux-click="onVisitTileClick_">
+ visit="[[cluster.visits.0]]" discount="[[discounts.0]]"
+ on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_">
</ntp-history-clusters-tile>
<template is="dom-if" if="[[shouldShowCartTile_(cart)]]" restamp>
<ntp-history-clusters-cart-tile
@@ -154,10 +155,12 @@
<template is="dom-if" if="[[!shouldShowCartTile_(cart)]]" restamp>
<div class="small-tiles">
<ntp-history-clusters-tile small-format visit="[[cluster.visits.1]]"
- on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_">
+ discount="[[discounts.1]]" on-click="onVisitTileClick_"
+ on-aux-click="onVisitTileClick_">
</ntp-history-clusters-tile>
<ntp-history-clusters-tile small-format visit="[[cluster.visits.2]]"
- on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_">
+ discount="[[discounts.2]]"on-click="onVisitTileClick_"
+ on-aux-click="onVisitTileClick_">
</ntp-history-clusters-tile>
</div>
</template>
@@ -171,10 +174,12 @@
<div id="layout3" class="layout">
<div class="main-tile">
<ntp-history-clusters-tile medium-format visit="[[cluster.visits.0]]"
- on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_">
+ discount="[[discounts.0]]" on-click="onVisitTileClick_"
+ on-aux-click="onVisitTileClick_">
</ntp-history-clusters-tile>
<ntp-history-clusters-tile medium-format visit="[[cluster.visits.1]]"
- on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_">
+ discount="[[discounts.1]]" on-click="onVisitTileClick_"
+ on-aux-click="onVisitTileClick_">
</ntp-history-clusters-tile>
</div>
<template is="dom-if" if="[[shouldShowCartTile_(cart)]]" restamp>
@@ -186,10 +191,12 @@
<template is="dom-if" if="[[!shouldShowCartTile_(cart)]]" restamp>
<div class="small-tiles">
<ntp-history-clusters-tile small-format visit="[[cluster.visits.2]]"
- on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_">
+ discount="[[discounts.2]]" on-click="onVisitTileClick_"
+ on-aux-click="onVisitTileClick_">
</ntp-history-clusters-tile>
<ntp-history-clusters-tile small-format visit="[[cluster.visits.3]]"
- on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_">
+ discount="[[discounts.3]]" on-click="onVisitTileClick_"
+ on-aux-click="onVisitTileClick_">
</ntp-history-clusters-tile>
</div>
</template>
@@ -202,7 +209,7 @@
</div>
<cr-lazy-render id="infoDialogRender">
<template>
- <ntp-info-dialog inner-h-t-m-l="[[i18nAdvanced('modulesJourneysInfo')]]">
+ <ntp-info-dialog inner-h-t-m-l="[[getInfo_(discounts)]]">
</ntp-info-dialog>
</template>
</cr-lazy-render>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts b/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
index 4ec7018b23e..2889c8eece7 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
@@ -4,6 +4,7 @@
import '../module_header.js';
import './suggest_tile.js';
+import '../../discount.mojom-webui.js';
import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
import {assert} from 'chrome://resources/js/assert_ts.js';
@@ -12,7 +13,7 @@ import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bu
import {Cart} from '../../cart.mojom-webui.js';
import {Cluster, URLVisit} from '../../history_cluster_types.mojom-webui.js';
-import {LayoutType} from '../../history_clusters.mojom-webui.js';
+import {LayoutType} from '../../history_clusters_layout_type.mojom-webui.js';
import {I18nMixin, loadTimeData} from '../../i18n_setup.js';
import {NewTabPageProxy} from '../../new_tab_page_proxy.js';
import {InfoDialogElement} from '../info_dialog';
@@ -83,6 +84,15 @@ export class HistoryClustersModuleElement extends I18nMixin
value: null,
},
+ /**
+ The discounts displayed on the visit tiles of this element, could be
+ empty.
+ */
+ discounts: {
+ type: Array,
+ value: [],
+ },
+
searchResultPage: Object,
overflowScroll_: {
@@ -95,6 +105,7 @@ export class HistoryClustersModuleElement extends I18nMixin
cluster: Cluster;
cart: Cart|null;
+ discounts: string[];
layoutType: LayoutType;
searchResultPage: URLVisit;
private setDisabledModulesListenerId_: number|null = null;
@@ -200,8 +211,8 @@ export class HistoryClustersModuleElement extends I18nMixin
composed: true,
detail: {
message: loadTimeData.getStringF(
- 'disableQuestsModuleToastMessage',
- loadTimeData.getString('disableQuestsModuleToastName')),
+ 'modulesDisableToastMessage',
+ loadTimeData.getString('modulesThisTypeOfCardText')),
},
});
this.dispatchEvent(disableEvent);
@@ -244,6 +255,13 @@ export class HistoryClustersModuleElement extends I18nMixin
'modulesChromeCartInHistoryClustersModuleEnabled') &&
!!cart;
}
+
+ private getInfo_(discounts: string[]): TrustedHTML {
+ const hasDiscount = discounts.some((discount) => !!discount);
+ return this.i18nAdvanced(
+ hasDiscount ? 'modulesHistoryWithDiscountInfo' :
+ 'modulesJourneysInfo');
+ }
}
customElements.define(
@@ -309,6 +327,22 @@ async function createElement(): Promise<HistoryClustersModuleElement|null> {
visit.hasUrlKeyedImage && visit.isKnownToSync)
.length;
const visitCount = element.cluster.visits.length;
+ if (loadTimeData.getBoolean('historyClustersModuleDiscountsEnabled')) {
+ const {discounts} = await HistoryClustersProxyImpl.getInstance()
+ .handler.getDiscountsForCluster(clusters[0]);
+ for (const visit of clusters[0].visits) {
+ let discountInValue = '';
+ for (const [url, discount] of discounts) {
+ if (url.url === visit.normalizedUrl.url && discount.length > 0) {
+ discountInValue = discount[0].valueInText;
+ visit.normalizedUrl.url = discount[0].annotatedVisitUrl.url;
+ }
+ }
+ element.discounts.push(discountInValue);
+ }
+ } else {
+ element.discounts = Array(visitCount).fill('');
+ }
// Calculate which layout to use.
if (imageCount >= LAYOUT_3_MIN_IMAGE_VISITS) {
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.html b/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.html
index 74f9f5a052e..683a8aeb860 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.html
@@ -5,6 +5,7 @@
border-radius: var(--ntp-module-item-border-radius);
color: var(--color-new-tab-page-primary-foreground);
display: inline-block;
+ min-width: 0;
width: 100%;
}
@@ -169,6 +170,51 @@
line-height: 16px;
}
+ #discountChip {
+ background: var(--color-sys-tertiary-container);
+ border-radius: var(--ntp-module-item-border-radius);
+ color: var(--color-sys-on-tertiary-container);
+ display: inline-block;
+ font-weight: 500;
+ font-size: 11px;
+ height: 18px;
+ line-height: 18px;
+ margin-top: 8px;
+ padding-inline-end: 4px;
+ padding-inline-start: 4px;
+ text-align: center;
+ }
+
+ :host([large-format]) #discountChip {
+ margin-bottom: 26px;
+ margin-inline-start: 16px;
+ }
+
+ :host([medium-format]) #discountChip {
+ margin-bottom: 32px;
+ margin-inline-start: 8px;
+ }
+
+ :host([small-format]) #discountChip {
+ margin-bottom: 30px;
+ margin-inline-start: 16px;
+ }
+
+ :host([has-discount]) #title{
+ margin-bottom: 0;
+ }
+
+ :host([has-discount][large-format]) #title{
+ -webkit-line-clamp: 1;
+ height: 24px;
+ }
+
+ :host([has-discount][medium-format]) #title,
+ :host([has-discount][small-format]) #title {
+ -webkit-line-clamp: 2;
+ height: 40px;
+ }
+
/* Set styles for high contrast mode in Windows. */
@media (forced-colors: active) {
/* Set focus outline since box-shadow isn't visible in hcm */
@@ -199,6 +245,9 @@
</div>
<div id="text-container">
<div id="title" class="truncate">[[visit.pageTitle]]</div>
+ <template is="dom-if" if="[[hasDiscount]]" restamp>
+ <div id="discountChip">[[discount]]</div>
+ </template>
<div id="info-container">
<page-favicon id="icon" url="[[visit.normalizedUrl]]"
is-known-to-sync="[[visit.isKnownToSync]]">
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts b/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts
index 67c82ea4c4b..4807811dfee 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/history_clusters/tile.ts
@@ -59,11 +59,24 @@ export class TileModuleElement extends I18nMixin
value: false,
reflectToAttribute: true,
},
+
+ // The texts for the discount chip.
+ discount: {
+ type: String,
+ },
+
+ hasDiscount: {
+ type: Boolean,
+ computed: `computeHasDiscount_(discount)`,
+ reflectToAttribute: true,
+ },
};
}
visit: URLVisit;
smallFormat: boolean;
+ discount: string;
+ hasDiscount: boolean;
private imageUrl_: Url|null;
hasImageUrl(): boolean {
@@ -83,6 +96,10 @@ export class TileModuleElement extends I18nMixin
return domain;
}
+ private computeHasDiscount_(): boolean {
+ return !!this.discount && this.discount.length !== 0;
+ }
+
// Set imageUrl when visit is set/updated.
private async onVisitUpdated_(): Promise<void> {
const visitUrl = this.visit.normalizedUrl;
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts b/chromium/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts
index b27e6af693d..b6a61f7f697 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts
@@ -11,9 +11,6 @@ import {NewTabPageProxy} from '../new_tab_page_proxy.js';
import {chromeCartDescriptor} from './cart/module.js';
import {driveDescriptor} from './drive/module.js';
-// <if expr="not is_official_build">
-import {dummyV2Descriptor, dummyV2Descriptor02, dummyV2Descriptor03, dummyV2Descriptor04, dummyV2Descriptor05, dummyV2Descriptor06, dummyV2Descriptor07, dummyV2Descriptor08, dummyV2Descriptor09, dummyV2Descriptor10, dummyV2Descriptor11, dummyV2Descriptor12} from './v2/dummy/module.js';
-// </if>
import {feedDescriptor} from './feed/module.js';
import {HistoryClustersProxyImpl} from './history_clusters/history_clusters_proxy.js';
import {historyClustersDescriptor} from './history_clusters/module.js';
@@ -22,6 +19,9 @@ import {ModuleRegistry} from './module_registry.js';
import {photosDescriptor} from './photos/module.js';
import {recipeTasksDescriptor} from './recipes/module.js';
import {driveDescriptor as driveV2Descriptor} from './v2/drive/module.js';
+// <if expr="not is_official_build">
+import {dummyV2Descriptor} from './v2/dummy/module.js';
+// </if>
import {historyClustersDescriptor as historyClustersV2Descriptor} from './v2/history_clusters/module.js';
const modulesRedesignedEnabled: boolean =
@@ -40,17 +40,6 @@ descriptors.push(
// <if expr="not is_official_build">
if (modulesRedesignedEnabled) {
descriptors.push(dummyV2Descriptor);
- descriptors.push(dummyV2Descriptor02);
- descriptors.push(dummyV2Descriptor03);
- descriptors.push(dummyV2Descriptor04);
- descriptors.push(dummyV2Descriptor05);
- descriptors.push(dummyV2Descriptor06);
- descriptors.push(dummyV2Descriptor07);
- descriptors.push(dummyV2Descriptor08);
- descriptors.push(dummyV2Descriptor09);
- descriptors.push(dummyV2Descriptor10);
- descriptors.push(dummyV2Descriptor11);
- descriptors.push(dummyV2Descriptor12);
}
// </if>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/module_header.html b/chromium/chrome/browser/resources/new_tab_page/modules/module_header.html
index f2178a1e203..c02bffe7354 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/module_header.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/module_header.html
@@ -106,7 +106,7 @@
</cr-icon-button>
</template>
<template is="dom-if" if="[[!hideMenuButton]]" restamp>
- <cr-icon-button id="menuButton" title="$i18n{moreActions}"
+ <cr-icon-button id="menuButton" title="[[moreActionsText]]"
class="icon-more-vert" on-click="onMenuButtonClick_">
</cr-icon-button>
</template>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/module_header.ts b/chromium/chrome/browser/resources/new_tab_page/modules/module_header.ts
index 536e72ae889..cc6e759ed46 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/module_header.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/module_header.ts
@@ -71,6 +71,7 @@ export class ModuleHeaderElement extends PolymerElement {
dismissText: String,
disableText: String,
+ moreActionsText: String,
modulesRedesignedEnabled_: {
type: Boolean,
@@ -100,6 +101,7 @@ export class ModuleHeaderElement extends PolymerElement {
hideMenuButton: boolean;
dismissText: string;
disableText: string;
+ moreActionsText: string;
private modulesRedesignedEnabled_: boolean;
private computeIconStyle_() {
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.html b/chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.html
index 25738411729..b54ab2f3d84 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.html
@@ -24,8 +24,10 @@
justify-content: center;
}
- :host([modules-redesigned-enabled_]) #moduleElement {
- background: none;
+ :host([modules-redesigned-enabled_]) {
+ background-color: var(--color-new-tab-page-module-background);
+ border: none;
+ height: fit-content;
}
</style>
<div id="impressionProbe"></div>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts b/chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts
index 3342b2a1078..859116e2e85 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import {assert} from 'chrome://resources/js/assert_ts.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {recordLoadDuration, recordOccurence, recordPerdecage} from '../metrics_utils.js';
@@ -40,6 +41,11 @@ export class ModuleWrapperElement extends PolymerElement {
observer: 'onModuleChange_',
type: Object,
},
+ modulesRedesignedEnabled_: {
+ type: Boolean,
+ value: () => loadTimeData.getBoolean('modulesRedesignedEnabled'),
+ reflectToAttribute: true,
+ },
};
}
@@ -69,9 +75,14 @@ export class ModuleWrapperElement extends PolymerElement {
if (intersectionRatio >= 1.0) {
headerObserver.disconnect();
const time = WindowProxy.getInstance().now();
- recordLoadDuration('NewTabPage.Modules.Impression', time);
- recordLoadDuration(
- `NewTabPage.Modules.Impression.${this.module.descriptor.id}`, time);
+ // TODO(crbug.com/1444758): Add module instances impression duration
+ // metrics.
+ if (!this.modulesRedesignedEnabled_) {
+ recordLoadDuration('NewTabPage.Modules.Impression', time);
+ recordLoadDuration(
+ `NewTabPage.Modules.Impression.${this.module.descriptor.id}`,
+ time);
+ }
this.dispatchEvent(new Event('detect-impression'));
this.module.element.dispatchEvent(new Event('detect-impression'));
}
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/modules.gni b/chromium/chrome/browser/resources/new_tab_page/modules/modules.gni
index a03c53f0509..10c35c46e0a 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/modules.gni
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/modules.gni
@@ -23,7 +23,8 @@ modules_non_web_component_files =
"modules/module_registry.ts",
] + cart_non_web_component_files + drive_non_web_component_files +
feed_non_web_component_files + photos_non_web_component_files +
- recipes_non_web_component_files + history_clusters_non_web_component_files
+ recipes_non_web_component_files + history_clusters_non_web_component_files +
+ history_clusters_v2_non_web_component_files
if (!is_official_build) {
modules_non_web_component_files += dummy_v2_non_web_component_files
@@ -36,6 +37,7 @@ modules_web_component_files =
"modules/module_header.ts",
"modules/modules.ts",
"modules/module_wrapper.ts",
+ "modules/v2/module_header.ts",
"modules/v2/modules.ts",
] + cart_web_component_files + drive_web_component_files +
drive_v2_web_component_files + feed_web_component_files +
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/modules.html b/chromium/chrome/browser/resources/new_tab_page/modules/modules.html
index 89e12b314b6..d4a812f500a 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/modules.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/modules.html
@@ -60,6 +60,7 @@
}
.action-button {
+ --text-color-action: var(--color-new-tab-page-action-button-foreground);
background-color: var(--color-new-tab-page-action-button-background);
margin-inline-end: 8px;
margin-top: 18px;
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/photos/module.html b/chromium/chrome/browser/resources/new_tab_page/modules/photos/module.html
index cd9f31de38c..bee0651bf7f 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/photos/module.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/photos/module.html
@@ -432,7 +432,10 @@
on-disable-button-click="onDisableButtonClick_"
disable-text="[[i18nRecursive('',
'modulesDisableButtonText',
- 'modulesPhotosMemoriesDisable')]]">
+ 'modulesPhotosMemoriesDisable')]]"
+ more-actions-text="[[i18nRecursive('',
+ 'modulesMoreActions',
+ 'modulesPhotosTitle')]]">
$i18n{modulesPhotosTitle}
</ntp-module-header>
<div id="memories">
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/recipes/module.html b/chromium/chrome/browser/resources/new_tab_page/modules/recipes/module.html
index b97c4f7dbde..576af568188 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/recipes/module.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/recipes/module.html
@@ -184,6 +184,7 @@
<ntp-module-header
dismiss-text="[[i18n('modulesDismissButtonText', dismissName_)]]"
disable-text="[[i18n('modulesDisableButtonText', disableName_)]]"
+ more-actions-text="[[i18n('modulesMoreActions', disableName_)]]"
show-info-button on-info-button-click="onInfoButtonClick_"
show-dismiss-button on-dismiss-button-click="onDismissButtonClick_"
on-disable-button-click="onDisableButtonClick_"
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.html
index ff04111e760..21ac9ad754c 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.html
@@ -1,101 +1,125 @@
<style>
:host {
- display: block;
- height: 100%;
- width: 100%;
+ background-color: var(--color-new-tab-page-module-background);
}
- ntp-module-header {
- margin-bottom: 8px;
+ @media (forced-colors: active) {
+ /* Set outline since background isn't visible in hcm */
+ ntp-module-header-v2,
+ a {
+ border-radius: var(--ntp-module-item-border-radius);
+ outline: var(--cr-border-hcm);
+ }
+
+ .file {
+ overflow: visible;
+ }
}
- #files {
- display: flex;
- flex-direction: column;
- margin-bottom: 12px;
+ ntp-module-header-v2 {
+ margin: 8px;
}
- .file:hover {
- background-color: var(--google-grey-400);
+ :host-context(.focus-outline-visible) a:focus,
+ a:focus-visible {
+ box-shadow: var(--ntp-focus-shadow);
+ outline: none;
+ }
+
+ #files {
+ background-color: var(--color-new-tab-page-module-item-background);
+ border-radius: var(--ntp-module-item-border-radius);
+ margin: 8px;
}
.file {
- color: inherit;
+ align-items: center;
display: flex;
- height: 40px;
- padding-bottom: 8px;
- padding-top: 8px;
+ height: 52px;
+ padding-bottom: 2px;
+ padding-top: 2px;
+ position: relative;
text-decoration: none;
+ overflow: hidden;
+ }
+
+ .file:hover #hover-layer {
+ background: var(--color-new-tab-page-module-item-background-hovered);
+ display: block;
+ inset: 0;
+ pointer-events: none;
+ position: absolute;
+ }
+
+ #hover-layer {
+ display: none;
+ }
+
+ .file:first-of-type {
+ border-radius: var(--ntp-module-item-border-radius)
+ var(--ntp-module-item-border-radius) 0 0;
+ }
+
+ .file:last-of-type {
+ border-radius: 0 0 var(--ntp-module-item-border-radius)
+ var(--ntp-module-item-border-radius);
+ }
+
+ .file-icon {
+ height: 18px;
+ margin-inline-end: 17px;
+ margin-inline-start: 17px;
+ width: 18px;
}
.file-info {
- display: flex;
- flex-direction: column;
- margin-inline-start: 16px;
min-width: 0;
- padding-inline-end: 8px;
+ padding-inline-end: 16px;
}
.file-title,
.file-description {
- line-height: 20px;
+ color: var(--color-new-tab-page-primary-foreground);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
- .file-description {
- color: var(--color-new-tab-page-secondary-foreground);
- font-size: 12px;
- }
-
- #iconBackground {
- align-items: center;
- background-color: var(--google-grey-100);
- border-radius: 4px;
- display: flex;
- flex-shrink: 0;
- height: 40px;
- justify-content: center;
- margin-inline-end: 16px;
- margin-inline-start: auto;
- width: 40px;
+ .file-title {
+ font-size: 13px;
+ line-height: 20px;
}
- .file-icon {
- height: 24px;
- width: 24px;
+ .file-description {
+ font-size: 11px;
+ line-height: 13px;
}
</style>
-<ntp-module-header
- show-info-button-dropdown
- disable-text="[[i18nRecursive('',
- 'modulesDisableButtonText',
- 'modulesDriveSentence2')]]"
- dismiss-text="[[i18nRecursive('',
- 'modulesDismissButtonText',
- 'modulesDriveFilesLower')]]"
- icon-src="icons/drive_logo.svg"
- show-info-button on-info-button-click="onInfoButtonClick_"
- show-dismiss-button on-dismiss-button-click="onDismissButtonClick_"
+<ntp-module-header-v2
+ id="moduleHeaderElementV2"
+ header-text="[[i18n('modulesDriveTitleV2')]]"
+ menu-item-groups="[[getMenuItemGroups_()]]"
+ more-actions-text="[[i18nRecursive('',
+ 'modulesMoreActions',
+ 'modulesDriveSentence')]]"
on-disable-button-click="onDisableButtonClick_"
- on-info-button-click="onInfoButtonClick_">
- $i18n{modulesDriveTitle}
-</ntp-module-header>
+ on-dismiss-button-click="onDismissButtonClick_"
+ on-info-button-click="onInfoButtonClick_"
+ on-menu-button-click="onMenuButtonClick_">
+</ntp-module-header-v2>
<div id="files">
<template id="fileRepeat" is="dom-repeat" items="[[files]]">
<a class="file" href="[[item.itemUrl.url]]" on-click="onFileClick_">
+ <div id="hover-layer"></div>
+ <img is="cr-auto-img"
+ class="file-icon"
+ draggable="false"
+ auto-src="[[getImageSrc_(item)]]">
+ </img>
<div class="file-info">
<div class="file-title">[[item.title]]</div>
<div class="file-description">[[item.justificationText]]</div>
</div>
- <div id="iconBackground">
- <img is="cr-auto-img"
- class="file-icon"
- draggable="false"
- auto-src="[[getImageSrc_(item)]]">
- </img>
- </div>
</a>
</template>
</div>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.ts
index 9f09c16cb82..207d98efb31 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/drive/module.ts
@@ -13,6 +13,7 @@ import {I18nMixin, loadTimeData} from '../../../i18n_setup.js';
import {DriveProxy} from '../../drive/drive_module_proxy.js';
import {InfoDialogElement} from '../../info_dialog.js';
import {ModuleDescriptor} from '../../module_descriptor.js';
+import {MenuItem, ModuleHeaderElementV2} from '../module_header.js';
import {getTemplate} from './module.html.js';
@@ -21,6 +22,7 @@ export interface DriveModuleElement {
fileRepeat: DomRepeat,
files: HTMLElement,
infoDialogRender: CrLazyRenderElement<InfoDialogElement>,
+ moduleHeaderElementV2: ModuleHeaderElementV2,
};
}
@@ -51,6 +53,37 @@ export class DriveModuleElement extends I18nMixin
file.mimeType;
}
+ private getMenuItemGroups_(): MenuItem[][] {
+ return [
+ [
+ {
+ action: 'dismiss',
+ icon: 'modules:visibility_off',
+ text: this.i18nRecursive(
+ '', 'modulesDismissButtonText', 'modulesDriveFilesLower'),
+ },
+ {
+ action: 'disable',
+ icon: 'modules:block',
+ text: this.i18nRecursive(
+ '', 'modulesDisableButtonTextV2', 'modulesDriveSentenceV2'),
+ },
+ {
+ action: 'info',
+ icon: 'modules:info',
+ text: this.i18n('moduleInfoButtonTitle'),
+ },
+ ],
+ [
+ {
+ action: 'customize-module',
+ icon: 'modules:tune',
+ text: this.i18n('modulesCustomizeButtonText'),
+ },
+ ],
+ ];
+ }
+
private onDisableButtonClick_() {
const disableEvent = new CustomEvent('disable-module', {
composed: true,
@@ -63,6 +96,20 @@ export class DriveModuleElement extends I18nMixin
this.dispatchEvent(disableEvent);
}
+ private onDismissButtonClick_() {
+ DriveProxy.getHandler().dismissModule();
+ this.dispatchEvent(new CustomEvent('dismiss-module-instance', {
+ bubbles: true,
+ composed: true,
+ detail: {
+ message: loadTimeData.getStringF(
+ 'dismissModuleToastMessage',
+ loadTimeData.getString('modulesDriveFilesSentence')),
+ restoreCallback: () => DriveProxy.getHandler().restoreModule(),
+ },
+ }));
+ }
+
private onFileClick_(e: DomRepeatEvent<File>) {
const clickFileEvent = new Event('usage', {composed: true});
this.dispatchEvent(clickFileEvent);
@@ -73,14 +120,21 @@ export class DriveModuleElement extends I18nMixin
private onInfoButtonClick_() {
this.$.infoDialogRender.get().showModal();
}
+
+ private onMenuButtonClick_(e: Event) {
+ this.$.moduleHeaderElementV2.showAt(e);
+ }
}
customElements.define(DriveModuleElement.is, DriveModuleElement);
-async function createDriveElement(): Promise<DriveModuleElement> {
+async function createDriveElement(): Promise<DriveModuleElement|null> {
const {files} = await DriveProxy.getHandler().getFiles();
+ if (files.length === 0) {
+ return null;
+ }
const element = new DriveModuleElement();
- element.files = files.slice(0, 2);
+ element.files = files;
return element;
}
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.html
index 64c0a2938c0..1b98dc55a4f 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.html
@@ -13,6 +13,11 @@
justify-content: center;
}
+ :host([format='narrow']) .tile-item,
+ :host([format='narrow']) img {
+ width: 120px;
+ }
+
.tile-item {
color: var(--color-new-tab-page-primary-foreground);
height: 120px;
@@ -27,13 +32,16 @@
disable-text="[[i18nRecursive('',
'modulesDisableButtonText',
'modulesDummyLower')]]"
+ more-actions-text="[[i18nRecursive('',
+ 'modulesMoreActions',
+ 'modulesDummyLower')]]"
icon-src="icons/generic_globe.svg"
on-disable-button-click="onDisableButtonClick_">
[[title]]
</ntp-module-header>
<div id="moduleContent">
<cr-grid id="tiles" columns="2">
- <template id="tileList" is="dom-repeat" items="[[tiles]]">
+ <template is="dom-repeat" items="[[tiles]]">
<div class="tile-item" title="[[item.label]]">
<img is="cr-auto-img" auto-src="[[item.imageUrl]]"></img>
<span>[[item.value]]</span>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.ts
index 2ebeee43a52..ff8882dcab5 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/dummy/module.ts
@@ -7,7 +7,7 @@ import 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js';
import '../../../strings.m.js';
import '../../module_header.js';
-import {DomRepeat, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {FooDataItem} from '../../../foo.mojom-webui.js';
import {I18nMixin, loadTimeData} from '../../../i18n_setup.js';
@@ -18,7 +18,6 @@ import {getTemplate} from './module.html.js';
export interface DummyModuleElement {
$: {
- tileList: DomRepeat,
tiles: HTMLElement,
};
}
@@ -47,16 +46,6 @@ export class DummyModuleElement extends I18nMixin
tiles: FooDataItem[];
override title: string;
- constructor() {
- super();
- this.initializeData_();
- }
-
- private async initializeData_() {
- const tileData = await FooProxy.getHandler().getData();
- this.tiles = tileData.data;
- }
-
private onDisableButtonClick_() {
this.dispatchEvent(new CustomEvent('disable-module', {
bubbles: true,
@@ -72,56 +61,15 @@ export class DummyModuleElement extends I18nMixin
customElements.define(DummyModuleElement.is, DummyModuleElement);
-function createDummyElement(titleId: string): DummyModuleElement {
- const element = new DummyModuleElement();
- element.title = loadTimeData.getString(titleId);
- return element;
+async function createDummyElements(): Promise<HTMLElement[]|null> {
+ const tiles = (await FooProxy.getHandler().getData()).data;
+ return (Array(12).fill(0).map(_ => {
+ const element = new DummyModuleElement();
+ element.title = loadTimeData.getString('modulesDummyTitle');
+ element.tiles = [...tiles];
+ return element;
+ }) as unknown) as HTMLElement[];
}
export const dummyV2Descriptor: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy',
- () => Promise.resolve(createDummyElement('modulesDummyTitle')));
-
-export const dummyV2Descriptor02: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy2',
- () => Promise.resolve(createDummyElement('modulesDummy2Title')));
-
-export const dummyV2Descriptor03: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy3',
- () => Promise.resolve(createDummyElement('modulesDummy3Title')));
-
-export const dummyV2Descriptor04: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy4',
- () => Promise.resolve(createDummyElement('modulesDummy4Title')));
-
-export const dummyV2Descriptor05: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy5',
- () => Promise.resolve(createDummyElement('modulesDummy5Title')));
-
-export const dummyV2Descriptor06: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy6',
- () => Promise.resolve(createDummyElement('modulesDummy6Title')));
-
-export const dummyV2Descriptor07: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy7',
- () => Promise.resolve(createDummyElement('modulesDummy7Title')));
-
-export const dummyV2Descriptor08: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy8',
- () => Promise.resolve(createDummyElement('modulesDummy8Title')));
-
-export const dummyV2Descriptor09: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy9',
- () => Promise.resolve(createDummyElement('modulesDummy9Title')));
-
-export const dummyV2Descriptor10: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy10',
- () => Promise.resolve(createDummyElement('modulesDummy10Title')));
-
-export const dummyV2Descriptor11: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy11',
- () => Promise.resolve(createDummyElement('modulesDummy11Title')));
-
-export const dummyV2Descriptor12: ModuleDescriptor = new ModuleDescriptor(
- /*id=*/ 'dummy12',
- () => Promise.resolve(createDummyElement('modulesDummy12Title')));
+ /*id=*/ 'dummy', createDummyElements);
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/cart/cart_tile.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/cart/cart_tile.html
new file mode 100644
index 00000000000..7d7dc2e3ee3
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/cart/cart_tile.html
@@ -0,0 +1,262 @@
+<style include="history-clusters-shared-style">
+ :host {
+ background: var(--color-new-tab-page-module-item-background);
+ border-radius: var(--ntp-module-item-border-radius);
+ display: inline-flex;
+ height: 100%;
+ min-width: 0;
+ width: 100%;
+ }
+
+ a:link,
+ a:visited,
+ a:hover,
+ a:active {
+ text-decoration: none;
+ }
+
+ a:hover {
+ background-color: var(--color-new-tab-page-module-item-background-hovered);
+ border-radius: var(--ntp-module-item-border-radius);
+ }
+
+ a:active {
+ background-color: var(--color-new-tab-page-active-background);
+ border-radius: var(--ntp-module-item-border-radius);
+ }
+
+ :host-context(.focus-outline-visible) a:focus,
+ a:focus-visible {
+ border-radius: var(--ntp-module-item-border-radius);
+ box-shadow: var(--ntp-focus-shadow);
+ outline: none;
+ }
+
+ #content {
+ flex: 1;
+ overflow: hidden;
+ position: relative;
+ }
+
+ #label-container {
+ background: linear-gradient(0, rgba(0, 0, 0, 0) 0%,
+ rgba(0, 0, 0, 0.4) 69.79%);
+ border-radius: var(--ntp-module-item-border-radius)
+ var(--ntp-module-item-border-radius) 0 0;
+ color: var(--color-new-tab-page-primary-foreground);
+ display: flex;
+ font-size: 11px;
+ position: absolute;
+ height: 40px;
+ margin: 4px;
+ width: calc(100% - 8px); /* Subtract parent margin */
+ z-index: 1;
+ }
+
+ #icon {
+ margin: 12px 0 0 12px;
+ }
+
+ #label {
+ color: white;
+ margin-top: 12px;
+ }
+
+ #images {
+ border-radius: var(--ntp-module-item-border-radius);
+ margin: 4px;
+ overflow: hidden;
+ position: relative;
+ height: 122px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ }
+
+ :host(:not([format='wide']):not([images-enabled])) #images {
+ height: 60px;
+ }
+
+ :host([format='wide']:not([show-related-searches])) #images {
+ height: 68px;
+ }
+
+ #images img {
+ object-fit: cover;
+ }
+
+ .large-image {
+ background-color: var(--color-new-tab-page-module-background);
+ height: 100%;
+ width: 100%;
+ }
+
+ .small-image,
+ :host([show-related-searches][format='wide']:not([image-count_='2']):not([image-count_='3'])) .small-image {
+ background: var(--color-new-tab-page-module-background);
+ border-radius: var(--ntp-module-item-border-radius);
+ height: calc(100%/2 - 2px); /* subtract half of gap for each image */
+ width: calc(100%/2 - 2px); /* subtract half of gap for each image */
+ }
+
+ :host([format='wide']:not([show-related-searches])) .small-image,
+ :host([image-count_='2']) .small-image,
+ :host([image-count_='3']) .small-image,
+ :host(:not([images-enabled]):not([image-count_='2']):not([image-count_='3'])) .small-image {
+ height: 100%;
+ width: calc(100%/2 - 2px); /* subtract half of gap for each image */
+ }
+
+ #extraImageCard {
+ align-items: center;
+ display: inline-flex;
+ color: var(--color-new-tab-page-icon-button-background-active);
+ font-size: 14px;
+ justify-content: center;
+ vertical-align: top;
+ }
+
+ #images page-favicon {
+ left: 50%;
+ position: absolute;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ #icon {
+ background-color: white;
+ /* Need both this and size so default icon shrinks. */
+ background-size: 10px;
+ height: 16px;
+ margin-inline-end: 8px;
+ width: 16px;
+ }
+
+ #title {
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ color: var(--color-new-tab-page-primary-foreground);
+ display: -webkit-box;
+ font-size: var(--ntp-module-text-size);
+ line-height: 20px;
+ margin: 16px 16px 4px 16px;
+ white-space: initial;
+ }
+
+ :host(:not([format='wide']):not([images-enabled])) #title {
+ margin-top: 8px;
+ }
+
+ :host([format='narrow'][show-discount-chip_]) #title {
+ -webkit-line-clamp: 1;
+ }
+
+ :host([format='wide']) #title {
+ -webkit-line-clamp: 1;
+ margin: 12px 16px 8px;
+ }
+
+ #titleAnnotation {
+ background: var(--color-sys-tonal-container);
+ color: var(--color-sys-on-tonal-container);
+ min-width: 60px;
+ }
+
+ #annotationContainer {
+ align-items: start;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ margin-inline: 16px;
+ }
+
+ #discountChip {
+ background: var(--color-sys-tertiary-container);
+ color: var(--color-sys-on-tertiary-container);
+ min-width: 40px;
+ }
+
+ .annotation {
+ align-items: center;
+ border-radius: 4px;
+ display: flex;
+ font-size: 11px;
+ font-weight: 500;
+ height: 17px;
+ justify-content: center;
+ line-height: 16px;
+ padding-inline-end: 4px;
+ padding-inline-start: 4px;
+ text-align: center;
+ }
+
+ #date {
+ color: var(--color-new-tab-page-secondary-foreground);
+ min-width: max-content;
+ bottom: 16px;
+ font-size: 11px;
+ left: 16px;
+ line-height: 12px;
+ position: absolute;
+ }
+
+ /* Set styles for high contrast mode in Windows. */
+ @media (forced-colors: active) {
+ /* Set focus outline since box-shadow isn't visible in hcm */
+ :host-context(.focus-outline-visible) a:focus {
+ outline: var(--cr-focus-outline-hcm);
+ }
+
+ /* Set outline since background isn't visible in hcm */
+ a {
+ border-radius: 12px;
+ outline: var(--cr-border-hcm);
+ }
+ }
+</style>
+<a id="content" href="[[cart.cartUrl.url]]"
+ aria-label$="[[tileLabel_]]">
+ <div id="label-container">
+ <page-favicon id="icon" url="[[cart.cartUrl]]"
+ is-known-to-sync="[[visit.isKnownToSync]]" size="10">
+ </page-favicon>
+ <div id="label" class="truncate">[[cart.domain]]</div>
+ </div>
+ <div id="images">
+ <template is="dom-if" if="[[!hasMultipleImages_()]]" restamp>
+ <template is="dom-if" if="[[cart.productImageUrls.length]]" restamp>
+ <img class="large-image" is="cr-auto-img"
+ auto-src="[[getSingleImageToShow_()]]" draggable="false" alt="">
+ </img>
+ </template>
+ <template is="dom-if" if="[[!cart.productImageUrls.length]]" restamp>
+ <page-favicon class="large-image" id="fallbackImage"
+ url="[[cart.cartUrl]]" is-known-to-sync="true" size="24">
+ </page-favicon>
+ </template>
+ </template>
+ <template is="dom-if" if="[[hasMultipleImages_()]]" restamp>
+ <template id="imagesRepeat" is="dom-repeat"
+ items="[[getImagesToShow_()]]">
+ <img class="small-image" is="cr-auto-img" auto-src="[[item.url]]"
+ draggable="false" alt="">
+ </img>
+ </template>
+ <template is="dom-if" if="[[shouldShowExtraImagesCard_()]]" restamp>
+ <div id="extraImageCard" class="small-image">
+ +[[getExtraImagesCount_()]]
+ </div>
+ </template>
+ </template>
+ </div>
+ <div id="title" class="truncate">[[cart.merchant]]</div>
+ <div id="annotationContainer">
+ <div id="titleAnnotation" class="annotation" >
+ $i18n{modulesJourneysCartAnnotation}
+ </div>
+ <template is="dom-if" if="[[cart.discountText]]" restamp>
+ <div id="discountChip" class="annotation">[[cart.discountText]]</div>
+ </template>
+ </div>
+ <div id="date">[[cart.relativeDate]]</div>
+</a>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/cart/cart_tile.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/cart/cart_tile.ts
new file mode 100644
index 00000000000..4c069232082
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/cart/cart_tile.ts
@@ -0,0 +1,147 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '../../../history_clusters/page_favicon.js';
+
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {Cart} from '../../../../cart.mojom-webui.js';
+import {I18nMixin, loadTimeData} from '../../../../i18n_setup.js';
+
+import {getTemplate} from './cart_tile.html.js';
+
+export class CartTileModuleElementV2 extends I18nMixin
+(PolymerElement) {
+ static get is() {
+ return 'ntp-history-clusters-cart-tile-v2';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ /* The cart to display. */
+ cart: Object,
+
+ format: {
+ type: String,
+ reflectToAttribute: true,
+ },
+
+ imagesEnabled: {
+ type: Boolean,
+ reflectToAttribute: true,
+ },
+
+ showRelatedSearches: Boolean,
+
+ /* The label of the tile in a11y mode. */
+ tileLabel_: {
+ type: String,
+ computed: `computeTileLabel_(cart)`,
+ },
+
+ imageCount_: {
+ type: Number,
+ computed: `computeImageCount_()`,
+ reflectToAttribute: true,
+ },
+ };
+ }
+
+ cart: Cart;
+ format: string;
+ imagesEnabled:
+ boolean;
+ showRelatedSearches: boolean;
+ private imageCount_: number;
+ private tileLabel_: string;
+
+ override ready() {
+ super.ready();
+ }
+
+ private hasMultipleImages_(): boolean {
+ return this.cart.productImageUrls.length > 1;
+ }
+
+ private getSingleImageToShow_(): string {
+ return this.cart.productImageUrls[0].url;
+ }
+
+ private getImagesToShow_(): Object[] {
+ const images = this.cart.productImageUrls;
+ if (!this.showRelatedSearches && this.format === 'wide') {
+ return images.slice(0, 1);
+ } else {
+ if (images.length >= 4) {
+ if (this.imagesEnabled ||
+ (this.showRelatedSearches && this.format === 'wide')) {
+ return images.slice(0, (images.length > 4) ? 3 : 4);
+ } else {
+ return images.slice(0, 1);
+ }
+ } else if (images.length === 3) {
+ return images.slice(0, 1);
+ } else {
+ return images;
+ }
+ }
+ }
+
+ private shouldShowExtraImagesCard_(): boolean {
+ return this.showRelatedSearches ? (this.cart.productImageUrls.length > 2) :
+ true;
+ }
+
+ private getExtraImagesCount_(): number {
+ const images = this.cart.productImageUrls;
+ if (!this.showRelatedSearches && this.format === 'wide') {
+ return images.length - 1;
+ } else {
+ if (images.length >= 4) {
+ if (this.imagesEnabled ||
+ (this.showRelatedSearches && this.format === 'wide')) {
+ return images.length - 3;
+ } else {
+ return images.length - 1;
+ }
+ } else if (images.length === 3) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+ }
+
+ private computeImageCount_(): number {
+ return this.cart.productImageUrls.length;
+ }
+
+ private computeTileLabel_(): string {
+ const productCount = this.cart.productImageUrls.length;
+ const discountText = this.cart.discountText;
+ const merchantName = this.cart.merchant;
+ const merchantDomain = this.cart.domain;
+ const relativeDate = this.cart.relativeDate;
+
+ if (productCount === 0) {
+ return loadTimeData.getStringF(
+ 'modulesJourneysCartTileLabelDefault', discountText, merchantName,
+ merchantDomain, relativeDate);
+ } else if (productCount === 1) {
+ return loadTimeData.getStringF(
+ 'modulesJourneysCartTileLabelSingular', discountText, merchantName,
+ merchantDomain, relativeDate);
+ } else {
+ return loadTimeData.getStringF(
+ 'modulesJourneysCartTileLabelPlural', productCount, discountText,
+ merchantName, merchantDomain, relativeDate);
+ }
+ }
+}
+
+customElements.define(CartTileModuleElementV2.is, CartTileModuleElementV2);
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.html
new file mode 100644
index 00000000000..8765528a77a
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.html
@@ -0,0 +1,154 @@
+<style include="cr-icons cr-shared-style">
+ :host {
+ background: var(--color-new-tab-page-module-item-background);
+ border-radius: var(--ntp-module-item-border-radius);
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ }
+
+ :host([suggestion-chip-header-enabled_]),
+ :host([suggestion-chip-header-enabled_]) ntp-module-header-v2 {
+ background: transparent;
+ border: none;
+ }
+
+ @media (forced-colors: active) {
+ /* Set outline since background isn't visible in hcm */
+ :host {
+ border-radius: var(--ntp-module-item-border-radius);
+ outline: var(--cr-border-hcm);
+ }
+ }
+
+ #container {
+ background-color: var(--color-new-tab-page-module-item-background);
+ border: none;
+ border-radius: var(--ntp-module-item-border-radius);
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ padding: 0;
+ }
+
+ button {
+ font-family: inherit;
+ }
+
+ ntp-module-header-v2 {
+ width: 100%;
+ }
+
+ h2 {
+ text-align: start;
+ }
+
+ .label-container {
+ color: var(--color-new-tab-page-primary-foreground);
+ font-size: 20px;
+ height: 48px;
+ line-height: 24px;
+ padding: 0 16px;
+ }
+
+ :host(:not([suggestion-chip-header-enabled_]):hover) {
+ cursor: pointer;
+ }
+
+ :host(:not([suggestion-chip-header-enabled_]):hover) .hover-layer,
+ #suggestion-chip:hover .hover-layer {
+ background: var(--color-new-tab-page-module-item-background-hovered);
+ display: block;
+ inset: 0;
+ position: absolute;
+ }
+
+ .hover-layer {
+ border-radius: var(--ntp-module-item-border-radius);
+ display: none;
+ pointer-events: none;
+ }
+
+ :host([suggestion-chip-header-enabled_]) .label-container {
+ background: var(--color-new-tab-page-module-item-background);
+ border-radius: var(--ntp-module-item-border-radius);
+ }
+
+ :host([suggestion-chip-header-enabled_][format='wide']
+ :not([show-related-searches])) .label-container {
+ align-items: start;
+ }
+
+ h2 {
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ display: -webkit-box;
+ font-weight: inherit;
+ font-size: inherit;
+ margin: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ :host([suggestion-chip-header-enabled_]) h2 {
+ -webkit-line-clamp: 1;
+ }
+
+ #suggestion-chip {
+ display: flex;
+ font-size: var(--ntp-module-text-size);
+ height: 100%;
+ position: relative;
+ text-decoration: none;
+ }
+
+ #container:focus-visible,
+ #suggestion-chip:focus-visible {
+ box-shadow: var(--ntp-focus-shadow);
+ outline: none;
+ }
+
+ #suggestion-chip-icon {
+ --cr-icon-ripple-size: 16px;
+ --cr-icon-image: url(chrome://resources/images/icon_history.svg);
+ align-self: center;
+ background-color: var(--color-new-tab-page-primary-foreground);
+ margin: 16px 0;
+ }
+
+ #suggestion-chip-label {
+ align-self: center;
+ margin-inline-start: 12px;
+ line-height: 20px;
+ }
+</style>
+
+<template is="dom-if" if="[[!suggestionChipHeaderEnabled_]]">
+ <div class="hover-layer"></div>
+ <button id="container" on-click="onClick_">
+ <ntp-module-header-v2
+ header-text="[[i18n('modulesJourneysResumeJourney', '')]]"
+ on-menu-button-click="onMenuButtonClick_"
+ menu-item-groups="[[getMenuItemGroups_()]]"
+ more-actions-text="[[i18n('modulesMoreActions', clusterLabel)]]">
+ </ntp-module-header-v2>
+ <h2 id="label" class="label-container">
+ [[clusterLabel]]
+ </h2>
+ </button>
+</template>
+<template is="dom-if" if="[[suggestionChipHeaderEnabled_]]">
+ <ntp-module-header-v2
+ header-text="[[i18n('modulesJourneysResumeJourney', '')]]"
+ on-menu-button-click="onMenuButtonClick_"
+ menu-item-groups="[[getMenuItemGroups_()]]"
+ more-actions-text="[[i18n('modulesMoreActions', clusterLabel)]]">
+ </ntp-module-header-v2>
+ <a id="suggestion-chip" class="label-container" href="[[normalizedUrl.url]]"
+ on-click="onSuggestClick_">
+ <div class="hover-layer"></div>
+ <div id="suggestion-chip-icon" class="cr-icon"></div>
+ <h2 id="suggestion-chip-label">[[clusterLabel]]</h2>
+ </a>
+</template>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.ts
new file mode 100644
index 00000000000..af4bcb6f959
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.ts
@@ -0,0 +1,109 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '../module_header.js';
+import '../icons.html.js';
+import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {I18nMixin, loadTimeData} from '../../../i18n_setup.js';
+import {MenuItem, ModuleHeaderElementV2} from '../module_header.js';
+
+import {getTemplate} from './header_tile.html.js';
+
+/** Element that displays a header inside a module. */
+const ElementBase = I18nMixin(PolymerElement);
+export class HistoryClustersHeaderElementV2 extends ElementBase {
+ static get is() {
+ return 'history-clusters-header-v2';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ clusterLabel: String,
+
+ /** Whether suggestion chip header style will show. */
+ suggestionChipHeaderEnabled_: {
+ type: Boolean,
+ reflectToAttribute: true,
+ value: () => loadTimeData.getBoolean(
+ 'historyClustersSuggestionChipHeaderEnabled'),
+ },
+ };
+ }
+
+ clusterId: number;
+ clusterLabel: string;
+ normalizedUrl: Url;
+
+ private onClick_(e: Event) {
+ e.stopPropagation();
+ this.dispatchEvent(new CustomEvent(
+ 'show-all-button-click', {bubbles: true, composed: true}));
+ }
+
+ private onSuggestClick_(e: Event) {
+ e.stopPropagation();
+ this.dispatchEvent(
+ new CustomEvent('suggest-click', {bubbles: true, composed: true}));
+ }
+
+ private onMenuButtonClick_(e: Event) {
+ e.stopPropagation();
+ const moduleHeader = this.shadowRoot!.querySelector<ModuleHeaderElementV2>(
+ 'ntp-module-header-v2')!;
+ moduleHeader.showAt(e);
+ }
+
+ private getMenuItemGroups_(): MenuItem[][] {
+ return [
+ [
+ {
+ action: 'done',
+ icon: 'modules:done',
+ text: this.i18n('modulesJourneysDoneButton'),
+ },
+ {
+ action: 'dismiss',
+ icon: 'modules:thumb_down',
+ text: this.i18n('modulesJourneysDismissButton'),
+ },
+ {
+ action: 'disable',
+ icon: 'modules:block',
+ text: this.i18nRecursive(
+ '', 'modulesDisableButtonTextV2', 'modulesThisTypeOfCardText'),
+ },
+ {
+ action: 'show-all',
+ icon: 'modules:dock_to_left',
+ text: this.i18n('modulesJourneysShowAllButton'),
+ },
+ {
+ action: 'info',
+ icon: 'modules:info',
+ text: this.i18n('moduleInfoButtonTitle'),
+ },
+ ],
+ [
+ {
+ action: 'customize-module',
+ icon: 'modules:tune',
+ text: this.i18n('modulesCustomizeButtonText'),
+ },
+ ],
+ ];
+ }
+}
+
+customElements.define(
+ HistoryClustersHeaderElementV2.is, HistoryClustersHeaderElementV2);
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/history_clusters.gni b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/history_clusters.gni
index 87b5463bf49..f6934528cd9 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/history_clusters.gni
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/history_clusters.gni
@@ -4,8 +4,13 @@
# List of files that should be passed to html_to_wrapper().
history_clusters_v2_web_component_files = [
+ "modules/v2/history_clusters/cart/cart_tile.ts",
+ "modules/v2/history_clusters/header_tile.ts",
"modules/v2/history_clusters/module.ts",
- "modules/v2/history_clusters/module_header.ts",
"modules/v2/history_clusters/suggest_tile.ts",
"modules/v2/history_clusters/visit_tile.ts",
]
+
+# List of files that don't need to be passed to html_to_wrapper().
+history_clusters_v2_non_web_component_files =
+ [ "modules/v2/history_clusters/history_clusters_proxy.ts" ]
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/history_clusters_proxy.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/history_clusters_proxy.ts
new file mode 100644
index 00000000000..4d24a6dfac0
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/history_clusters_proxy.ts
@@ -0,0 +1,32 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {PageHandler, PageHandlerRemote} from '../../../history_clusters_v2.mojom-webui.js';
+
+export interface HistoryClustersProxy {
+ handler: PageHandlerRemote;
+}
+
+export class HistoryClustersProxyImpl implements HistoryClustersProxy {
+ handler: PageHandlerRemote;
+
+ constructor(handler: PageHandlerRemote) {
+ this.handler = handler;
+ }
+
+ static getInstance(): HistoryClustersProxy {
+ if (instance) {
+ return instance;
+ }
+
+ const handler = PageHandler.getRemote();
+ return instance = new HistoryClustersProxyImpl(handler);
+ }
+
+ static setInstance(obj: HistoryClustersProxy) {
+ instance = obj;
+ }
+}
+
+let instance: HistoryClustersProxy|null = null;
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.html
index f90ad59fce8..c81cce474bc 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.html
@@ -1,14 +1,26 @@
-<style include="cr-icons">
+<style include="cr-icons cr-shared-style">
:host {
--grid-gap: 8px;
- height: 410px;
- width: 100%;
+ height: 432px;
+ }
+
+ :host([images-enabled_]) {
+ height: 496px;
+ }
+
+ :host([images-enabled_]:not([show-related-searches])) {
+ align-self: flex-start;
+ height: 370px;
}
:host([format='wide']) {
height: 244px;
}
+ :host([format='wide']:not([show-related-searches])) {
+ height: 192px;
+ }
+
cr-icon-button {
--cr-icon-button-icon-size: 20px;
--cr-icon-button-fill-color: var(--color-new-tab-page-primary-foreground);
@@ -18,34 +30,48 @@
margin-inline-start: 0;
}
- #doneButton {
- --cr-icon-image: url(chrome://resources/images/icon_checkmark.svg);
- }
-
#layout {
display: grid;
grid-gap: var(--grid-gap);
grid-template: repeat(7, 1fr) / repeat(2, 1fr);
- height: 100%;
+ height: calc(100% - 16px);
+ padding: 8px;
+ }
+
+ :host([images-enabled_]:not([show-related-searches])) #layout {
+ grid-template: repeat(6, 1fr) / repeat(2, 1fr);
+ }
+
+ :host([images-enabled_]) #layout {
+ grid-template: repeat(8, 1fr) / repeat(2, 1fr);
}
:host([format='wide']) #layout {
grid-template: repeat(4, 1fr) / repeat(4, 1fr);
}
- ntp-module-header-v2 {
+ :host([format='wide']:not([show-related-searches])) #layout {
+ grid-template: repeat(1, 1fr) / repeat(4, 1fr);
+ }
+
+ history-clusters-header-v2 {
grid-column: 1 / 3;
grid-row: 1 / 3;
}
+ :host([format='wide']:not([show-related-searches]))
+ history-clusters-header-v2 {
+ grid-column: 1 / 3;
+ grid-row: 1;
+ }
+
#first-tile {
grid-column: 1 / 2;
grid-row: 3 / 6;
}
- #last-tile {
- grid-column: 2 / 3;
- grid-row: 3 / 6;
+ :host([images-enabled_]) #first-tile {
+ grid-row: 3 / 7;
}
:host([format='wide']) #first-tile {
@@ -53,88 +79,112 @@
grid-row: 1 / 5;
}
- :host([format='wide']) #last-tile {
- grid-column: 4 / 5;
- grid-row: 1 / 5;
+ :host([format='wide']:not([show-related-searches])) #first-tile {
+ grid-column: 3 / 4;
+ grid-row: 1;
}
- #first-related-search {
- border-radius: 12px 12px 0 0;
+ #last-tile {
+ grid-column: 2 / 3;
+ grid-row: 3 / 6;
}
- #related-searches-divider {
- border-bottom: 1px solid
- var(--color-new-tab-page-history-clusters-module-item-background);
- left: 16px;
- position: absolute;
- right: 16px;
- top: 50%;
+ :host([images-enabled_]) #last-tile {
+ grid-row: 3 / 7;
+ }
+
+ :host([format='wide']) #last-tile {
+ grid-column: 4 / 5;
+ grid-row: 1 / 5;
}
- #last-related-search {
- border-radius: 0 0 12px 12px;
+ :host([format='wide']:not([show-related-searches])) #last-tile {
+ grid-column: 4 / 5;
+ grid-row: 1;
}
#related-searches {
- background: var(--color-new-tab-page-module-background);
+ background: var(--color-new-tab-page-module-item-background);
border-radius: var(--ntp-module-item-border-radius);
- display: grid;
- grid-auto-flow: row;
+ display: flex;
+ flex-direction: column;
grid-column: 1 / 3;
grid-row: 6 / 8;
- position: relative;
width: 100%;
}
+ :host([images-enabled_]:not([format='wide'])) #related-searches {
+ grid-row: 7 / 9;
+ }
+
:host([format='wide']) #related-searches {
- display: grid;
- grid-auto-flow: row;
grid-column: 1 / 3;
grid-row: 3 / 5;
width: 100%;
}
+
+ .related-search {
+ flex-grow: 1;
+ }
+
+ #related-searches-divider {
+ background-color: var(--color-new-tab-page-module-background);
+ border: none;
+ height: 1px;
+ margin: 0 16px;
+ }
</style>
<div id="layout">
- <ntp-module-header-v2
- disable-text="[[i18nRecursive('',
- 'modulesDisableButtonText',
- 'modulesJourneyDisable')]]"
- dismiss-text="[[i18n('modulesDismissButtonText', cluster.label)]]"
+ <history-clusters-header-v2
on-disable-button-click="onDisableButtonClick_"
on-dismiss-button-click="onDismissButtonClick_"
- on-info-button-click="onInfoButtonClick_">
- [[cluster.label]]
- <cr-icon-button id="doneButton" slot="title-actions">
- </cr-icon-button>
- <button id="openAllInTabGroupButton" class="dropdown-item"
- on-click="onOpenAllInTabGroupClick_"
- slot="action-menu-items" type="button">
- [[i18n('modulesJourneysOpenAllInNewTabGroupButtonText')]]
- </button>
- </ntp-module-header-v2>
+ on-done-button-click="onDoneButtonClick_"
+ on-info-button-click="onInfoButtonClick_"
+ on-show-all-button-click="onShowAllButtonClick_"
+ on-suggest-click="recordClick_"
+ cluster-label="[[computeLabel_()]]"
+ normalized-url="[[cluster.visits.0.normalizedUrl]]"
+ format$="[[format]]"
+ show-related-searches$="[[showRelatedSearches]]">
+ </history-clusters-header-v2>
<ntp-history-clusters-visit-tile id="first-tile"
- visit="[[cluster.visits.0]]" on-click="onVisitTileClick_"
- on-aux-click="onVisitTileClick_">
- </ntp-history-clusters-visit-tile>
- <ntp-history-clusters-visit-tile id="last-tile"
- visit="[[cluster.visits.1]]" on-click="onVisitTileClick_"
- on-aux-click="onVisitTileClick_">
+ visit="[[cluster.visits.1]]" discount="[[discounts.1]]"
+ on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_"
+ format="[[format]]" show-related-searches$="[[showRelatedSearches]]"
+ images-enabled="[[imagesEnabled_]]">
</ntp-history-clusters-visit-tile>
- <div id="related-searches">
+ <template is="dom-if" if="[[shouldShowCartTile_(cart)]]" restamp>
+ <ntp-history-clusters-cart-tile-v2
+ id="last-tile" cart="[[cart]]"
+ on-click="recordClick_" on-aux-click="recordClick_"
+ format="[[format]]" show-related-searches$="[[showRelatedSearches]]"
+ images-enabled="[[imagesEnabled_]]">
+ </ntp-history-clusters-cart-tile-v2>
+ </template>
+ <template is="dom-if" if="[[!shouldShowCartTile_(cart)]]" restamp>
+ <ntp-history-clusters-visit-tile id="last-tile"
+ visit="[[cluster.visits.2]]" discount="[[discounts.2]]"
+ on-click="onVisitTileClick_" on-aux-click="onVisitTileClick_"
+ format="[[format]]" show-related-searches$="[[showRelatedSearches]]"
+ images-enabled="[[imagesEnabled_]]">
+ </ntp-history-clusters-visit-tile>
+ </template>
+ <div id="related-searches" hidden="[[!showRelatedSearches]]">
<ntp-history-clusters-suggest-tile-v2 id="first-related-search"
- related-search="[[cluster.relatedSearches.0]]"
- on-click="onSuggestTileClick_" on-aux-click="onSuggestTileClick_">
+ class="related-search" related-search="[[cluster.relatedSearches.0]]"
+ on-click="onSuggestTileClick_" on-aux-click="onSuggestTileClick_"
+ is-first="true">
</ntp-history-clusters-suggest-tile-v2>
<div id="related-searches-divider"></div>
<ntp-history-clusters-suggest-tile-v2 id="last-related-search"
- related-search="[[cluster.relatedSearches.1]]"
+ class="related-search" related-search="[[cluster.relatedSearches.1]]"
on-click="onSuggestTileClick_" on-aux-click="onSuggestTileClick_">
</ntp-history-clusters-suggest-tile-v2>
</div>
</div>
<cr-lazy-render id="infoDialogRender">
<template>
- <ntp-info-dialog inner-h-t-m-l="[[i18nAdvanced('modulesJourneysInfo')]]">
+ <ntp-info-dialog inner-h-t-m-l="[[getInfo_(discounts)]]">
</ntp-info-dialog>
</template>
</cr-lazy-render>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.ts
index 10c98a1a6aa..c1f573cf6ec 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module.ts
@@ -2,24 +2,32 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import './module_header.js';
+import './cart/cart_tile.js';
+import './header_tile.js';
import './visit_tile.js';
import './suggest_tile.js';
+import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import '../../../discount.mojom-webui.js';
import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
import {assert} from 'chrome://resources/js/assert_ts.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {Cluster, URLVisit} from '../../../history_cluster_types.mojom-webui.js';
+import {Cart} from '../../../cart.mojom-webui.js';
+import {Cluster, InteractionState} from '../../../history_cluster_types.mojom-webui.js';
+import {LayoutType} from '../../../history_clusters_layout_type.mojom-webui.js';
import {I18nMixin, loadTimeData} from '../../../i18n_setup.js';
-import {HistoryClustersProxyImpl} from '../../history_clusters/history_clusters_proxy.js';
+import {NewTabPageProxy} from '../../../new_tab_page_proxy.js';
import {InfoDialogElement} from '../../info_dialog';
import {ModuleDescriptor} from '../../module_descriptor.js';
+import {HistoryClustersProxyImpl} from './history_clusters_proxy.js';
import {getTemplate} from './module.html.js';
export const MAX_MODULE_ELEMENT_INSTANCES = 3;
+const CLUSTER_MIN_REQUIRED_URL_VISITS = 3;
+
export interface HistoryClustersModuleElement {
$: {
infoDialogRender: CrLazyRenderElement<InfoDialogElement>,
@@ -40,28 +48,103 @@ export class HistoryClustersModuleElement extends I18nMixin
return {
layoutType: Number,
+ /** The cart displayed by this element, could be null. */
+ cart: {
+ type: Object,
+ value: null,
+ },
+
+ /**
+ The discounts displayed on the visit tiles of this element, could be
+ empty.
+ */
+ discounts: {
+ type: Array,
+ value: [],
+ },
+
/** The cluster displayed by this element. */
cluster: {
type: Object,
- observer: 'onClusterUpdated_',
},
- searchResultsPage_: Object,
-
format: {
type: String,
- value: 'narrow',
+ reflectToAttribute: true,
+ },
+
+ imagesEnabled_: {
+ type: Boolean,
+ reflectToAttribute: true,
+ value: () => loadTimeData.getBoolean('historyClustersImagesEnabled'),
+ },
+
+ showRelatedSearches: {
+ type: Boolean,
+ computed: `computeShowRelatedSearches(cluster)`,
reflectToAttribute: true,
},
};
}
+ cart: Cart|null;
+ discounts: string[];
cluster: Cluster;
format: string;
- private searchResultsPage_: URLVisit;
+ showRelatedSearches: boolean;
+ private imagesEnabled_: boolean;
+ private setDisabledModulesListenerId_: number|null = null;
- private onClusterUpdated_() {
- this.searchResultsPage_ = this.cluster!.visits[0];
+ override connectedCallback() {
+ super.connectedCallback();
+
+ if (loadTimeData.getBoolean(
+ 'modulesChromeCartInHistoryClustersModuleEnabled')) {
+ this.setDisabledModulesListenerId_ =
+ NewTabPageProxy.getInstance()
+ .callbackRouter.setDisabledModules.addListener(
+ async (_: boolean, ids: string[]) => {
+ if (ids.includes('chrome_cart')) {
+ this.cart = null;
+ } else if (!this.cart) {
+ const {cart} =
+ await HistoryClustersProxyImpl.getInstance()
+ .handler.getCartForCluster(this.cluster);
+ this.cart = cart;
+ }
+ });
+ }
+ }
+
+ override disconnectedCallback() {
+ super.disconnectedCallback();
+
+ if (this.setDisabledModulesListenerId_) {
+ NewTabPageProxy.getInstance().callbackRouter.removeListener(
+ this.setDisabledModulesListenerId_);
+ }
+ }
+
+ override ready() {
+ super.ready();
+
+ HistoryClustersProxyImpl.getInstance().handler.recordLayoutTypeShown(
+ this.imagesEnabled_ ? LayoutType.kImages : LayoutType.kTextOnly,
+ this.cluster.id);
+ }
+
+ private computeShowRelatedSearches(): boolean {
+ return this.cluster.relatedSearches.length > 1;
+ }
+
+ private computeLabel_(): string {
+ return this.cluster.label.replace(/[“”]+/g, '');
+ }
+
+ private shouldShowCartTile_(cart: Object): boolean {
+ return loadTimeData.getBoolean(
+ 'modulesChromeCartInHistoryClustersModuleEnabled') &&
+ !!cart;
}
private onDisableButtonClick_() {
@@ -69,22 +152,47 @@ export class HistoryClustersModuleElement extends I18nMixin
composed: true,
detail: {
message: loadTimeData.getStringF(
- 'disableQuestsModuleToastMessage',
- loadTimeData.getString('disableQuestsModuleToastName')),
+ 'modulesDisableToastMessage',
+ loadTimeData.getString('modulesThisTypeOfCardText')),
},
});
this.dispatchEvent(disableEvent);
}
private onDismissButtonClick_() {
- HistoryClustersProxyImpl.getInstance().handler.dismissCluster(
- [this.searchResultsPage_, ...this.cluster.visits], this.cluster.id);
- this.dispatchEvent(new CustomEvent('dismiss-module', {
+ HistoryClustersProxyImpl.getInstance()
+ .handler.updateClusterVisitsInteractionState(
+ this.cluster.visits, InteractionState.kHidden);
+ this.dispatchEvent(new CustomEvent('dismiss-module-instance', {
+ bubbles: true,
+ composed: true,
+ detail: {
+ message: loadTimeData.getStringF(
+ 'dismissModuleToastMessage', this.cluster.label),
+ restoreCallback: () => {
+ HistoryClustersProxyImpl.getInstance()
+ .handler.updateClusterVisitsInteractionState(
+ this.cluster.visits, InteractionState.kDefault);
+ },
+ },
+ }));
+ }
+
+ private onDoneButtonClick_() {
+ HistoryClustersProxyImpl.getInstance()
+ .handler.updateClusterVisitsInteractionState(
+ this.cluster.visits, InteractionState.kDone);
+ this.dispatchEvent(new CustomEvent('dismiss-module-instance', {
bubbles: true,
composed: true,
detail: {
message: loadTimeData.getStringF(
'dismissModuleToastMessage', this.cluster.label),
+ restoreCallback: () => {
+ HistoryClustersProxyImpl.getInstance()
+ .handler.updateClusterVisitsInteractionState(
+ this.cluster.visits, InteractionState.kDefault);
+ },
},
}));
}
@@ -93,17 +201,49 @@ export class HistoryClustersModuleElement extends I18nMixin
this.$.infoDialogRender.get().showModal();
}
- private onShowAllClick_() {
- assert(this.cluster.label.length >= 2);
+ private onShowAllButtonClick_() {
+ assert(this.cluster.label.length >= 2, 'Unexpected cluster label length');
HistoryClustersProxyImpl.getInstance().handler.showJourneysSidePanel(
this.cluster.label.substring(1, this.cluster.label.length - 1));
+ this.dispatchEvent(new Event('usage', {bubbles: true, composed: true}));
}
- private onOpenAllInTabGroupClick_() {
- const urls = [this.searchResultsPage_, ...this.cluster.visits].map(
- visit => visit.normalizedUrl);
- HistoryClustersProxyImpl.getInstance().handler.openUrlsInTabGroup(
- urls, this.cluster.tabGroupName ?? null);
+ private onSuggestTileClick_(e: Event) {
+ this.recordTileClickIndex_(e.target as HTMLElement, 'Suggest');
+ this.recordClick_();
+ }
+
+ private onVisitTileClick_(e: Event) {
+ this.recordTileClickIndex_(e.target as HTMLElement, 'Visit');
+ this.recordClick_();
+ }
+
+ private recordTileClickIndex_(tile: HTMLElement, tileType: string) {
+ const layoutType =
+ this.imagesEnabled_ ? LayoutType.kImages : LayoutType.kTextOnly;
+ const index = Array.from(tile.parentNode!.children).indexOf(tile);
+ chrome.metricsPrivate.recordValue(
+ {
+ metricName: `NewTabPage.HistoryClusters.Layout${layoutType}.${
+ tileType!}Tile.ClickIndex`,
+ type: chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LINEAR,
+ min: 0,
+ max: 10,
+ buckets: 10,
+ },
+ index);
+ }
+
+ private recordClick_() {
+ HistoryClustersProxyImpl.getInstance().handler.recordClick(this.cluster.id);
+ this.dispatchEvent(new Event('usage', {bubbles: true, composed: true}));
+ }
+
+ private getInfo_(discounts: string[]): TrustedHTML {
+ const hasDiscount = discounts.some((discount) => !!discount);
+ return this.i18nAdvanced(
+ hasDiscount ? 'modulesHistoryWithDiscountInfo' :
+ 'modulesJourneysInfo');
}
}
@@ -114,7 +254,31 @@ async function createElement(cluster: Cluster):
Promise<HistoryClustersModuleElement> {
const element = new HistoryClustersModuleElement();
element.cluster = cluster;
+ if (loadTimeData.getBoolean(
+ 'modulesChromeCartInHistoryClustersModuleEnabled')) {
+ const {cart} =
+ await HistoryClustersProxyImpl.getInstance().handler.getCartForCluster(
+ cluster);
+ element.cart = cart;
+ }
+ element.discounts = [];
+ if (loadTimeData.getBoolean('historyClustersModuleDiscountsEnabled')) {
+ const {discounts} = await HistoryClustersProxyImpl.getInstance()
+ .handler.getDiscountsForCluster(cluster);
+ for (const visit of cluster.visits) {
+ let discountInValue = '';
+ for (const [url, discount] of discounts) {
+ if (url.url === visit.normalizedUrl.url && discount.length > 0) {
+ discountInValue = discount[0].valueInText;
+ visit.normalizedUrl.url = discount[0].annotatedVisitUrl.url;
+ }
+ }
+ element.discounts.push(discountInValue);
+ }
+ } else {
+ element.discounts = Array(cluster.visits.length).fill('');
+ }
return element;
}
@@ -130,7 +294,9 @@ async function createElements(): Promise<HTMLElement[]|null> {
if (elements.length === MAX_MODULE_ELEMENT_INSTANCES) {
break;
}
- elements.push(await createElement(clusters[i]));
+ if (clusters[i].visits.length >= CLUSTER_MIN_REQUIRED_URL_VISITS) {
+ elements.push(await createElement(clusters[i]));
+ }
}
return (elements as unknown) as HTMLElement[];
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module_header.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module_header.html
deleted file mode 100644
index 230c4b720b3..00000000000
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module_header.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<style include="cr-icons">
- :host {
- background: var(--color-new-tab-page-module-background);
- border-radius: var(--ntp-module-item-border-radius);
- display: flex;
- flex-direction: column;
- padding: 16px;
- }
-
- #titleContainer {
- align-items: center;
- display: flex;
- height: 20px;
- }
-
- #title {
- color: var(--color-new-tab-page-primary-foreground);
- font-size: 15px;
- font-weight: normal;
- }
-
- #headerSpacer {
- flex-grow: 1;
- }
-
- cr-action-menu {
- --cr-menu-shadow: var(--ntp-menu-shadow);
- }
-
- cr-icon-button {
- --cr-icon-button-icon-size: 20px;
- --cr-icon-button-fill-color: var(--color-new-tab-page-primary-foreground);
- --cr-icon-button-hover-background-color:
- var(--color-new-tab-page-control-background-hovered);
- margin-inline-end: -4px;
- margin-inline-start: 0;
- }
-
- #menuButton {
- margin-inline-end: -10px;
- }
-
- #menuButton {
- background-color: var(--color-new-tab-page-module-scroll-button);
- height: 18px;
- margin: 0;
- width: 18px;
- }
-
- #menuButton:hover {
- background-color: var(--color-new-tab-page-module-scroll-button-hover);
- }
-
- #label {
- -webkit-line-clamp: 2;
- display: flex;
- height: 48px;
- line-height: 24px;
- margin: 36px 8px 0 0;
- }
-
- #label span {
- align-self: flex-end;
- font-size: 24px;
- }
-</style>
-<div id="titleContainer">
- <h2 id="title">[[i18n('modulesJourneysResumeJourney', '')]]</h2>
- <div id="headerSpacer"></div>
- <slot name="title-actions"></slot>
- <cr-icon-button id="menuButton" title="$i18n{moreActions}"
- class="icon-more-vert" on-click="onMenuButtonClick_">
- </cr-icon-button>
-</div>
-<div id="label">
- <span><slot></slot></span>
-</div>
-
-<cr-action-menu id="actionMenu">
- <slot name="action-menu-items"></slot>
- <button id="openInfoDialogButton" class="dropdown-item"
- on-click="onInfoButtonClick_" type="button">
- [[i18n('moduleInfoButtonTitle')]]
- </button>
- <button id="dismissButton" class="dropdown-item"
- on-click="onDismissButtonClick_">
- [[dismissText]]
- </button>
- <button id="disableButton" class="dropdown-item"
- on-click="onDisableButtonClick_">
- [[disableText]]
- </button>
- <button id="customizeButton" class="dropdown-item"
- on-click="onCustomizeButtonClick_">
- $i18n{modulesCustomizeButtonText}
- </button>
-</cr-action-menu>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module_header.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module_header.ts
deleted file mode 100644
index 0b3dbde153d..00000000000
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/module_header.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-
-import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {I18nMixin} from '../../../i18n_setup.js';
-
-import {getTemplate} from './module_header.html.js';
-
-export interface ModuleHeaderElementV2 {
- $: {
- actionMenu: CrActionMenuElement,
- };
-}
-
-/** Element that displays a header inside a module. */
-export class ModuleHeaderElementV2 extends I18nMixin
-(PolymerElement) {
- static get is() {
- return 'ntp-module-header-v2';
- }
-
- static get template() {
- return getTemplate();
- }
-
- static get properties() {
- return {
- dismissText: String,
- disableText: String,
- };
- }
-
- dismissText: string;
- disableText: string;
-
- private onInfoButtonClick_() {
- this.$.actionMenu.close();
- this.dispatchEvent(
- new Event('info-button-click', {bubbles: true, composed: true}));
- }
-
- private onMenuButtonClick_(e: Event) {
- this.$.actionMenu.showAt(e.target as HTMLElement);
- }
-
- private onDismissButtonClick_() {
- this.$.actionMenu.close();
- this.dispatchEvent(new Event('dismiss-button-click', {bubbles: true}));
- }
-
- private onDisableButtonClick_() {
- this.$.actionMenu.close();
- this.dispatchEvent(new Event('disable-button-click', {bubbles: true}));
- }
-
- private onCustomizeButtonClick_() {
- this.$.actionMenu.close();
- this.dispatchEvent(
- new Event('customize-module', {bubbles: true, composed: true}));
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'ntp-module-header-v2': ModuleHeaderElementV2;
- }
-}
-
-customElements.define(ModuleHeaderElementV2.is, ModuleHeaderElementV2);
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/suggest_tile.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/suggest_tile.html
index 2b50f580b89..75003afc44c 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/suggest_tile.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/suggest_tile.html
@@ -1,4 +1,12 @@
<style include="history-clusters-shared-style">
+ .related-search {
+ display: flex;
+ flex-direction: row;
+ height: 100%;
+ position: relative;
+ width: 100%;
+ }
+
a:link,
a:visited,
a:hover,
@@ -6,23 +14,44 @@
text-decoration: none;
}
- :host-context(.focus-outline-visible) :focus,
- :focus-visible {
+ :host-context(.focus-outline-visible) a:focus,
+ a:focus-visible {
box-shadow: var(--ntp-focus-shadow);
outline: none;
}
- .related-search {
- display: flex;
- flex-direction: row;
- height: 100%;
- width: 100%;
+ .hover-layer {
+ display: none;
+ background: var(--color-new-tab-page-module-item-background-hovered);
+ inset: 0;
+ pointer-events: none;
+ position: absolute;
+ }
+
+ a,
+ .hover-layer {
+ border-radius: 0 0 var(--ntp-module-item-border-radius)
+ var(--ntp-module-item-border-radius);
+ }
+
+ :host([is-first]) a,
+ :host([is-first]) .hover-layer {
+ border-radius: var(--ntp-module-item-border-radius)
+ var(--ntp-module-item-border-radius) 0 0;
+ }
+
+ a:hover .hover-layer {
+ display: block;
+ }
+
+ a:active .hover-layer {
+ background: var(--color-new-tab-page-active-background);
}
.title {
color: var(--color-new-tab-page-primary-foreground);
font-size: var(--ntp-module-text-size);
- margin: auto 0;
+ margin: auto 8px auto 0;
}
.icon {
@@ -33,6 +62,7 @@
background-position: center center;
background-repeat: no-repeat;
background-size: 20px;
+ flex-shrink: 0;
height: 20px;
margin: auto 16px;
width: 20px;
@@ -60,9 +90,10 @@
href="[[computeSearchUrl_(relatedSearch.query)]]"
aria-label$="[[i18n('modulesJourneysSearchSuggAcc',
relatedSearch.query)]]">
+ <div class="hover-layer"></div>
<div class="icon"
style="-webkit-mask-image:
url(//resources/images/icon_search.svg);">
</div>
<div class="title truncate">[[relatedSearch.query]]</div>
-</a> \ No newline at end of file
+</a>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.html
index 77ec9e8ee8c..72250900061 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.html
@@ -1,11 +1,22 @@
<style include="history-clusters-shared-style">
:host {
- background: var(--color-new-tab-page-module-background);
+ background: var(--color-new-tab-page-module-item-background);
border-radius: var(--ntp-module-item-border-radius);
- display: inline-block;
+ display: inline-flex;
+ height: 100%;
+ min-width: 0;
width: 100%;
}
+ .hover-layer {
+ display: none;
+ background: var(--color-new-tab-page-module-item-background-hovered);
+ border-radius: var(--ntp-module-item-border-radius);
+ inset: 0;
+ pointer-events: none;
+ position: absolute;
+ }
+
a:link,
a:visited,
a:hover,
@@ -13,22 +24,75 @@
text-decoration: none;
}
- :host-context(.focus-outline-visible) :focus,
- :focus-visible {
+ a:hover .hover-layer {
+ display: block;
+ }
+
+ a:active .hover-layer {
+ background: var(--color-new-tab-page-active-background);
+ }
+
+ :host-context(.focus-outline-visible) a:focus,
+ a:focus-visible {
border-radius: var(--ntp-module-item-border-radius);
box-shadow: var(--ntp-focus-shadow);
outline: none;
}
#content {
- display: inline-block;
+ flex: 1;
+ min-width: 0;
+ position: relative;
+ }
+
+ #label-container {
+ color: var(--color-new-tab-page-secondary-foreground);
+ display: flex;
+ flex-direction: row;
+ font-size: 11px;
+ height: 20px;
margin: 16px;
}
+ :host([images-enabled]) #label-container {
+ background: linear-gradient(0, rgba(0, 0, 0, 0) 0%,
+ rgba(0, 0, 0, 0.4) 69.79%);
+ border-radius: var(--ntp-module-item-border-radius)
+ var(--ntp-module-item-border-radius) 0 0;
+ color: var(--color-new-tab-page-primary-foreground);
+ display: flex;
+ font-size: 11px;
+ position: absolute;
+ height: 40px;
+ margin: 4px;
+ width: calc(100% - 8px); /* Subtract parent margin */
+ z-index: 1;
+ }
+
+ #label {
+ color: var(--color-new-tab-page-secondary-foreground);
+ }
+
+ :host([images-enabled]) #label {
+ color: white;
+ margin-top: 12px;
+ }
+
+ :host([images-enabled]) #icon {
+ margin: 12px 8px 0 12px;
+ }
+
#image {
background: var(--color-new-tab-page-module-background);
+ border-radius: var(--ntp-module-item-border-radius);
+ margin: 4px;
overflow: hidden;
position: relative;
+ height: 122px;
+ }
+
+ :host([format='wide']:not([show-related-searches])) #image {
+ height: 68px;
}
#image img {
@@ -37,7 +101,19 @@
width: 100%;
}
+ #image-icon {
+ height: 24px;
+ left: 50%;
+ position: absolute;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: 24px;
+ }
+
#icon {
+ background-color: white;
+ /* Need both this and size so default icon shrinks. */
+ background-size: 10px;
height: 16px;
margin-inline-end: 8px;
width: 16px;
@@ -45,46 +121,34 @@
#title {
-webkit-box-orient: vertical;
+ -webkit-line-clamp: 3;
color: var(--color-new-tab-page-primary-foreground);
display: -webkit-box;
font-size: var(--ntp-module-text-size);
- margin: 16px 0 16px 0;
- white-space: initial;
- }
-
- :host([format='wide']) #title {
- margin: 24px 0 86px 0;
- }
-
- #title {
- -webkit-line-clamp: 3;
height: 60px;
line-height: 20px;
+ margin: 16px;
+ white-space: initial;
}
- #label-container {
- color: var(--color-new-tab-page-secondary-foreground);
- display: flex;
- flex-direction: row;
- font-size: 11px;
- height: 20px;
+ :host([format='wide']) #title {
+ margin: 12px 16px;
}
#date {
- /*TODO: Update color */
- color: var(--color-new-tab-page-primary-foreground);
+ color: var(--color-new-tab-page-secondary-foreground);
min-width: max-content;
+ bottom: 16px;
+ font-size: 11px;
+ left: 16px;
+ line-height: 12px;
+ position: absolute;
}
#annotation {
display: none;
}
- /*TODO: Fix label size */
- #label {
- max-width: 100px;
- }
-
/* Set styles for high contrast mode in Windows. */
@media (forced-colors: active) {
/* Set focus outline since box-shadow isn't visible in hcm */
@@ -94,27 +158,58 @@
/* Set outline since background isn't visible in hcm */
a {
- border-radius: 12px;
+ border-radius: var(--ntp-module-item-border-radius);
outline: var(--cr-border-hcm);
}
}
+
+ #discountChip {
+ background: var(--color-sys-tertiary-container);
+ border-radius: 4px;
+ color: var(--color-sys-on-tertiary-container);
+ display: inline-block;
+ font-weight: 500;
+ font-size: 11px;
+ height: 18px;
+ line-height: 18px;
+ margin-inline-start: 16px;
+ margin-top: 8px;
+ padding-inline-end: 4px;
+ padding-inline-start: 4px;
+ text-align: center;
+ }
+
+ :host([has-discount]) #title {
+ -webkit-line-clamp: 1;
+ height: 20px;
+ margin-bottom: 0;
+ }
</style>
<a id="content" href="[[visit.normalizedUrl.url]]"
aria-label$="[[visit.pageTitle]], [[label_]], [[visit.relativeDate]]">
+ <div class="hover-layer"></div>
<div id="label-container">
<page-favicon id="icon" url="[[visit.normalizedUrl]]"
- is-known-to-sync="[[visit.isKnownToSync]]">
+ is-known-to-sync="[[visit.isKnownToSync]]" size="10">
</page-favicon>
<div id="label" class="truncate">[[label_]]</div>
</div>
- <div id="image">
+ <div id="image" hidden="[[!imagesEnabled]]">
<template is="dom-if" if="[[imageUrl_]]" restamp>
<img is="cr-auto-img" auto-src="[[imageUrl_.url]]"
draggable="false" alt="">
</img>
</template>
+ <template is="dom-if" if="[[!imageUrl_]]" restamp>
+ <page-favicon id="image-icon" url="[[visit.normalizedUrl]]"
+ is-known-to-sync="[[visit.isKnownToSync]]" size="24">
+ </page-favicon>
+ </template>
</div>
<div id="title" class="truncate">[[visit.pageTitle]]</div>
+ <template is="dom-if" if="[[hasDiscount]]" restamp>
+ <div id="discountChip">[[discount]]</div>
+ </template>
<template is="dom-if" if="[[annotation_]]">
<div id="annotation">[[annotation_]]</div>
</template>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.ts
index dede120767a..ab0402c9d38 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/visit_tile.ts
@@ -48,22 +48,41 @@ export class VisitTileModuleElement extends I18nMixin
computed: `computeLabel_(visit.urlForDisplay)`,
},
+ imagesEnabled: {
+ type: Boolean,
+ reflectToAttribute: true,
+ },
+
/* The image url for the tile. */
imageUrl_: {
type: Object,
value: null,
+ reflectToAttribute: true,
},
format: {
type: String,
- value: 'wide',
+ reflectToAttribute: true,
+ },
+
+ // The texts for the discount chip.
+ discount: {
+ type: String,
+ },
+
+ hasDiscount: {
+ type: Boolean,
+ computed: `computeHasDiscount_(discount)`,
reflectToAttribute: true,
},
};
}
format: string;
+ imagesEnabled: boolean;
visit: URLVisit;
+ discount: string;
+ hasDiscount: boolean;
private imageUrl_: Url|null;
hasImageUrl(): boolean {
@@ -83,6 +102,10 @@ export class VisitTileModuleElement extends I18nMixin
return domain;
}
+ private computeHasDiscount_(): boolean {
+ return !!this.discount && this.discount.length !== 0;
+ }
+
// Set imageUrl when visit is set/updated.
private async onVisitUpdated_(): Promise<void> {
const visitUrl = this.visit.normalizedUrl;
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/icons.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/icons.html
new file mode 100644
index 00000000000..18643a79ecf
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/icons.html
@@ -0,0 +1,36 @@
+<iron-iconset-svg name="modules" size="24">
+ <svg>
+ <defs>
+ <g id="block" viewBox="0 -960 960 960">
+ <path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-60q142.375 0 241.188-98.812Q820-337.625 820-480q0-60.662-21-116.831Q778-653 740-699L261-220q45 39 101.493 59.5Q418.987-140 480-140ZM221-261l478-478q-46-39-102.169-60T480-820q-142.375 0-241.188 98.812Q140-622.375 140-480q0 61.013 22 117.507Q184-306 221-261Z">
+ </path>
+ </g>
+ <g id="dock_to_left" viewBox="0 0 20 20">
+ <path d="M4.45833 17.1667C4 17.1667 3.61111 17.0139 3.29167 16.7083C2.98611 16.3889 2.83333 16 2.83333 15.5417V4.45833C2.83333 4 2.98611 3.61806 3.29167 3.3125C3.61111 2.99306 4 2.83333 4.45833 2.83333H15.5417C16 2.83333 16.3819 2.99306 16.6875 3.3125C17.0069 3.61806 17.1667 4 17.1667 4.45833V15.5417C17.1667 16 17.0069 16.3889 16.6875 16.7083C16.3819 17.0139 16 17.1667 15.5417 17.1667H4.45833ZM11.3125 15.4375V4.5625H4.5625V15.4375H11.3125Z">
+ </path>
+ </g>
+ <g id="done" viewBox="0 -960 960 960">
+ <path d="M378-246 154-470l43-43 181 181 384-384 43 43-427 427Z">
+ </path>
+ </g>
+ <g id="thumb_down" viewBox="0 -960 960 960">
+ <path d="M242-840h444v512L408-40l-39-31q-6-5-9-14t-3-22v-10l45-211H103q-24 0-42-18t-18-42v-81.839Q43-477 41.5-484.5T43-499l126-290q8.878-21.25 29.595-36.125Q219.311-840 242-840Zm384 60H229L103-481v93h373l-53 249 203-214v-427Zm0 427v-427 427Zm60 25v-60h133v-392H686v-60h193v512H686Z">
+ </path>
+ </g>
+ <g id="info" viewBox="0 0 24 24">
+ <path d="M0 0h24v24H0V0z" fill="none">
+ </path>
+ <path d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z">
+ </path>
+ </g>
+ <g id="tune" viewBox="0 -960 960 960">
+ <path d="M427-120v-225h60v83h353v60H487v82h-60Zm-307-82v-60h247v60H120Zm187-166v-82H120v-60h187v-84h60v226h-60Zm120-82v-60h413v60H427Zm166-165v-225h60v82h187v60H653v83h-60Zm-473-83v-60h413v60H120Z">
+ </path>
+ </g>
+ <g id="visibility_off" viewBox="0 -960 960 960">
+ <path d="m629-419-44-44q26-71-27-118t-115-24l-44-44q17-11 38-16t43-5q71 0 120.5 49.5T650-500q0 22-5.5 43.5T629-419Zm129 129-40-40q49-36 85.5-80.5T857-500q-50-111-150-175.5T490-740q-42 0-86 8t-69 19l-46-47q35-16 89.5-28T485-800q143 0 261.5 81.5T920-500q-26 64-67 117t-95 93Zm58 226L648-229q-35 14-79 21.5t-89 7.5q-146 0-265-81.5T40-500q20-52 55.5-101.5T182-696L56-822l42-43 757 757-39 44ZM223-654q-37 27-71.5 71T102-500q51 111 153.5 175.5T488-260q33 0 65-4t48-12l-64-64q-11 5-27 7.5t-30 2.5q-70 0-120-49t-50-121q0-15 2.5-30t7.5-27l-97-97Zm305 142Zm-116 58Z">
+ </path>
+ </g>
+ </defs>
+ </svg>
+</iron-iconset-svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/module_header.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/module_header.html
new file mode 100644
index 00000000000..3e84c3526ec
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/module_header.html
@@ -0,0 +1,79 @@
+<style include="cr-icons">
+ :host {
+ --menu-item-margin: 12px;
+ background: var(--color-new-tab-page-module-item-background);
+ border-radius: var(--ntp-module-item-border-radius);
+ display: flex;
+ flex-direction: column;
+ }
+
+ #titleContainer {
+ align-items: center;
+ display: flex;
+ height: 20px;
+ padding: 16px;
+ }
+
+ #title {
+ color: var(--color-new-tab-page-primary-foreground);
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 20px;
+ }
+
+ #headerSpacer {
+ flex-grow: 1;
+ }
+
+ #menuButton {
+ --cr-icon-button-icon-size: 20px;
+ --cr-icon-button-fill-color: var(--color-new-tab-page-primary-foreground);
+ --cr-icon-button-hover-background-color:
+ var(--color-new-tab-page-control-background-hovered);
+ margin-inline: 0 -6px;
+ }
+
+ #actionMenu {
+ --cr-menu-shadow: var(--ntp-menu-shadow);
+ }
+
+ .action-menu-icon {
+ --cr-icon-ripple-size: 20px;
+ -webkit-mask-size: 16px;
+ background-color: transparent;
+ margin-inline-start: calc(-1 * var(--menu-item-margin));
+ margin-inline-end: var(--menu-item-margin);
+ }
+
+ #actionMenuDivider {
+ background-color: var(--color-new-tab-page-module-context-menu-divider);
+ border: none;
+ height: 1px;
+ margin: 0;
+ }
+</style>
+<div id="titleContainer">
+ <h2 id="title">[[headerText]]</h2>
+ <div id="headerSpacer"></div>
+ <slot name="title-actions"></slot>
+ <cr-icon-button id="menuButton" title="[[moreActionsText]]"
+ class="icon-more-vert" on-click="onMenuButtonClick_">
+ </cr-icon-button>
+</div>
+
+<cr-action-menu id="actionMenu">
+ <template is="dom-repeat"
+ items="[[menuItemGroups]]" as="group">
+ <template is="dom-repeat" items="[[group]]">
+ <button id="[[item.action]]" class="dropdown-item"
+ on-click="onButtonClick_" data-action$="[[item.action]]">
+ <iron-icon
+ class="action-menu-icon cr-icon"
+ icon="[[item.icon]]">
+ </iron-icon>
+ [[item.text]]
+ </button>
+ </template>
+ <hr id="actionMenuDivider" hidden$="[[!showDivider_(index)]]">
+ </template>
+</cr-action-menu>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/module_header.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/module_header.ts
new file mode 100644
index 00000000000..1a262eb3df3
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/module_header.ts
@@ -0,0 +1,87 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import './icons.html.js';
+import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+
+import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {I18nMixin} from '../../i18n_setup.js';
+
+import {getTemplate} from './module_header.html.js';
+
+export interface MenuItem {
+ action: string;
+ icon: string;
+ text: string;
+}
+
+export interface ModuleHeaderElementV2 {
+ $: {
+ actionMenu: CrActionMenuElement,
+ };
+}
+
+/** Element that displays a header inside a module. */
+export class ModuleHeaderElementV2 extends I18nMixin
+(PolymerElement) {
+ static get is() {
+ return 'ntp-module-header-v2';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ headerText: String,
+ moreActionsText: String,
+ menuItemGroups: Array,
+ };
+ }
+
+ headerText: string;
+ menuItemGroups: MenuItem[][];
+ moreActionsText: string;
+
+ showAt(e: Event) {
+ this.$.actionMenu.showAt(e.target as HTMLElement);
+ }
+
+ private onButtonClick_(e: DomRepeatEvent<MenuItem>) {
+ const {action} = e.model.item;
+ assert(action);
+ e.stopPropagation();
+ this.$.actionMenu.close();
+ if (action === 'customize-module') {
+ this.dispatchEvent(
+ new Event('customize-module', {bubbles: true, composed: true}));
+ } else {
+ this.dispatchEvent(new Event(
+ `${action}-button-click`, {bubbles: true, composed: true}));
+ }
+ }
+
+ private onMenuButtonClick_(e: Event) {
+ e.stopPropagation();
+ this.dispatchEvent(new Event('menu-button-click', {bubbles: true}));
+ }
+
+ private showDivider_(index: number): boolean {
+ return index === 0;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'ntp-module-header-v2': ModuleHeaderElementV2;
+ }
+}
+
+customElements.define(ModuleHeaderElementV2.is, ModuleHeaderElementV2);
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.html b/chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.html
index 78bab5a68a1..4c6886b2bdf 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.html
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.html
@@ -1,4 +1,33 @@
<style include="cr-hidden-style">
+ #container {
+ display: flex;
+ flex-flow: row wrap;
+ gap: var(--container-gap);
+ justify-content: center;
+ margin-top: 8px;
+ max-width: var(--container-max-width, inherit);
+ }
+
+ #undoToastMessage {
+ flex-grow: 1;
+ }
</style>
<div id="container">
</div>
+<template>
+ <ntp-module-wrapper module="[[item]]"
+ hidden="[[moduleDisabled_(disabledModules_, item)]]"
+ on-disable-module="onDisableModule_"
+ on-dismiss-module-instance="onDismissModuleInstance_">
+ </ntp-module-wrapper>
+</template>
+<cr-toast id="undoToast" duration="10000">
+ <div id="undoToastMessage">[[undoData_.message]]</div>
+ <template is="dom-if" if="[[undoData_.undo]]">
+ <cr-button id="undoButton"
+ aria-label="$i18n{undoDescription}"
+ on-click="onUndoButtonClick_">
+ $i18n{undo}
+ </cr-button>
+ </template>
+</cr-toast>
diff --git a/chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.ts b/chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.ts
index 2ab4cc50936..f70d7a55540 100644
--- a/chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/modules/v2/modules.ts
@@ -3,11 +3,65 @@
// found in the LICENSE file.
import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
+import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {EventTracker} from 'chrome://resources/js/event_tracker.js';
+import {PolymerElement, TemplateInstanceBase, templatize} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {loadTimeData} from '../../i18n_setup.js';
+import {NewTabPageProxy} from '../../new_tab_page_proxy.js';
+import {WindowProxy} from '../../window_proxy.js';
+import {Module} from '../module_descriptor.js';
+import {ModuleRegistry} from '../module_registry.js';
+import {ModuleInstance, ModuleWrapperElement} from '../module_wrapper.js';
import {getTemplate} from './modules.html.js';
+export interface NamedWidth {
+ name: string;
+ value: number;
+}
+
+export const SUPPORTED_MODULE_WIDTHS: NamedWidth[] = [
+ {name: 'narrow', value: 312},
+ {name: 'medium', value: 360},
+ {name: 'wide', value: 728},
+];
+
+interface QueryDetails {
+ maxWidth: number;
+ query: string;
+}
+
+const CONTAINER_GAP_WIDTH = 8;
+
+const MARGIN_WIDTH = 48;
+
+const METRIC_NAME_MODULE_DISABLED = 'NewTabPage.Modules.Disabled';
+
+export type UndoActionEvent =
+ CustomEvent<{message: string, restoreCallback?: () => void}>;
+export type DismissModuleInstanceEvent = UndoActionEvent;
+export type DisableModuleEvent = UndoActionEvent;
+
+declare global {
+ interface HTMLElementEventMap {
+ 'disable-module': DisableModuleEvent;
+ 'dismiss-module-instance': DismissModuleInstanceEvent;
+ }
+}
+
+export interface ModulesV2Element {
+ $: {
+ container: HTMLElement,
+ undoToast: CrToastElement,
+ undoToastMessage: HTMLElement,
+ };
+}
+
/** Container for the NTP modules. */
export class ModulesV2Element extends PolymerElement {
static get is() {
@@ -19,7 +73,352 @@ export class ModulesV2Element extends PolymerElement {
}
static get properties() {
- return {};
+ return {
+ disabledModules_: {
+ type: Object,
+ observer: 'onDisabledModulesChange_',
+ value: () => ({all: true, ids: []}),
+ },
+
+ modulesShownToUser: {
+ type: Boolean,
+ notify: true,
+ },
+
+ /** Data about the most recent un-doable action. */
+ undoData_: {
+ type: Object,
+ value: null,
+ },
+ };
+ }
+
+ modulesShownToUser: boolean;
+ private maxColumnCount_: number;
+ private containerMaxWidth_: number;
+ private disabledModules_: {all: boolean, ids: string[]};
+ private eventTracker_: EventTracker = new EventTracker();
+ private undoData_: {message: string, undo?: () => void}|null;
+ private setDisabledModulesListenerId_: number|null = null;
+ private containerObserver_: MutationObserver|null = null;
+ private templateInstances_: TemplateInstanceBase[] = [];
+
+ override connectedCallback() {
+ super.connectedCallback();
+
+ this.setDisabledModulesListenerId_ =
+ NewTabPageProxy.getInstance()
+ .callbackRouter.setDisabledModules.addListener(
+ (all: boolean, ids: string[]) => {
+ this.disabledModules_ = {all, ids};
+ });
+ NewTabPageProxy.getInstance().handler.updateDisabledModules();
+
+ const widths: Set<number> = new Set();
+ for (let i = 0; i < SUPPORTED_MODULE_WIDTHS.length; i++) {
+ const namedWidth = SUPPORTED_MODULE_WIDTHS[i];
+ for (let u = 1; u <= this.maxColumnCount_ - i; u++) {
+ const width = (namedWidth.value * u) + (CONTAINER_GAP_WIDTH * (u - 1));
+ if (width <= this.containerMaxWidth_) {
+ widths.add(width);
+ }
+ }
+ }
+ // Widths must be deduped and sorted to ensure the min-width and max-with
+ // media features in the queries produced below are correctly generated.
+ const thresholds = [...widths];
+ thresholds.sort((i, j) => i - j);
+
+ const queries: QueryDetails[] = [];
+ for (let i = 1; i < thresholds.length - 1; i++) {
+ queries.push({
+ maxWidth: (thresholds[i + 1] - 1),
+ query: `(min-width: ${
+ thresholds[i] + 2 * MARGIN_WIDTH}px) and (max-width: ${
+ thresholds[i + 1] - 1 + (2 * MARGIN_WIDTH)}px)`,
+ });
+ }
+ queries.splice(0, 0, {
+ maxWidth: thresholds[0],
+ query: `(max-width: ${thresholds[0] - 1 + (2 * MARGIN_WIDTH)}px)`,
+ });
+ queries.push({
+ maxWidth: thresholds[thresholds.length - 1],
+ query: `(min-width: ${
+ thresholds[thresholds.length - 1] + (2 * MARGIN_WIDTH)}px)`,
+ });
+
+ // Produce media queries with relevant view thresholds at which module
+ // instance optimal widths should be re-evaluated.
+ queries.forEach(details => {
+ const query = WindowProxy.getInstance().matchMedia(details.query);
+ this.eventTracker_.add(query, 'change', (e: MediaQueryListEvent) => {
+ if (e.matches) {
+ this.updateContainerAndChildrenStyles_(details.maxWidth);
+ }
+ });
+ });
+
+ this.eventTracker_.add(window, 'keydown', this.onWindowKeydown_.bind(this));
+
+ this.containerObserver_ = new MutationObserver(() => {
+ this.updateContainerAndChildrenStyles_();
+ });
+ this.containerObserver_!.observe(this.$.container, {childList: true});
+ }
+
+ override disconnectedCallback() {
+ super.disconnectedCallback();
+
+ assert(this.setDisabledModulesListenerId_);
+ NewTabPageProxy.getInstance().callbackRouter.removeListener(
+ this.setDisabledModulesListenerId_);
+
+ this.eventTracker_.removeAll();
+
+ this.containerObserver_!.disconnect();
+ }
+
+ override ready() {
+ super.ready();
+
+ this.updateStyles({
+ '--container-gap': `${CONTAINER_GAP_WIDTH}px`,
+ });
+
+ this.maxColumnCount_ = loadTimeData.getInteger('modulesMaxColumnCount');
+ this.containerMaxWidth_ =
+ this.maxColumnCount_ * SUPPORTED_MODULE_WIDTHS[0].value +
+ (this.maxColumnCount_ - 1) * CONTAINER_GAP_WIDTH;
+ this.loadModules_();
+ }
+
+ private moduleDisabled_(
+ disabledModules: {all: true, ids: string[]},
+ instance: ModuleInstance): boolean {
+ return disabledModules.all ||
+ disabledModules.ids.includes(instance.descriptor.id);
+ }
+
+ private async loadModules_(): Promise<void> {
+ const modulesIdNames =
+ (await NewTabPageProxy.getInstance().handler.getModulesIdNames()).data;
+ const modules =
+ await ModuleRegistry.getInstance().initializeModulesHavingIds(
+ modulesIdNames.map(m => m.id),
+ loadTimeData.getInteger('modulesLoadTimeout'));
+ if (modules) {
+ NewTabPageProxy.getInstance().handler.onModulesLoadedWithData(
+ modules.map(module => module.descriptor.id));
+
+ const template = this.shadowRoot!.querySelector('template')!;
+ const moduleWrapperConstructor:
+ {new (_: Object): TemplateInstanceBase&HTMLElement} =
+ templatize(template, this, {
+ parentModel: true,
+ forwardHostProp: this.forwardHostProp_,
+ instanceProps: {item: true},
+ }) as {new (): TemplateInstanceBase & HTMLElement};
+
+
+ if (modules.length > 1) {
+ const maxModuleInstanceCount = loadTimeData.getInteger(
+ 'multipleLoadedModulesMaxModuleInstanceCount');
+ if (maxModuleInstanceCount > 0) {
+ modules.forEach(module => {
+ module.elements.splice(
+ maxModuleInstanceCount,
+ module.elements.length - maxModuleInstanceCount);
+ });
+ }
+ }
+
+ this.templateInstances_ =
+ modules
+ .map(module => {
+ return module.elements.map(element => {
+ return {
+ element,
+ descriptor: module.descriptor,
+ };
+ });
+ })
+ .flat()
+ .map(instance => {
+ return new moduleWrapperConstructor({item: instance});
+ });
+ this.templateInstances_.map(t => t.children[0] as HTMLElement)
+ .forEach(wrapperElement => {
+ this.$.container.appendChild(wrapperElement);
+ });
+
+ chrome.metricsPrivate.recordSmallCount(
+ 'NewTabPage.Modules.LoadedModulesCount', modules.length);
+ modulesIdNames.forEach(({id}) => {
+ chrome.metricsPrivate.recordBoolean(
+ `NewTabPage.Modules.EnabledOnNTPLoad.${id}`,
+ !this.disabledModules_.all &&
+ !this.disabledModules_.ids.includes(id));
+ });
+ chrome.metricsPrivate.recordSmallCount(
+ 'NewTabPage.Modules.InstanceCount', this.templateInstances_.length);
+ chrome.metricsPrivate.recordBoolean(
+ 'NewTabPage.Modules.VisibleOnNTPLoad', !this.disabledModules_.all);
+ this.recordModuleLoadedWithModules_(modules);
+ this.dispatchEvent(new Event('modules-loaded'));
+ }
+ }
+
+ private recordModuleLoadedWithModules_(modules: Module[]) {
+ const moduleDescriptorIds = modules.map(m => m.descriptor.id);
+
+ for (const moduleDescriptorId of moduleDescriptorIds) {
+ moduleDescriptorIds.forEach(id => {
+ if (id !== moduleDescriptorId) {
+ chrome.metricsPrivate.recordSparseValueWithPersistentHash(
+ `NewTabPage.Modules.LoadedWith.${moduleDescriptorId}`, id);
+ }
+ });
+ }
+ }
+
+ private forwardHostProp_(property: string, value: any) {
+ this.templateInstances_.forEach(instance => {
+ instance.forwardHostProp(property, value);
+ });
+ }
+
+ private updateContainerAndChildrenStyles_(availableWidth?: number) {
+ if (typeof availableWidth === 'undefined') {
+ availableWidth = Math.min(
+ document.body.clientWidth - 2 * MARGIN_WIDTH,
+ this.containerMaxWidth_);
+ }
+
+ const moduleWrappers =
+ Array.from(this.shadowRoot!.querySelectorAll(
+ 'ntp-module-wrapper:not([hidden])')) as ModuleWrapperElement[];
+ this.modulesShownToUser = moduleWrappers.length !== 0;
+ if (moduleWrappers.length === 0) {
+ return;
+ }
+
+ this.updateStyles({'--container-max-width': `${availableWidth}px`});
+
+ const clamp = (min: number, val: number, max: number) =>
+ Math.max(min, Math.min(val, max));
+ const rowMaxInstanceCount = clamp(
+ 1,
+ Math.floor(
+ (availableWidth + CONTAINER_GAP_WIDTH) /
+ (CONTAINER_GAP_WIDTH + SUPPORTED_MODULE_WIDTHS[0].value)),
+ this.maxColumnCount_);
+
+ let index = 0;
+ while (index < moduleWrappers.length) {
+ const instances = moduleWrappers.slice(index, index + rowMaxInstanceCount)
+ .map(w => w.module);
+ let namedWidth = SUPPORTED_MODULE_WIDTHS[0];
+ for (let i = 1; i < SUPPORTED_MODULE_WIDTHS.length; i++) {
+ if (Math.floor(
+ (availableWidth -
+ (CONTAINER_GAP_WIDTH * (instances.length - 1))) /
+ SUPPORTED_MODULE_WIDTHS[i].value) < instances.length) {
+ break;
+ }
+ namedWidth = SUPPORTED_MODULE_WIDTHS[i];
+ }
+
+ instances.slice(0, instances.length).forEach(instance => {
+ // The `format` attribute is leveraged by modules whose layout should
+ // change based on the available width.
+ instance.element.setAttribute('format', namedWidth.name);
+ instance.element.style.width = `${namedWidth.value}px`;
+ });
+
+ index += instances.length;
+ }
+ }
+
+ private onDisableModule_(e: DisableModuleEvent) {
+ const id = (e.target! as ModuleWrapperElement).module.descriptor.id;
+ const restoreCallback = e.detail.restoreCallback;
+ this.undoData_ = {
+ message: e.detail.message,
+ undo: () => {
+ if (restoreCallback) {
+ restoreCallback();
+ }
+ NewTabPageProxy.getInstance().handler.setModuleDisabled(id, false);
+ chrome.metricsPrivate.recordSparseValueWithPersistentHash(
+ 'NewTabPage.Modules.Enabled', id);
+ chrome.metricsPrivate.recordSparseValueWithPersistentHash(
+ 'NewTabPage.Modules.Enabled.Toast', id);
+ },
+ };
+
+ NewTabPageProxy.getInstance().handler.setModuleDisabled(id, true);
+ this.$.undoToast.show();
+ chrome.metricsPrivate.recordSparseValueWithPersistentHash(
+ METRIC_NAME_MODULE_DISABLED, id);
+ chrome.metricsPrivate.recordSparseValueWithPersistentHash(
+ `${METRIC_NAME_MODULE_DISABLED}.ModuleRequest`, id);
+ }
+
+ private onDisabledModulesChange_() {
+ this.updateContainerAndChildrenStyles_();
+ }
+
+ /**
+ * @param e Event notifying a module instance was dismissed. Contains the
+ * message to show in the toast.
+ */
+ private onDismissModuleInstance_(e: DismissModuleInstanceEvent) {
+ const wrapper = (e.target! as ModuleWrapperElement);
+ const index = Array.from(wrapper.parentNode!.children).indexOf(wrapper);
+ wrapper.remove();
+
+ const restoreCallback = e.detail.restoreCallback;
+ this.undoData_ = {
+ message: e.detail.message,
+ undo: restoreCallback ?
+ () => {
+ this.$.container.insertBefore(
+ wrapper, this.$.container.childNodes[index]);
+ restoreCallback();
+ chrome.metricsPrivate.recordSparseValueWithPersistentHash(
+ 'NewTabPage.Modules.Restored', wrapper.module.descriptor.id);
+ } :
+ undefined,
+ };
+
+ // Notify the user.
+ this.$.undoToast.show();
+
+ chrome.metricsPrivate.recordSparseValueWithPersistentHash(
+ 'NewTabPage.Modules.Dismissed', wrapper.module.descriptor.id);
+ }
+
+ private onUndoButtonClick_() {
+ if (!this.undoData_) {
+ return;
+ }
+
+ // Restore to the previous state.
+ this.undoData_.undo!();
+ // Notify the user.
+ this.$.undoToast.hide();
+ this.undoData_ = null;
+ }
+
+ private onWindowKeydown_(e: KeyboardEvent) {
+ let ctrlKeyPressed = e.ctrlKey;
+ // <if expr="is_macosx">
+ ctrlKeyPressed = ctrlKeyPressed || e.metaKey;
+ // </if>
+ if (ctrlKeyPressed && e.key === 'z') {
+ this.onUndoButtonClick_();
+ }
}
}
diff --git a/chromium/chrome/browser/resources/new_tab_page/new_tab_page.gni b/chromium/chrome/browser/resources/new_tab_page/new_tab_page.gni
index 0bb1b2a8058..11aeb9610c6 100644
--- a/chromium/chrome/browser/resources/new_tab_page/new_tab_page.gni
+++ b/chromium/chrome/browser/resources/new_tab_page/new_tab_page.gni
@@ -10,10 +10,12 @@ all_non_web_component_files = [
"background_manager.ts",
"customize_dialog_types.ts",
"i18n_setup.ts",
+ "image_processor.ts",
"lazy_load.ts",
"metrics_utils.ts",
"new_tab_page_proxy.ts",
"new_tab_page.ts",
+ "transparency.ts",
"utils.ts",
"window_proxy.ts",
] + modules_non_web_component_files
@@ -40,11 +42,14 @@ if (optimize_webui) {
# Transpiled Mojo JS files to be excluded from the bundle in optimized builds.
mojo_js_files = [
"cart.mojom-webui.js",
+ "discount.mojom-webui.js",
"chrome_cart.mojom-webui.js",
"drive.mojom-webui.js",
"feed.mojom-webui.js",
"history_cluster_types.mojom-webui.js",
"history_clusters.mojom-webui.js",
+ "history_clusters_v2.mojom-webui.js",
+ "history_clusters_layout_type.mojom-webui.js",
"new_tab_page.mojom-webui.js",
"photos.mojom-webui.js",
"recipes.mojom-webui.js",
diff --git a/chromium/chrome/browser/resources/new_tab_page/new_tab_page.html b/chromium/chrome/browser/resources/new_tab_page/new_tab_page.html
index 6e261416165..09cdd4b90d4 100644
--- a/chromium/chrome/browser/resources/new_tab_page/new_tab_page.html
+++ b/chromium/chrome/browser/resources/new_tab_page/new_tab_page.html
@@ -1,5 +1,6 @@
<!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<html dir="$i18n{textdirection}" lang="$i18n{language}"
+ $i18n{chromeRefresh2023Attribute}>
<head>
<meta charset="utf-8">
<title>$i18n{title}</title>
diff --git a/chromium/chrome/browser/resources/new_tab_page/new_tab_page.ts b/chromium/chrome/browser/resources/new_tab_page/new_tab_page.ts
index f63401418c0..196f0333e8f 100644
--- a/chromium/chrome/browser/resources/new_tab_page/new_tab_page.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/new_tab_page.ts
@@ -23,10 +23,12 @@ export {BackgroundManager} from './background_manager.js';
export {CustomizeDialogPage} from './customize_dialog_types.js';
export {DoodleShareDialogElement} from './doodle_share_dialog.js';
export {IframeElement} from './iframe.js';
+export {ProcessedFile, processFile} from './image_processor.js';
export {LogoElement} from './logo.js';
export {recordDuration, recordLoadDuration, recordOccurence, recordPerdecage} from './metrics_utils.js';
export {NewTabPageProxy} from './new_tab_page_proxy.js';
export {RealboxElement} from './realbox/realbox.js';
+export {checkTransparency, isBMP, isPNG, isWebP} from './transparency.js';
export {$$, createScrollBorders, decodeString16, mojoString16} from './utils.js';
export {Action as VoiceAction, Error as VoiceError} from './voice_search_overlay.js';
export {WindowProxy} from './window_proxy.js';
diff --git a/chromium/chrome/browser/resources/new_tab_page/realbox/realbox.html b/chromium/chrome/browser/resources/new_tab_page/realbox/realbox.html
index a939f58395f..fc3eab4d1bb 100644
--- a/chromium/chrome/browser/resources/new_tab_page/realbox/realbox.html
+++ b/chromium/chrome/browser/resources/new_tab_page/realbox/realbox.html
@@ -238,7 +238,7 @@
can-show-secondary-side="[[canShowSecondarySide]]"
had-secondary-side="{{hadSecondarySide}}"
has-secondary-side="{{hasSecondarySide}}"
- on-match-focusin="onMatchFocusin_" on-match-click="onMatchClick_"
+ on-match-focusin="onMatchFocusin_"
on-match-remove="onMatchRemove_" on-header-focusin="onHeaderFocusin_"
hidden$="[[!dropdownIsVisible]]">
</cr-realbox-dropdown>
diff --git a/chromium/chrome/browser/resources/new_tab_page/realbox/realbox.ts b/chromium/chrome/browser/resources/new_tab_page/realbox/realbox.ts
index e42d438db67..2ef4137bed0 100644
--- a/chromium/chrome/browser/resources/new_tab_page/realbox/realbox.ts
+++ b/chromium/chrome/browser/resources/new_tab_page/realbox/realbox.ts
@@ -621,13 +621,6 @@ export class RealboxElement extends PolymerElement {
}
/**
- * @param e Event containing index of the match that was clicked.
- */
- private onMatchClick_(e: CustomEvent<{index: number, event: MouseEvent}>) {
- this.navigateToMatch_(e.detail.index, e.detail.event);
- }
-
- /**
* @param e Event containing index of the match that received focus.
*/
private onMatchFocusin_(e: CustomEvent<number>) {
diff --git a/chromium/chrome/browser/resources/new_tab_page/transparency.ts b/chromium/chrome/browser/resources/new_tab_page/transparency.ts
new file mode 100644
index 00000000000..b1bde94fe00
--- /dev/null
+++ b/chromium/chrome/browser/resources/new_tab_page/transparency.ts
@@ -0,0 +1,122 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * Utility which gives a best effort guess on whether a supplied image file's
+ * bytes represent an image with transparency.
+ */
+export function checkTransparency(buffer: ArrayBuffer): boolean {
+ const view = new DataView(buffer);
+
+ return isTransparentPNG(view) || isTransparentBMP(view) ||
+ isTransparentWebP(view);
+}
+
+/**
+ * Safely gets Uint8 value from DataView.
+ *
+ * Swallows potential RangeErrors, opting to return null in those cases.
+ */
+function getUint8FromView(view: DataView, offset: number): number|null {
+ try {
+ return view.getUint8(offset);
+ } catch {
+ return null;
+ }
+}
+
+/**
+ * Safely gets Uint16 value from DataView.
+ *
+ * Swallows potential RangeErrors, opting to return null in those cases.
+ */
+function getUint16FromView(view: DataView, offset: number): number|null {
+ try {
+ return view.getUint16(offset);
+ } catch {
+ return null;
+ }
+}
+
+/**
+ * Safely gets Uint32 value from DataView.
+ *
+ * Swallows potential RangeErrors, opting to return null in those cases.
+ */
+function getUint32FromView(view: DataView, offset: number): number|null {
+ try {
+ return view.getUint32(offset);
+ } catch {
+ return null;
+ }
+}
+
+/**
+ * Whether a DataView represents a PNG image.
+ */
+export function isPNG(view: DataView): boolean {
+ // 89 50 4E 47 is PNG magic number.
+ // Next four bytes should always be 0D 0A 1A 0A.
+ return getUint32FromView(view, 0) === 0x89504E47 &&
+ getUint32FromView(view, 4) === 0x0D0A1A0A;
+}
+
+function isTransparentPNG(view: DataView): boolean {
+ if (!isPNG(view)) {
+ return false;
+ }
+ // We know format field exists in the IHDR chunk. The chunk exists at
+ // offset 8 +8 bytes (size, name) +8 (depth) & +9 (type) = 25 byte offset.
+ const type = getUint8FromView(view, 25);
+ return type === 0x04 || type === 0x06; // grayscale + alpha or RGB + alpha
+}
+
+/**
+ * Whether a DataView represents a WebP image.
+ */
+export function isWebP(view: DataView): boolean {
+ // 52 49 46 46 || <ignore 4 bytes> || 57 45 42 50 is the WebP magic number.
+ // R I F F || <ignore 4 bytes> || W E B P in ASCII.
+ // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
+ return getUint32FromView(view, 0) === 0x52494646 &&
+ getUint32FromView(view, 8) === 0x57454250;
+}
+
+/**
+ * Whether a DataView represents a WebP image.
+ * Checks WebP format (VP8 | VP8L | VP8X). VP8 never has an alpha channel. We
+ * make an assumption that VP8X and VP8L always have alpha channels. That is
+ * not true, but does simplify the logic and for practical purposes, this is
+ * good enough of an assumption for us.
+ */
+function isTransparentWebP(view: DataView): boolean {
+ if (!isWebP(view)) {
+ return false;
+ }
+ // Fourteenth byte indicates WebP format. There are three variations of WebP
+ // encoding: VP8, VP8L, and VP8X.
+ //
+ // Offset: 12 13 14 15
+ // ASCII: V P 8 ? <- ? == " " || "L" || "X"
+ // Hex: 56 50 38 ? <- ? == 0x20 || 0x4C || 0x58
+ const format = getUint8FromView(view, 15);
+ // 58 indicates VP8X, 4C indicates VP8L.
+ return format === 0x58 || format === 0x4C;
+}
+
+/**
+ * Whether a DataView represents a BMP image.
+ */
+export function isBMP(view: DataView): boolean {
+ // 42 4D is the BMP magic number.
+ return getUint16FromView(view, 0) === 0x424D;
+}
+
+function isTransparentBMP(view: DataView): boolean {
+ if (!isBMP(view)) {
+ return false;
+ }
+ // Check the value of the bit count field, 2 bytes, 28 byte offset.
+ return getUint16FromView(view, 28) === 0x32;
+}
diff --git a/chromium/chrome/browser/resources/ntp4/BUILD.gn b/chromium/chrome/browser/resources/ntp4/BUILD.gn
deleted file mode 100644
index 218fa70417a..00000000000
--- a/chromium/chrome/browser/resources/ntp4/BUILD.gn
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2018 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//ui/webui/resources/tools/build_webui.gni")
-
-assert(is_fuchsia || is_linux || is_mac || is_win)
-
-# Note: There are no plans to migrate this page to TypeScript, as it is a fairly
-# old page with an undecided future, and would be a lot of work. Passing JS file
-# through TS compiler to get some basic static checks (mostly validating syntax
-# but not types) and using build_webui() to leverage other benefits, like
-# automatic minification.
-
-build_webui("build") {
- grd_prefix = "apps"
-
- static_files = [
- "apps_page.css",
- "images/error_yellow900.svg",
- "images/trash.png",
- "nav_dot.css",
- "new_tab.css",
- "new_tab.html",
- "tile_page.css",
- "trash.css",
- ]
-
- non_web_component_files = [
- "app_info.js",
- "apps_page.js",
- "card_slider.js",
- "command.js",
- "context_menu_handler.js",
- "cr_deprecated.js",
- "dot_list.js",
- "menu.js",
- "menu_button.js",
- "menu_item.js",
- "nav_dot.js",
- "new_tab.js",
- "page_list_view.js",
- "page_switcher.js",
- "position_util.js",
- "tile_page.js",
- "touch_handler.js",
- "trash.js",
- "ui.js",
- "util.js",
- ]
-
- ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
- ts_deps = [
- "//ui/webui/resources/cr_elements:build_ts",
- "//ui/webui/resources/js:build_ts",
- ]
-}
diff --git a/chromium/chrome/browser/resources/ntp4/app_info.js b/chromium/chrome/browser/resources/ntp4/app_info.js
deleted file mode 100644
index f9fbb8fdf64..00000000000
--- a/chromium/chrome/browser/resources/ntp4/app_info.js
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @typedef {{app_launch_ordinal: string,
- * description: string,
- * detailsUrl: string,
- * direction: string,
- * enabled: boolean,
- * full_name: string,
- * full_name_direction: string,
- * homepageUrl: string,
- * icon_big: string,
- * icon_big_exists: boolean,
- * icon_small: string,
- * icon_small_exists: boolean,
- * id: string,
- * is_component: boolean,
- * is_deprecated_app: boolean,
- * is_webstore: boolean,
- * isLocallyInstalled: boolean,
- * hideDisplayMode: boolean,
- * kioskEnabled: boolean,
- * kioskMode: boolean,
- * kioskOnly: boolean,
- * launch_container: number,
- * launch_type: number,
- * mayChangeLaunchType: boolean,
- * mayShowRunOnOsLoginMode: boolean,
- * mayToggleRunOnOsLoginMode: boolean,
- * mayCreateShortcuts: boolean,
- * mayDisable: boolean,
- * name: string,
- * offlineEnabled: boolean,
- * optionsUrl: string,
- * packagedApp: boolean,
- * page_index: number,
- * runOnOsLoginMode: string,
- * title: string,
- * url: string,
- * version: string}}
- * @see chrome/browser/ui/webui/ntp/app_launcher_handler.cc
- */
-export let AppInfo;
diff --git a/chromium/chrome/browser/resources/ntp4/apps_page.css b/chromium/chrome/browser/resources/ntp4/apps_page.css
deleted file mode 100644
index 2f51154f727..00000000000
--- a/chromium/chrome/browser/resources/ntp4/apps_page.css
+++ /dev/null
@@ -1,114 +0,0 @@
-/* Copyright 2012 The Chromium Authors
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-.app {
- outline: none;
- position: absolute;
- text-align: center;
-}
-
-.app-contents {
- transition: transform 100ms;
-}
-
-.app-contents:active:not(.suppress-active),
-.app:not(.click-focus):focus .app-contents:not(.suppress-active),
-.drag-representation:not(.placing) .app-contents {
- transform: scale(1.1);
-}
-
-/* Don't animate the initial scaling. */
-.app-contents:active:not(.suppress-active),
-/* Active gets applied right before .suppress-active, so to avoid flicker
- * we need to make the scale go back to normal without an animation. */
-.app-contents.suppress-active {
- transition-duration: 0ms;
-}
-
-.app-title-container {
- align-items: center;
- display: flex;
- justify-content: center;
-}
-
-.app-title-container .title {
- display: block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.app.deprecated .app-title-container::after {
- content: url(images/error_yellow900.svg);
- height: 18px;
- padding-inline-start: 8px;
- width: 18px;
-}
-
-.app-img-container {
- /* -webkit-mask-image set by JavaScript to the image source. */
- -webkit-mask-size: 100% 100%;
- margin-left: auto;
- margin-right: auto;
-}
-
-.app-img-container > * {
- height: 100%;
- width: 100%;
-}
-
-.app-icon-div {
- -webkit-box-align: center;
- -webkit-box-pack: center;
- background-color: white;
- border: 1px solid #d5d5d5;
- border-radius: 5px;
- display: -webkit-box;
- margin-left: auto;
- margin-right: auto;
- position: relative;
- vertical-align: middle;
- z-index: 0;
-}
-
-.app-icon-div .app-img-container {
- bottom: 10px;
- left: 10px;
- position: absolute;
-}
-
-.app-icon-div .color-stripe {
- border-bottom-left-radius: 5px 5px;
- border-bottom-right-radius: 5px 5px;
- bottom: 0;
- height: 3px;
- opacity: 1.0;
- position: absolute;
- width: 100%;
- z-index: 100;
-}
-
-.app-context-menu > button:first-child {
- font-weight: bold;
-}
-
-.app-context-menu {
- z-index: 1000;
-}
-
-.app-context-menu > [checked]::before {
- height: 5px;
-}
-
-.launch-click-target {
- cursor: pointer;
-}
-
-.app-img-container > img:first-child {
- display: block;
-}
-
-.app .invisible {
- visibility: hidden;
-}
diff --git a/chromium/chrome/browser/resources/ntp4/apps_page.js b/chromium/chrome/browser/resources/ntp4/apps_page.js
deleted file mode 100644
index 754ea09b76d..00000000000
--- a/chromium/chrome/browser/resources/ntp4/apps_page.js
+++ /dev/null
@@ -1,906 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assert} from 'chrome://resources/js/assert_ts.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {$, appendParam} from 'chrome://resources/js/util_ts.js';
-
-import {AppInfo} from './app_info.js';
-import {contextMenuHandler} from './context_menu_handler.js';
-import {addSingletonGetter} from './cr_deprecated.js';
-import {Menu} from './menu.js';
-import {MenuItem} from './menu_item.js';
-import {getAppsPageIndex, getCardSlider} from './new_tab.js';
-import {getCurrentlyDraggingTile, setCurrentDropEffect, TilePage} from './tile_page.js';
-import {decorate, toCssPx} from './ui.js';
-import {findAncestorByClass} from './util.js';
-
-
-
-export const APP_LAUNCH = {
- // The histogram buckets (keep in sync with extension_constants.h).
- NTP_APPS_MAXIMIZED: 0,
- NTP_APPS_COLLAPSED: 1,
- NTP_APPS_MENU: 2,
- NTP_MOST_VISITED: 3,
- NTP_APP_RE_ENABLE: 16,
- NTP_WEBSTORE_FOOTER: 18,
- NTP_WEBSTORE_PLUS_ICON: 19,
-};
-
-// Histogram buckets for UMA tracking of where a DnD drop came from.
-const DRAG_SOURCE = {
- SAME_APPS_PANE: 0,
- OTHER_APPS_PANE: 1,
- MOST_VISITED_PANE: 2, // Deprecated.
- BOOKMARKS_PANE: 3, // Deprecated.
- OUTSIDE_NTP: 4,
-};
-const DRAG_SOURCE_LIMIT = DRAG_SOURCE.OUTSIDE_NTP + 1;
-
-// Run on OS Login available modes
-const RUN_ON_OS_LOGIN_MODE = {
- NOT_RUN: 'run_on_os_login_mode_not_run',
- WINDOWED: 'run_on_os_login_mode_windowed',
- MINIMIZED: 'run_on_os_login_mode_minimized',
-};
-
-// The fraction of the app tile size that the icon uses.
-const APP_IMG_SIZE_FRACTION = 4 / 5;
-
-/**
- * This policy maps a given string to a `TrustedHTML` object
- * without performing any validation. Callsites must ensure
- * that the resulting object will only be used in inert
- * documents.
- *
- * @type {!TrustedTypePolicy}
- */
-const unsanitizedPolicy = trustedTypes.createPolicy(
- 'apps-page-js', {createHTML: untrustedHTML => untrustedHTML});
-
-/**
- * App context menu. The class is designed to be used as a singleton with
- * the app that is currently showing a context menu stored in this.app_.
- * @constructor
- */
-function AppContextMenu() {
- this.__proto__ = AppContextMenu.prototype;
- this.initialize();
-}
-addSingletonGetter(AppContextMenu);
-
-AppContextMenu.prototype = {
- initialize() {
- const menu = new Menu();
- decorate(menu, Menu);
- menu.classList.add('app-context-menu');
- this.menu = menu;
-
- this.launch_ = this.appendMenuItem_();
- this.launch_.addEventListener('activate', this.onActivate_.bind(this));
-
- menu.appendChild(MenuItem.createSeparator());
- this.launchRegularTab_ = this.appendMenuItem_('applaunchtyperegular');
- this.launchPinnedTab_ = this.appendMenuItem_('applaunchtypepinned');
- this.launchNewWindow_ = this.appendMenuItem_('applaunchtypewindow');
- this.launchFullscreen_ = this.appendMenuItem_('applaunchtypefullscreen');
-
- const self = this;
- this.forAllLaunchTypes_(function(launchTypeButton, id) {
- launchTypeButton.addEventListener(
- 'activate', self.onLaunchTypeChanged_.bind(self));
- });
-
- this.runOnOsLogin_ = this.appendMenuItem_('runonoslogin');
- this.runOnOsLogin_.addEventListener(
- 'activate', this.onRunOnOsLoginModeChanged_.bind(this));
-
- this.launchTypeMenuSeparator_ = MenuItem.createSeparator();
- menu.appendChild(this.launchTypeMenuSeparator_);
- this.options_ = this.appendMenuItem_('appoptions');
- this.uninstall_ = this.appendMenuItem_('appuninstall');
-
- this.appinfo_ = this.appendMenuItem_('appinfodialog');
- this.appinfo_.addEventListener('activate', this.onShowAppInfo_.bind(this));
- if (!loadTimeData.getBoolean('canShowAppInfoDialog')) {
- this.details_ = this.appendMenuItem_('appdetails');
- this.details_.addEventListener(
- 'activate', this.onShowDetails_.bind(this));
- }
-
- this.options_.addEventListener('activate', this.onShowOptions_.bind(this));
- this.uninstall_.addEventListener('activate', this.onUninstall_.bind(this));
-
- this.createShortcutSeparator_ =
- menu.appendChild(MenuItem.createSeparator());
- this.createShortcut_ = this.appendMenuItem_('appcreateshortcut');
- this.createShortcut_.addEventListener(
- 'activate', this.onCreateShortcut_.bind(this));
-
- this.installLocallySeparator_ =
- menu.appendChild(MenuItem.createSeparator());
- this.installLocally_ = this.appendMenuItem_('appinstalllocally');
- this.installLocally_.addEventListener(
- 'activate', this.onInstallLocally_.bind(this));
-
- document.body.appendChild(menu);
- },
-
- /**
- * Appends a menu item to |this.menu|.
- * @param {string=} opt_textId If defined, the ID for the localized string
- * that acts as the item's label.
- * @private
- */
- appendMenuItem_(opt_textId) {
- const button =
- /** @type {!HTMLButtonElement} */ (document.createElement('button'));
- this.menu.appendChild(button);
- decorate(button, MenuItem);
- if (opt_textId) {
- button.textContent = loadTimeData.getString(opt_textId);
- }
- return button;
- },
-
- /**
- * Iterates over all the launch type menu items.
- * @param {function(MenuItem, number)} f The function to call for each
- * menu item. The parameters to the function include the menu item and
- * the associated launch ID.
- * @private
- */
- forAllLaunchTypes_(f) {
- // Order matters: index matches launchType id.
- const launchTypes = [
- this.launchPinnedTab_,
- this.launchRegularTab_,
- this.launchFullscreen_,
- this.launchNewWindow_,
- ];
-
- for (let i = 0; i < launchTypes.length; ++i) {
- if (!launchTypes[i]) {
- continue;
- }
-
- f(launchTypes[i], i);
- }
- },
-
- /**
- * Does all the necessary setup to show the menu for the given app.
- * @param {App} app The App object that will be showing a context menu.
- */
- setupForApp(app) {
- this.app_ = app;
-
- this.launch_.textContent = app.appData.title;
-
- const launchTypeWindow = this.launchNewWindow_;
- let hasLaunchType = false;
- this.forAllLaunchTypes_(function(launchTypeButton, id) {
- launchTypeButton.disabled = false;
- launchTypeButton.checked = app.appData.launch_type === id;
- // There are three cases when a launch type is hidden:
- // 1. type is anything except |launchTypeWindow| or
- // 2. if the launch type can't be changed or
- // 3. if the launch type is hidden.
- launchTypeButton.hidden = launchTypeButton !== launchTypeWindow ||
- !app.appData.mayChangeLaunchType || app.appData.hideDisplayMode;
-
- if (!launchTypeButton.hidden) {
- hasLaunchType = true;
- }
- });
-
- this.launchTypeMenuSeparator_.hidden =
- !app.appData.mayChangeLaunchType || !hasLaunchType;
-
- this.options_.disabled = !app.appData.optionsUrl || !app.appData.enabled;
- this.options_.hidden = app.appData.optionsUrl === undefined;
-
- this.uninstall_.disabled = !app.appData.mayDisable;
-
- this.appinfo_.textContent = '';
- if (app.appData.settingsMenuItemOverrideText) {
- this.appinfo_.textContent = app.appData.settingsMenuItemOverrideText;
- } else if (
- loadTimeData.getBoolean('canShowAppInfoDialog') &&
- app.appData.isLocallyInstalled) {
- this.appinfo_.textContent = loadTimeData.getString('appinfodialog');
- }
- this.appinfo_.hidden = !this.appinfo_.textContent;
- if (this.details_) {
- this.details_.hidden = !this.appinfo_.hidden;
- this.details_.disabled = !app.appData.detailsUrl;
- }
-
- this.createShortcutSeparator_.hidden = this.createShortcut_.hidden =
- !app.appData.mayCreateShortcuts;
-
- this.installLocallySeparator_.hidden = this.installLocally_.hidden =
- app.appData.isLocallyInstalled;
-
- this.runOnOsLogin_.hidden = !app.appData.mayShowRunOnOsLoginMode;
- this.runOnOsLogin_.disabled = !app.appData.mayToggleRunOnOsLoginMode;
- this.runOnOsLogin_.checked =
- app.appData.runOnOsLoginMode !== RUN_ON_OS_LOGIN_MODE.NOT_RUN;
- },
-
- /** @private */
- onActivate_() {
- chrome.send('launchApp', [this.app_.appId, APP_LAUNCH.NTP_APPS_MENU]);
- },
-
- /**
- * @param {Event} e
- * @private
- */
- onLaunchTypeChanged_(e) {
- const pressed = e.currentTarget;
- const app = this.app_;
- let targetLaunchType = pressed;
- // When bookmark apps are enabled, hosted apps can only toggle between
- // open as window and open as tab.
- targetLaunchType = this.launchNewWindow_.checked ? this.launchRegularTab_ :
- this.launchNewWindow_;
- this.forAllLaunchTypes_(function(launchTypeButton, id) {
- if (launchTypeButton === targetLaunchType) {
- chrome.send('setLaunchType', [app.appId, id]);
- // Manually update the launch type. We will only get
- // appsPrefChangeCallback calls after changes to other NTP instances.
- app.appData.launch_type = id;
- }
- });
- },
-
- /** @private */
- onShowOptions_() {
- window.location = this.app_.appData.optionsUrl;
- },
-
- /** @private */
- onShowDetails_() {
- let url = this.app_.appData.detailsUrl;
- url = appendParam(url, 'utm_source', 'chrome-ntp-launcher');
- window.location = url;
- },
-
- /** @private */
- onUninstall_() {
- chrome.send('uninstallApp', [this.app_.appData.id]);
- },
-
- /** @private */
- onCreateShortcut_() {
- chrome.send('createAppShortcut', [this.app_.appData.id]);
- },
-
- /** @private */
- onInstallLocally_() {
- chrome.send('installAppLocally', [this.app_.appData.id]);
- },
-
- /** @private */
- onShowAppInfo_() {
- chrome.send('showAppInfo', [this.app_.appData.id]);
- },
-
- /** @private */
- onRunOnOsLoginModeChanged_(e) {
- const pressed = e.currentTarget;
- const app = this.app_;
- let mode = RUN_ON_OS_LOGIN_MODE.NOT_RUN;
-
- if (pressed === this.runOnOsLogin_ && !pressed.checked) {
- mode = RUN_ON_OS_LOGIN_MODE.WINDOWED;
- }
-
- chrome.send('runOnOsLogin', [app.appData.id, mode]);
- },
-};
-
-/**
- * Creates a new App object.
- * @param {Object} appData The data object that describes the app.
- * @constructor
- * @extends {HTMLDivElement}
- */
-export function App(appData) {
- const el = /** @type {!App} */ (document.createElement('div'));
- el.__proto__ = App.prototype;
- el.initialize(appData);
-
- return el;
-}
-
-App.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- /**
- * Initialize the app object.
- * @param {Object} appData The data object that describes the app.
- *
- * TODO(crbug.com/425829): This function makes use of deprecated getter or
- * setter functions.
- * @suppress {deprecated}
- */
- initialize(appData) {
- this.appData = appData;
- assert(this.appData_.id, 'Got an app without an ID');
- this.id = this.appData_.id;
- this.setAttribute('role', 'menuitem');
- this.setAttribute('aria-label', this.appData_.full_name);
-
- this.className = 'app focusable';
-
- if (!this.appData_.icon_big_exists && this.appData_.icon_small_exists) {
- this.useSmallIcon_ = true;
- }
-
- this.appContents_ = (this.useSmallIcon_ ? $('app-small-icon-template') :
- $('app-large-icon-template'))
- .cloneNode(true);
- this.appContents_.id = '';
- this.appendChild(this.appContents_);
-
- this.appImgContainer_ =
- /** @type {HTMLElement} */ (this.querySelector('.app-img-container'));
- this.appImg_ = this.appImgContainer_.querySelector('img');
- this.setIcon();
-
- if (this.useSmallIcon_) {
- this.imgDiv_ =
- /** @type {HTMLElement} */ (this.querySelector('.app-icon-div'));
- this.addLaunchClickTarget_(this.imgDiv_);
- this.imgDiv_.title = this.appData_.full_name;
- chrome.send('getAppIconDominantColor', [this.id]);
- } else {
- this.addLaunchClickTarget_(this.appImgContainer_);
- this.appImgContainer_.title = this.appData_.full_name;
- }
-
- // The app's full name is shown in the tooltip, whereas the short name
- // is used for the label.
- const appSpan =
- /** @type {HTMLElement} */ (this.appContents_.querySelector('.title'));
- appSpan.textContent = this.appData_.title;
- appSpan.title = this.appData_.full_name;
- this.addLaunchClickTarget_(
- /** @type {HTMLElement} */
- (this.querySelector('.app-title-container')));
-
- if (this.appData_.is_deprecated_app) {
- this.classList.add('deprecated');
- }
-
- this.addEventListener('keydown', contextMenuHandler);
- this.addEventListener('keyup', contextMenuHandler);
-
- // This hack is here so that appContents.contextMenu will be the same as
- // this.contextMenu.
- const self = this;
-
- // TODO(crbug.com/425829): Remove above suppression once we no longer use
- // deprecated function defineGetter.
- // eslint-disable-next-line no-restricted-properties
- this.appContents_.__defineGetter__('contextMenu', function() {
- return self.contextMenu;
- });
-
- if (!this.appData_.kioskMode) {
- this.appContents_.addEventListener('contextmenu', contextMenuHandler);
- }
-
- this.addEventListener('mousedown', this.onMousedown_, true);
- this.addEventListener('keydown', this.onKeydown_);
- this.addEventListener('blur', this.onBlur_);
- },
-
- /**
- * Sets the color of the favicon dominant color bar.
- * @param {string} color The css-parsable value for the color.
- */
- set stripeColor(color) {
- this.querySelector('.color-stripe').style.backgroundColor = color;
- },
-
- /**
- * Removes the app tile from the page. Should be called after the app has
- * been uninstalled.
- *
- * TODO(dbeam): this method now conflicts with HTMLElement#remove(), which
- * is why the param is optional. Rename.
- *
- * @param {boolean=} opt_animate Whether the removal should be animated.
- */
- remove(opt_animate) {
- // Unset the ID immediately, because the app is already gone. But leave
- // the tile on the page as it animates out.
- this.id = '';
- this.tile.doRemove(opt_animate);
- },
-
- /**
- * Set the URL of the icon from |appData_|. This won't actually show the
- * icon until loadIcon() is called (for performance reasons; we don't want
- * to load icons until we have to).
- */
- setIcon() {
- let src =
- this.useSmallIcon_ ? this.appData_.icon_small : this.appData_.icon_big;
- if (!this.appData_.enabled || !this.appData_.isLocallyInstalled ||
- (!this.appData_.offlineEnabled && !navigator.onLine)) {
- src += '?grayscale=true';
- }
-
- this.appImgSrc_ = src;
- this.classList.add('icon-loading');
- },
-
- /**
- * Shows the icon for the app. That is, it causes chrome to load the app
- * icon resource.
- */
- loadIcon() {
- if (this.appImgSrc_) {
- this.appImg_.src = this.appImgSrc_;
- this.appImg_.classList.remove('invisible');
- this.appImgSrc_ = null;
- }
-
- this.classList.remove('icon-loading');
- },
-
- /**
- * Set the size and position of the app tile.
- * @param {number} size The total size of |this|.
- * @param {number} x The x-position.
- * @param {number} y The y-position.
- * animate.
- */
- setBounds(size, x, y) {
- const imgSize = size * APP_IMG_SIZE_FRACTION;
- this.appImgContainer_.style.width = this.appImgContainer_.style.height =
- toCssPx(this.useSmallIcon_ ? 16 : imgSize);
- if (this.useSmallIcon_) {
- // 3/4 is the ratio of 96px to 128px (the used height and full height
- // of icons in apps).
- const iconSize = imgSize * 3 / 4;
- // The -2 is for the div border to improve the visual alignment for the
- // icon div.
- this.imgDiv_.style.width = this.imgDiv_.style.height =
- toCssPx(iconSize - 2);
- // Margins set to get the icon placement right and the text to line up.
- this.imgDiv_.style.marginTop = this.imgDiv_.style.marginBottom =
- toCssPx((imgSize - iconSize) / 2);
- }
-
- this.style.width = this.style.height = toCssPx(size);
- this.style.left = toCssPx(x);
- this.style.right = toCssPx(x);
- this.style.top = toCssPx(y);
- },
-
- /** @private */
- onBlur_() {
- this.classList.remove('click-focus');
- this.appContents_.classList.remove('suppress-active');
- },
-
- /**
- * Invoked when an app is clicked.
- * @param {Event} e The click/auxclick event.
- * @private
- */
- onClick_(e) {
- if (/** @type {MouseEvent} */ (e).button > 1) {
- return;
- }
-
- chrome.send('launchApp', [
- this.appId,
- APP_LAUNCH.NTP_APPS_MAXIMIZED,
- 'chrome-ntp-icon',
- e.button,
- e.altKey,
- e.ctrlKey,
- e.metaKey,
- e.shiftKey,
- ]);
-
- // Don't allow the click to trigger a link or anything
- e.preventDefault();
- },
-
- /**
- * Invoked when the user presses a key while the app is focused.
- * @param {Event} e The key event.
- * @private
- */
- onKeydown_(e) {
- if (e.key === 'F10' && e.shiftKey) {
- this.appContents_.dispatchEvent(new MouseEvent('contextmenu'));
- e.preventDefault();
- e.stopPropagation();
- } else if (e.key === 'Enter') {
- chrome.send('launchApp', [
- this.appId,
- APP_LAUNCH.NTP_APPS_MAXIMIZED,
- '',
- 0,
- e.altKey,
- e.ctrlKey,
- e.metaKey,
- e.shiftKey,
- ]);
- e.preventDefault();
- e.stopPropagation();
- }
- },
-
- /**
- * Adds a node to the list of targets that will launch the app. This list
- * is also used in onMousedown to determine whether the app contents should
- * be shown as active (if we don't do this, then clicking anywhere in
- * appContents, even a part that is outside the ideally clickable region,
- * will cause the app icon to look active).
- * @param {HTMLElement} node The node that should be clickable.
- * @private
- */
- addLaunchClickTarget_(node) {
- node.classList.add('launch-click-target');
- node.addEventListener('click', this.onClick_.bind(this));
- node.addEventListener('auxclick', this.onClick_.bind(this));
- },
-
- /**
- * Handler for mousedown on the App. Adds a class that allows us to
- * not display as :active for right clicks (specifically, don't pulse on
- * these occasions). Also, we don't pulse for clicks that aren't within the
- * clickable regions.
- * @param {Event} e The mousedown event.
- * @private
- */
- onMousedown_(e) {
- // If the current platform uses middle click to autoscroll and this
- // mousedown isn't handled, onClick_() will never fire. crbug.com/142939
- if (e.button === 1) {
- e.preventDefault();
- }
-
- if (e.button === 2 ||
- !findAncestorByClass(
- /** @type {Element} */ (e.target), 'launch-click-target')) {
- this.appContents_.classList.add('suppress-active');
- } else {
- this.appContents_.classList.remove('suppress-active');
- }
-
- // This class is here so we don't show the focus state for apps that
- // gain keyboard focus via mouse clicking.
- this.classList.add('click-focus');
- },
-
- /**
- * Change the appData and update the appearance of the app.
- * @param {AppInfo} appData The new data object that describes the app.
- */
- replaceAppData(appData) {
- this.appData_ = appData;
- this.setIcon();
- this.loadIcon();
- },
-
- /**
- * The data and preferences for this app.
- * @type {Object}
- */
- set appData(data) {
- this.appData_ = data;
- },
- get appData() {
- return this.appData_;
- },
-
- /** @type {string} */
- get appId() {
- return this.appData_.id;
- },
-
- /**
- * Returns a pointer to the context menu for this app. All apps share the
- * singleton AppContextMenu. This function is called by the
- * ContextMenuHandler in response to the 'contextmenu' event.
- * @type {Menu}
- */
- get contextMenu() {
- const menu = AppContextMenu.getInstance();
- menu.setupForApp(this);
- return menu.menu;
- },
-
- /**
- * Returns whether this element can be 'removed' from chrome (i.e. whether
- * the user can drag it onto the trash and expect something to happen).
- * @return {boolean} True if the app can be uninstalled.
- */
- canBeRemoved() {
- return this.appData_.mayDisable;
- },
-
- /**
- * Uninstalls the app after it's been dropped on the trash.
- */
- removeFromChrome() {
- chrome.send('uninstallApp', [this.appData_.id, true]);
- this.tile.tilePage.removeTile(this.tile, true);
- },
-
- /**
- * Called when a drag is starting on the tile. Updates dataTransfer with
- * data for this tile.
- */
- setDragData(dataTransfer) {
- dataTransfer.setData('Text', this.appData_.title);
- dataTransfer.setData('URL', this.appData_.url);
- },
-};
-
-const appsPageGridValues = {
- // The fewest tiles we will show in a row.
- minColCount: 3,
- // The most tiles we will show in a row.
- maxColCount: 6,
-
- // The smallest a tile can be.
- minTileWidth: 64 / APP_IMG_SIZE_FRACTION,
- // The biggest a tile can be.
- maxTileWidth: 128 / APP_IMG_SIZE_FRACTION,
-
- // The padding between tiles, as a fraction of the tile width.
- tileSpacingFraction: 1 / 8,
-};
-TilePage.initGridValues(appsPageGridValues);
-
-/**
- * Creates a new AppsPage object.
- * @constructor
- * @extends {TilePage}
- */
-export function AppsPage() {
- const el = new TilePage(appsPageGridValues);
- el.__proto__ = AppsPage.prototype;
- el.initialize();
-
- return el;
-}
-
-AppsPage.prototype = {
- __proto__: TilePage.prototype,
-
- initialize() {
- this.classList.add('apps-page');
-
- this.addEventListener('cardselected', this.onCardSelected_);
-
- this.addEventListener('tilePage:tile_added', this.onTileAdded_);
-
- this.content_.addEventListener('scroll', this.onScroll.bind(this));
- },
-
- /**
- * Highlight a newly installed app as it's added to the NTP.
- * @param {AppInfo} appData The data object that describes the app.
- */
- insertAndHighlightApp(appData) {
- getCardSlider().selectCardByValue(this);
- this.content_.scrollTop = this.content_.scrollHeight;
- this.insertApp(appData, true);
- },
-
- /**
- * Similar to appendApp, but it respects the app_launch_ordinal field of
- * |appData|.
- * @param {Object} appData The data that describes the app.
- * @param {boolean} animate Whether to animate the insertion.
- */
- insertApp(appData, animate) {
- let index = this.tileElements_.length;
- for (let i = 0; i < this.tileElements_.length; i++) {
- if (appData.app_launch_ordinal <
- this.tileElements_[i].firstChild.appData.app_launch_ordinal) {
- index = i;
- break;
- }
- }
-
- this.addTileAt(new App(appData), index, animate);
- },
-
- /**
- * Handler for 'cardselected' event, fired when |this| is selected. The
- * first time this is called, we load all the app icons.
- * @private
- */
- onCardSelected_() {
- const apps = /** @type {NodeList<App>} */ (
- this.querySelectorAll('.app.icon-loading'));
- for (let i = 0; i < apps.length; i++) {
- apps[i].loadIcon();
- }
- },
-
- /**
- * Handler for tile additions to this page.
- * @param {Event} e The tilePage:tile_added event.
- * @private
- */
- onTileAdded_(e) {
- assert(e.currentTarget === this);
- assert(e.addedTile.firstChild instanceof App);
- if (this.classList.contains('selected-card')) {
- e.addedTile.firstChild.loadIcon();
- }
- },
-
- /**
- * A handler for when the apps page is scrolled (then we need to reposition
- * the bubbles.
- * @override
- */
- onScroll() {
- if (!this.selected) {
- return;
- }
- for (let i = 0; i < this.tileElements_.length; i++) {
- const app = this.tileElements_[i].firstChild;
- assert(app instanceof App);
- }
- },
-
- /** @override */
- doDragOver(e) {
- // Only animatedly re-arrange if the user is currently dragging an app.
- const tile = getCurrentlyDraggingTile();
- if (tile && tile.querySelector('.app')) {
- TilePage.prototype.doDragOver.call(this, e);
- } else {
- e.preventDefault();
- this.setDropEffect(e.dataTransfer);
- }
- },
-
- /** @override */
- shouldAcceptDrag(e) {
- if (getCurrentlyDraggingTile()) {
- return true;
- }
- if (!e.dataTransfer || !e.dataTransfer.types) {
- return false;
- }
- return Array.prototype.indexOf.call(
- e.dataTransfer.types, 'text/uri-list') !== -1;
- },
-
- /** @override */
- addDragData(dataTransfer, index) {
- let sourceId = -1;
- const currentlyDraggingTile = getCurrentlyDraggingTile();
- if (currentlyDraggingTile) {
- const tileContents = currentlyDraggingTile.firstChild;
- if (tileContents.classList.contains('app')) {
- const originalPage = currentlyDraggingTile.tilePage;
- const samePageDrag = originalPage === this;
- sourceId = samePageDrag ? DRAG_SOURCE.SAME_APPS_PANE :
- DRAG_SOURCE.OTHER_APPS_PANE;
- this.tileGrid_.insertBefore(
- currentlyDraggingTile, this.tileElements_[index]);
- this.tileMoved(currentlyDraggingTile);
- if (!samePageDrag) {
- originalPage.fireRemovedEvent(currentlyDraggingTile, index, true);
- this.fireAddedEvent(currentlyDraggingTile, index, true);
- }
- }
- } else {
- this.addOutsideData_(dataTransfer);
- sourceId = DRAG_SOURCE.OUTSIDE_NTP;
- }
-
- assert(sourceId !== -1);
- chrome.send(
- 'metricsHandler:recordInHistogram',
- ['NewTabPage.AppsPageDragSource', sourceId, DRAG_SOURCE_LIMIT]);
- },
-
- /**
- * Adds drag data that has been dropped from a source that is not a tile.
- * @param {Object} dataTransfer The data transfer object that holds drop
- * data.
- * @private
- */
- addOutsideData_(dataTransfer) {
- const url = dataTransfer.getData('url');
- assert(url);
-
- // If the dataTransfer has html data, use that html's text contents as the
- // title of the new link.
- const html = dataTransfer.getData('text/html');
- let title;
- if (html) {
- // It's important that we don't attach this node to the document
- // because it might contain scripts.
- const doc = document.implementation.createHTMLDocument();
- doc.body.innerHTML = unsanitizedPolicy.createHTML(html);
- title = doc.body.textContent;
- }
-
- // Make sure title is >=1 and <=45 characters for Chrome app limits.
- if (!title) {
- title = url;
- }
- if (title.length > 45) {
- title = title.substring(0, 45);
- }
- const data = {url: url, title: title};
-
- // Synthesize an app.
- this.generateAppForLink(data);
- },
-
- /**
- * Creates a new crx-less app manifest and installs it.
- * @param {Object} data The data object describing the link. Must have |url|
- * and |title| members.
- */
- generateAppForLink(data) {
- assert(data.url !== undefined);
- assert(data.title !== undefined);
- const pageIndex = getAppsPageIndex(this);
- chrome.send('generateAppForLink', [data.url, data.title, pageIndex]);
- },
-
- /** @override */
- tileMoved(draggedTile) {
- if (!(draggedTile.firstChild instanceof App)) {
- return;
- }
-
- const pageIndex = getAppsPageIndex(this);
- chrome.send('setPageIndex', [draggedTile.firstChild.appId, pageIndex]);
-
- const appIds = [];
- for (let i = 0; i < this.tileElements_.length; i++) {
- const tileContents = this.tileElements_[i].firstChild;
- if (tileContents instanceof App) {
- appIds.push(tileContents.appId);
- }
- }
-
- chrome.send('reorderApps', [draggedTile.firstChild.appId, appIds]);
- },
-
- /** @override */
- setDropEffect(dataTransfer) {
- const tile = getCurrentlyDraggingTile();
- if (tile && tile.querySelector('.app')) {
- setCurrentDropEffect(dataTransfer, 'move');
- } else {
- setCurrentDropEffect(dataTransfer, 'copy');
- }
- },
-};
-
-/**
- * Launches the specified app using the APP_LAUNCH_NTP_APP_RE_ENABLE
- * histogram. This should only be invoked from the AppLauncherHandler.
- * @param {string} appId The ID of the app.
- */
-function launchAppAfterEnable(appId) {
- chrome.send('launchApp', [appId, APP_LAUNCH.NTP_APP_RE_ENABLE]);
-}
-window['ntp'] = window['ntp'] || {};
-window['ntp']['launchAppAfterEnable'] = launchAppAfterEnable;
diff --git a/chromium/chrome/browser/resources/ntp4/card_slider.js b/chromium/chrome/browser/resources/ntp4/card_slider.js
deleted file mode 100644
index 18d281fe9d3..00000000000
--- a/chromium/chrome/browser/resources/ntp4/card_slider.js
+++ /dev/null
@@ -1,691 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assert, assertInstanceof} from 'chrome://resources/js/assert_ts.js';
-import {isRTL} from 'chrome://resources/js/util_ts.js';
-
-import {dispatchSimpleEvent} from './cr_deprecated.js';
-import {TouchHandler} from './touch_handler.js';
-
-/**
- * @fileoverview Card slider implementation. Allows you to create interactions
- * that have items that can slide left to right to reveal additional items.
- * Works by adding the necessary event handlers to a specific DOM structure
- * including a frame, container and cards.
- * - The frame defines the boundary of one item. Each card will be expanded to
- * fill the width of the frame. This element is also overflow hidden so that
- * the additional items left / right do not trigger horizontal scrolling.
- * - The container is what all the touch events are attached to. This element
- * will be expanded to be the width of all cards.
- * - The cards are the individual viewable items. There should be one card for
- * each item in the list. Only one card will be visible at a time. Two cards
- * will be visible while you are transitioning between cards.
- *
- * This class is designed to work well on any hardware-accelerated touch device.
- * It should still work on pre-hardware accelerated devices it just won't feel
- * very good. It should also work well with a mouse.
- */
-
-// Use an anonymous function to enable strict mode just for this file (which
-// will be concatenated with other files when embedded in Chrome
-/**
- * @constructor
- * @param {!Element} frame The bounding rectangle that cards are visible in.
- * @param {!Element} container The surrounding element that will have event
- * listeners attached to it.
- * @param {number} cardWidth The width of each card should have.
- */
-export function CardSlider(frame, container, cardWidth) {
- /**
- * @type {!Element}
- * @private
- */
- this.frame_ = frame;
-
- /**
- * @type {!Element}
- * @private
- */
- this.container_ = container;
-
- /**
- * Array of card elements.
- * @type {!Array<!Element>}
- * @private
- */
- this.cards_ = [];
-
- /**
- * Index of currently shown card.
- * @type {number}
- * @private
- */
- this.currentCard_ = -1;
-
- /**
- * @type {number}
- * @private
- */
- this.cardWidth_ = cardWidth;
-
- /**
- * @type {!TouchHandler}
- * @private
- */
- this.touchHandler_ = new TouchHandler(this.container_);
-}
-
-
-/**
- * The time to transition between cards when animating. Measured in ms.
- * @type {number}
- * @private
- * @const
- */
-CardSlider.TRANSITION_TIME_ = 200;
-
-
-/**
- * The minimum velocity required to transition cards if they did not drag past
- * the halfway point between cards. Measured in pixels / ms.
- * @type {number}
- * @private
- * @const
- */
-CardSlider.TRANSITION_VELOCITY_THRESHOLD_ = 0.2;
-
-
-CardSlider.prototype = {
- /**
- * The current left offset of the container relative to the frame. This
- * position does not include deltas from active drag operations, and
- * always aligns with a frame boundary.
- * @type {number}
- * @private
- */
- currentLeft_: 0,
-
- /**
- * Current offset relative to |currentLeft_| due to an active drag
- * operation.
- * @type {number}
- * @private
- */
- deltaX_: 0,
-
- /**
- * Initialize all elements and event handlers. Must call after construction
- * and before usage.
- * @param {boolean} ignoreMouseWheelEvents If true, horizontal mouse wheel
- * events will be ignored, rather than flipping between pages.
- */
- initialize(ignoreMouseWheelEvents) {
- const view = this.container_.ownerDocument.defaultView;
- assert(
- view.getComputedStyle(this.container_).display === '-webkit-box',
- 'Container should be display -webkit-box.');
- assert(
- view.getComputedStyle(this.frame_).overflow === 'hidden',
- 'Frame should be overflow hidden.');
- assert(
- view.getComputedStyle(this.container_).position === 'static',
- 'Container should be position static.');
-
- this.updateCardWidths_();
-
- this.mouseWheelScrollAmount_ = 0;
- this.mouseWheelCardSelected_ = false;
- this.mouseWheelIsContinuous_ = false;
- this.scrollClearTimeout_ = null;
- if (!ignoreMouseWheelEvents) {
- this.frame_.addEventListener('mousewheel', this.onMouseWheel_.bind(this));
- }
- this.container_.addEventListener(
- 'transitionend', this.onTransitionEnd_.bind(this));
-
- // Also support touch events in case a touch screen happens to be
- // available. Note that this has minimal impact in the common case of
- // no touch events (eg. we're mainly just adding listeners for events that
- // will never trigger).
- this.container_.addEventListener(
- TouchHandler.EventType.TOUCH_START, this.onTouchStart_.bind(this));
- this.container_.addEventListener(
- TouchHandler.EventType.DRAG_START, this.onDragStart_.bind(this));
- this.container_.addEventListener(
- TouchHandler.EventType.DRAG_MOVE, this.onDragMove_.bind(this));
- this.container_.addEventListener(
- TouchHandler.EventType.DRAG_END, this.onDragEnd_.bind(this));
-
- this.touchHandler_.enable(/* opt_capture */ false);
- },
-
- /**
- * Use in cases where the width of the frame has changed in order to update
- * the width of cards. For example should be used when orientation changes
- * in full width sliders.
- * @param {number} newCardWidth Width all cards should have, in pixels.
- */
- resize(newCardWidth) {
- if (newCardWidth !== this.cardWidth_) {
- this.cardWidth_ = newCardWidth;
-
- this.updateCardWidths_();
-
- // Must upate the transform on the container to show the correct card.
- this.transformToCurrentCard_();
- }
- },
-
- /**
- * Sets the cards used. Can be called more than once to switch card sets.
- * @param {!Array<!Element>} cards The individual viewable cards.
- * @param {number} index Index of the card to in the new set of cards to
- * navigate to.
- */
- setCards(cards, index) {
- assert(
- index >= 0 && index < cards.length,
- 'Invalid index in CardSlider#setCards');
- this.cards_ = cards;
-
- this.updateCardWidths_();
- this.updateSelectedCardAttributes_();
-
- // Jump to the given card index.
- this.selectCard(index, false, false, true);
- },
-
- /**
- * Ensures that for all cards:
- * - if the card is the current card, then it has 'selected-card' in its
- * classList, and is visible for accessibility
- * - if the card is not the selected card, then it does not have
- * 'selected-card' in its classList, and is invisible for accessibility.
- * @private
- */
- updateSelectedCardAttributes_() {
- for (let i = 0; i < this.cards_.length; i++) {
- if (i === this.currentCard_) {
- this.cards_[i].classList.add('selected-card');
- this.cards_[i].removeAttribute('aria-hidden');
- } else {
- this.cards_[i].classList.remove('selected-card');
- this.cards_[i].setAttribute('aria-hidden', true);
- }
- }
- },
-
- /**
- * Updates the width of each card.
- * @private
- */
- updateCardWidths_() {
- for (let i = 0, card; card = this.cards_[i]; i++) {
- card.style.width = this.cardWidth_ + 'px';
- }
- },
-
- /**
- * Returns the index of the current card.
- * @return {number} index of the current card.
- */
- get currentCard() {
- return this.currentCard_;
- },
-
- /**
- * Allows setting the current card index.
- * @param {number} index A new index to set the current index to.
- * @return {number} The new index after having been set.
- */
- set currentCard(index) {
- return (this.currentCard_ = index);
- },
-
- /**
- * Returns the number of cards.
- * @return {number} number of cards.
- */
- get cardCount() {
- return this.cards_.length;
- },
-
- /**
- * Returns the current card itself.
- * @return {!Element} the currently shown card.
- */
- get currentCardValue() {
- return this.cards_[this.currentCard_];
- },
-
- /**
- * Returns the frame holding the cards.
- * @return {Element} The frame used to position the cards.
- */
- get frame() {
- return this.frame_;
- },
-
- /**
- * Handle horizontal scrolls to flip between pages.
- * @private
- */
- onMouseWheel_(e) {
- if (e.wheelDeltaX === 0) {
- return;
- }
-
- // Continuous devices such as an Apple Touchpad or Apple MagicMouse will
- // send arbitrary delta values. Conversly, standard mousewheels will
- // send delta values in increments of 120. (There is of course a small
- // chance we mistake a continuous device for a non-continuous device.
- // Unfortunately there isn't a better way to do this until real touch
- // events are available to desktop clients.)
- const DISCRETE_DELTA = 120;
- if (e.wheelDeltaX % DISCRETE_DELTA) {
- this.mouseWheelIsContinuous_ = true;
- }
-
- if (this.mouseWheelIsContinuous_) {
- // For continuous devices, detect a page swipe when the accumulated
- // delta matches a pre-defined threshhold. After changing the page,
- // ignore wheel events for a short time before repeating this process.
- if (this.mouseWheelCardSelected_) {
- return;
- }
- this.mouseWheelScrollAmount_ += e.wheelDeltaX;
- if (Math.abs(this.mouseWheelScrollAmount_) >= 600) {
- let pagesToScroll = this.mouseWheelScrollAmount_ > 0 ? 1 : -1;
- if (!isRTL()) {
- pagesToScroll *= -1;
- }
- let newCardIndex = this.currentCard + pagesToScroll;
- newCardIndex =
- Math.min(this.cards_.length - 1, Math.max(0, newCardIndex));
- this.selectCard(newCardIndex, true);
- this.mouseWheelCardSelected_ = true;
- }
- } else {
- // For discrete devices, consider each wheel tick a page change.
- let pagesToScroll = e.wheelDeltaX / DISCRETE_DELTA;
- if (!isRTL()) {
- pagesToScroll *= -1;
- }
- let newCardIndex = this.currentCard + pagesToScroll;
- newCardIndex =
- Math.min(this.cards_.length - 1, Math.max(0, newCardIndex));
- this.selectCard(newCardIndex, true);
- }
-
- // We got a mouse wheel event, so cancel any pending scroll wheel timeout.
- if (this.scrollClearTimeout_ !== null) {
- clearTimeout(this.scrollClearTimeout_);
- }
- // If we didn't use up all the scroll, hold onto it for a little bit, but
- // drop it after a delay.
- if (this.mouseWheelScrollAmount_ !== 0) {
- this.scrollClearTimeout_ =
- setTimeout(this.clearMouseWheelScroll_.bind(this), 500);
- }
- },
-
- /**
- * Resets the amount of horizontal scroll we've seen to 0. See
- * onMouseWheel_.
- * @private
- */
- clearMouseWheelScroll_() {
- this.mouseWheelScrollAmount_ = 0;
- this.mouseWheelCardSelected_ = false;
- },
-
- /**
- * Handles the ends of transitions on transform (animated
- * card switches).
- * @param {Event} e The transitionend event.
- * @private
- */
- onTransitionEnd_(e) {
- // Ignore irrelevant transitions that might bubble up.
- if (e.target !== this.container_ || e.propertyName !== 'transform') {
- return;
- }
- this.fireChangeEndedEvent_(true);
- },
-
- /**
- * Dispatches a simple event to tell subscribers we're done moving to the
- * newly selected card.
- * @param {boolean} wasAnimated whether or not the change was animated.
- * @private
- */
- fireChangeEndedEvent_(wasAnimated) {
- const e = document.createEvent('Event');
- e.initEvent('cardSlider:card_change_ended', true, true);
- e.cardSlider = this;
- e.changedTo = this.currentCard_;
- e.wasAnimated = wasAnimated;
- this.container_.dispatchEvent(e);
- },
-
- /**
- * Add a card to the card slider at a particular index. If the card being
- * added is inserted in front of the current card, cardSlider.currentCard
- * will be adjusted accordingly (to current card + 1).
- * @param {!Node} card A card that will be added to the card slider.
- * @param {number} index An index at which the given |card| should be
- * inserted. Must be positive and less than the number of cards.
- */
- addCardAtIndex(card, index) {
- assert(card instanceof Node, '|card| isn\'t a Node');
- this.assertValidIndex_(index);
- this.cards_ = Array.prototype.concat.call(
- this.cards_.slice(0, index), card, this.cards_.slice(index));
-
- this.updateSelectedCardAttributes_();
-
- if (this.currentCard_ === -1) {
- this.currentCard_ = 0;
- } else if (index <= this.currentCard_) {
- this.selectCard(this.currentCard_ + 1, false, true, true);
- }
-
- this.fireAddedEvent_(card, index);
- },
-
- /**
- * Append a card to the end of the list.
- * @param {!Element} card A card to add at the end of the card slider.
- */
- appendCard(card) {
- assertInstanceof(card, Element);
- this.cards_.push(card);
- this.fireAddedEvent_(card, this.cards_.length - 1);
- },
-
- /**
- * Dispatches a simple event to tell interested subscribers that a card was
- * added to this card slider.
- * @param {Node} card The recently added card.
- * @param {number} index The position of the newly added card.
- * @private
- */
- fireAddedEvent_(card, index) {
- this.assertValidIndex_(index);
- const e = document.createEvent('Event');
- e.initEvent('cardSlider:card_added', true, true);
- e.addedIndex = index;
- e.addedCard = card;
- this.container_.dispatchEvent(e);
- },
-
- /**
- * Returns the card at a particular index.
- * @param {number} index The index of the card to return.
- * @return {!Element} The card at the given index.
- */
- getCardAtIndex(index) {
- this.assertValidIndex_(index);
- return this.cards_[index];
- },
-
- /**
- * Removes a card by index from the card slider. If the card to be removed
- * is the current card or in front of the current card, the current card
- * will be updated (to current card - 1).
- * @param {!Element} card A card to be removed.
- */
- removeCard(card) {
- assertInstanceof(card, Element);
- this.removeCardAtIndex(this.cards_.indexOf(card));
- },
-
- /**
- * Removes a card by index from the card slider. If the card to be removed
- * is the current card or in front of the current card, the current card
- * will be updated (to current card - 1).
- * @param {number} index The index of the tile that should be removed.
- */
- removeCardAtIndex(index) {
- this.assertValidIndex_(index);
- const removed = this.cards_.splice(index, 1).pop();
-
- if (this.cards_.length === 0) {
- this.currentCard_ = -1;
- } else if (index < this.currentCard_) {
- this.selectCard(this.currentCard_ - 1, false, true);
- }
-
- this.fireRemovedEvent_(removed, index);
- },
-
- /**
- * Dispatches a cardSlider:card_removed event so interested subscribers know
- * when a card was removed from this card slider.
- * @param {Node} card The recently removed card.
- * @param {number} index The index of the card before it was removed.
- * @private
- */
- fireRemovedEvent_(card, index) {
- const e = document.createEvent('Event');
- e.initEvent('cardSlider:card_removed', true, true);
- e.removedCard = card;
- e.removedIndex = index;
- this.container_.dispatchEvent(e);
- },
-
- /**
- * This re-syncs the transform that's used to position the frame in
- * the likely event it needs to be updated by a card being inserted or
- * removed in the flow.
- */
- repositionFrame() {
- this.transformToCurrentCard_();
- },
-
- /**
- * Checks the the given |index| exists in this.cards_.
- * @param {number} index An index to check.
- * @private
- */
- assertValidIndex_(index) {
- assert(index >= 0 && index < this.cards_.length);
- },
-
- /**
- * Selects a new card, ensuring that it is a valid index, transforming the
- * view and possibly calling the change card callback.
- * @param {number} newCardIndex Index of card to show.
- * @param {boolean=} opt_animate If true will animate transition from
- * current position to new position.
- * @param {boolean=} opt_dontNotify If true, don't tell subscribers that
- * we've changed cards.
- * @param {boolean=} opt_forceChange If true, ignore if the card already
- * selected.
- */
- selectCard(newCardIndex, opt_animate, opt_dontNotify, opt_forceChange) {
- this.assertValidIndex_(newCardIndex);
-
- const previousCard = this.currentCardValue;
- let isChangingCard =
- !this.cards_[newCardIndex].classList.contains('selected-card');
-
- if (typeof opt_forceChange !== 'undefined' && opt_forceChange) {
- isChangingCard = true;
- }
-
- if (isChangingCard) {
- this.currentCard_ = newCardIndex;
- this.updateSelectedCardAttributes_();
- }
-
- const willTransitionHappen = this.transformToCurrentCard_(opt_animate);
-
- if (isChangingCard && !opt_dontNotify) {
- const event = document.createEvent('Event');
- event.initEvent('cardSlider:card_changed', true, true);
- event.cardSlider = this;
- event.wasAnimated = !!opt_animate;
- this.container_.dispatchEvent(event);
-
- // We also dispatch an event on the cards themselves.
- if (previousCard) {
- dispatchSimpleEvent(previousCard, 'carddeselected', true, true);
- }
- dispatchSimpleEvent(this.currentCardValue, 'cardselected', true, true);
- }
-
- // If we're not changing, animated, or transitioning, fire a
- // cardSlider:card_change_ended event right away.
- if ((!isChangingCard || !opt_animate || !willTransitionHappen) &&
- !opt_dontNotify) {
- this.fireChangeEndedEvent_(false);
- }
- },
-
- /**
- * Selects a card from the stack. Passes through to selectCard.
- * @param {!Element} newCard The card that should be selected.
- * @param {boolean=} opt_animate Whether to animate.
- */
- selectCardByValue(newCard, opt_animate) {
- const i = this.cards_.indexOf(newCard);
- assert(i !== -1);
- this.selectCard(i, opt_animate);
- },
-
- /**
- * Centers the view on the card denoted by this.currentCard. Can either
- * animate to that card or snap to it.
- * @param {boolean=} opt_animate If true will animate transition from
- * current position to new position.
- * @return {boolean} Whether or not a transformation was necessary.
- * @private
- */
- transformToCurrentCard_(opt_animate) {
- const prevLeft = this.currentLeft_;
- this.currentLeft_ = -this.cardWidth_ *
- (isRTL() ? this.cards_.length - this.currentCard - 1 :
- this.currentCard);
-
- // If there's no change, return something to let the caller know there
- // won't be a transition occuring.
- if (prevLeft === this.currentLeft_ && this.deltaX_ === 0) {
- return false;
- }
-
- // Animate to the current card, which will either transition if the
- // current card is new, or reset the existing card if we didn't drag
- // enough to change cards.
- let transition = '';
- if (opt_animate) {
- transition =
- 'transform ' + CardSlider.TRANSITION_TIME_ + 'ms ease-in-out';
- }
- this.container_.style.transition = transition;
- this.translateTo_(this.currentLeft_);
-
- return true;
- },
-
- /**
- * Moves the view to the specified position.
- * @param {number} x Horizontal position to move to.
- * @private
- */
- translateTo_(x) {
- // We use a transform to slide because this is GPU accelerated on
- // Chrome and iOS. Once Chrome does GPU acceleration on the position
- // fixed-layout elements we could simply set the element's position to
- // fixed and modify 'left' instead.
- this.deltaX_ = x - this.currentLeft_;
- this.container_.style.transform = 'translate3d(' + x + 'px, 0, 0)';
- },
-
- /* Touch ******************************************************************/
-
- /**
- * Clear any transition that is in progress and enable dragging for the
- * touch.
- * @param {!Event} e The TouchHandler event.
- * @private
- */
- onTouchStart_(e) {
- e = /** @type {!TouchHandler.Event} */ (e);
- this.container_.style.transition = '';
- e.enableDrag = true;
- },
-
- /**
- * Tell the TouchHandler that dragging is acceptable when the user begins by
- * scrolling horizontally and there is more than one card to slide.
- * @param {!Event} e The TouchHandler event.
- * @private
- */
- onDragStart_(e) {
- e = /** @type {!TouchHandler.Event} */ (e);
- e.enableDrag =
- this.cardCount > 1 && Math.abs(e.dragDeltaX) > Math.abs(e.dragDeltaY);
- },
-
- /**
- * On each drag move event reposition the container appropriately so the
- * cards look like they are sliding.
- * @param {!Event} e The TouchHandler event.
- * @private
- */
- onDragMove_(e) {
- e = /** @type {!TouchHandler.Event} */ (e);
- let deltaX = e.dragDeltaX;
- // If dragging beyond the first or last card then apply a backoff so the
- // dragging feels stickier than usual.
- if (!this.currentCard && deltaX > 0 ||
- this.currentCard === (this.cards_.length - 1) && deltaX < 0) {
- deltaX /= 2;
- }
- this.translateTo_(this.currentLeft_ + deltaX);
- },
-
- /**
- * On drag end events we may want to transition to another card, depending
- * on the ending position of the drag and the velocity of the drag.
- * @param {!Event} e The TouchHandler event.
- * @private
- */
- onDragEnd_(e) {
- e = /** @type {!TouchHandler.Event} */ (e);
- const deltaX = e.dragDeltaX;
- const velocity = this.touchHandler_.getEndVelocity().x;
- const newX = this.currentLeft_ + deltaX;
- let newCardIndex = Math.round(-newX / this.cardWidth_);
-
- if (newCardIndex === this.currentCard &&
- Math.abs(velocity) > CardSlider.TRANSITION_VELOCITY_THRESHOLD_) {
- // The drag wasn't far enough to change cards but the velocity was
- // high enough to transition anyways. If the velocity is to the left
- // (negative) then the user wishes to go right (card + 1).
- newCardIndex += velocity > 0 ? -1 : 1;
- }
- // Ensure that the new card index is valid. The new card index could be
- // invalid if a swipe suggests scrolling off the end of the list of
- // cards.
- if (newCardIndex < 0) {
- newCardIndex = 0;
- } else if (newCardIndex >= this.cardCount) {
- newCardIndex = this.cardCount - 1;
- }
- this.selectCard(newCardIndex, /* animate */ true);
- },
-
- /**
- * Cancel any current touch/slide as if we saw a touch end
- */
- cancelTouch() {
- // Stop listening to any current touch
- this.touchHandler_.cancelTouch();
-
- // Ensure we're at a card bounary
- this.transformToCurrentCard_(true);
- },
-};
diff --git a/chromium/chrome/browser/resources/ntp4/command.js b/chromium/chrome/browser/resources/ntp4/command.js
deleted file mode 100644
index a205613b005..00000000000
--- a/chromium/chrome/browser/resources/ntp4/command.js
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// NOTE: This file depends on ui.js (or the autogenerated ui.m.js module
-// version). These files and all files that depend on them are deprecated, and
-// should only be used by legacy UIs that have not yet been updated to new
-// patterns. Use Web Components in any new code.
-
-/**
- * @fileoverview A command is an abstraction of an action a user can do in the
- * UI.
- *
- * When the focus changes in the document for each command a canExecute event
- * is dispatched on the active element. By listening to this event you can
- * enable and disable the command by setting the event.canExecute property.
- *
- * When a command is executed a command event is dispatched on the active
- * element. Note that you should stop the propagation after you have handled the
- * command if there might be other command listeners higher up in the DOM tree.
- */
-
-// clang-format off
-import {assert} from 'chrome://resources/js/assert_ts.js';
-
-import {define as crUiDefine} from './ui.js';
-
-import {KeyboardShortcutList} from 'chrome://resources/js/keyboard_shortcut_list.js';
-
-import {dispatchPropertyChange, getPropertyDescriptor, PropertyKind} from './cr_deprecated.js';
-import {MenuItem} from './menu_item.js';
-// clang-format on
-
-/**
- * Creates a new command element.
- * @constructor
- * @extends {HTMLElement}
- */
-export const Command = crUiDefine('command');
-
-Command.prototype = {
- __proto__: HTMLElement.prototype,
-
- /**
- * Initializes the command.
- */
- decorate() {
- assert(this.ownerDocument);
- CommandManager.init(this.ownerDocument);
-
- if (this.hasAttribute('shortcut')) {
- this.shortcut = this.getAttribute('shortcut');
- }
- },
-
- /**
- * Executes the command by dispatching a command event on the given element.
- * If |element| isn't given, the active element is used instead.
- * If the command is {@code disabled} this does nothing.
- * @param {HTMLElement=} opt_element Optional element to dispatch event on.
- */
- execute(opt_element) {
- if (this.disabled) {
- return;
- }
- const doc = this.ownerDocument;
- if (doc.activeElement) {
- const e = new Event('command', {bubbles: true});
- e.command = this;
-
- (opt_element || doc.activeElement).dispatchEvent(e);
- }
- },
-
- /**
- * Call this when there have been changes that might change whether the
- * command can be executed or not.
- * @param {Node=} opt_node Node for which to actuate command state.
- */
- canExecuteChange(opt_node) {
- dispatchCanExecuteEvent(this, opt_node || this.ownerDocument.activeElement);
- },
-
- /**
- * The keyboard shortcut that triggers the command. This is a string
- * consisting of a key (as reported by WebKit in keydown) as
- * well as optional key modifiers joinded with a '|'.
- *
- * Multiple keyboard shortcuts can be provided by separating them by
- * whitespace.
- *
- * For example:
- * "F1"
- * "Backspace|Meta" for Apple command backspace.
- * "a|Ctrl" for Control A
- * "Delete Backspace|Meta" for Delete and Command Backspace
- *
- * @type {string}
- */
- shortcut_: '',
- get shortcut() {
- return this.shortcut_;
- },
- set shortcut(shortcut) {
- const oldShortcut = this.shortcut_;
- if (shortcut !== oldShortcut) {
- this.keyboardShortcuts_ = new KeyboardShortcutList(shortcut);
-
- // Set this after the keyboardShortcuts_ since that might throw.
- this.shortcut_ = shortcut;
- dispatchPropertyChange(this, 'shortcut', this.shortcut_, oldShortcut);
- }
- },
-
- /**
- * Whether the event object matches the shortcut for this command.
- * @param {!Event} e The key event object.
- * @return {boolean} Whether it matched or not.
- */
- matchesEvent(e) {
- if (!this.keyboardShortcuts_) {
- return false;
- }
- return this.keyboardShortcuts_.matchesEvent(e);
- },
-};
-
-/**
- * The label of the command.
- * @type {string}
- */
-Command.prototype.label;
-Object.defineProperty(
- Command.prototype, 'label',
- getPropertyDescriptor('label', PropertyKind.ATTR));
-
-/**
- * Whether the command is disabled or not.
- * @type {boolean}
- */
-Command.prototype.disabled;
-Object.defineProperty(
- Command.prototype, 'disabled',
- getPropertyDescriptor('disabled', PropertyKind.BOOL_ATTR));
-
-/**
- * Whether the command is hidden or not.
- */
-Object.defineProperty(
- Command.prototype, 'hidden',
- getPropertyDescriptor('hidden', PropertyKind.BOOL_ATTR));
-
-/**
- * Whether the command is checked or not.
- * @type {boolean}
- */
-Command.prototype.checked;
-Object.defineProperty(
- Command.prototype, 'checked',
- getPropertyDescriptor('checked', PropertyKind.BOOL_ATTR));
-
-/**
- * The flag that prevents the shortcut text from being displayed on menu.
- *
- * If false, the keyboard shortcut text (eg. "Ctrl+X" for the cut command)
- * is displayed in menu when the command is associated with a menu item.
- * Otherwise, no text is displayed.
- * @type {boolean}
- */
-Command.prototype.hideShortcutText;
-Object.defineProperty(
- Command.prototype, 'hideShortcutText',
- getPropertyDescriptor('hideShortcutText', PropertyKind.BOOL_ATTR));
-
-/**
- * Dispatches a canExecute event on the target.
- * @param {!Command} command The command that we are testing for.
- * @param {EventTarget} target The target element to dispatch the event on.
- */
-function dispatchCanExecuteEvent(command, target) {
- const e = new CanExecuteEvent(command);
- target.dispatchEvent(e);
- command.disabled = !e.canExecute;
-}
-
-/**
- * The command managers for different documents.
- * @type {!Map<!Document, !CommandManager>}
- */
-const commandManagers = new Map();
-
-/**
- * Keeps track of the focused element and updates the commands when the focus
- * changes.
- * @param {!Document} doc The document that we are managing the commands for.
- * @constructor
- */
-function CommandManager(doc) {
- doc.addEventListener('focus', this.handleFocus_.bind(this), true);
- // Make sure we add the listener to the bubbling phase so that elements can
- // prevent the command.
- doc.addEventListener('keydown', this.handleKeyDown_.bind(this), false);
-}
-
-/**
- * Initializes a command manager for the document as needed.
- * @param {!Document} doc The document to manage the commands for.
- */
-CommandManager.init = function(doc) {
- if (!commandManagers.has(doc)) {
- commandManagers.set(doc, new CommandManager(doc));
- }
-};
-
-CommandManager.prototype = {
-
- /**
- * Handles focus changes on the document.
- * @param {Event} e The focus event object.
- * @private
- * @suppress {checkTypes}
- * TODO(vitalyp): remove the suppression.
- */
- handleFocus_(e) {
- const target = e.target;
-
- // Ignore focus on a menu button or command item.
- if (target.menu || target.command || (target instanceof MenuItem)) {
- return;
- }
-
- const commands = Array.prototype.slice.call(
- target.ownerDocument.querySelectorAll('command'));
-
- commands.forEach(function(command) {
- dispatchCanExecuteEvent(command, target);
- });
- },
-
- /**
- * Handles the keydown event and routes it to the right command.
- * @param {!Event} e The keydown event.
- */
- handleKeyDown_(e) {
- const target = e.target;
- const commands = Array.prototype.slice.call(
- target.ownerDocument.querySelectorAll('command'));
-
- for (let i = 0, command; command = commands[i]; i++) {
- if (command.matchesEvent(e)) {
- // When invoking a command via a shortcut, we have to manually check
- // if it can be executed, since focus might not have been changed
- // what would have updated the command's state.
- command.canExecuteChange();
-
- if (!command.disabled) {
- e.preventDefault();
- // We do not want any other element to handle this.
- e.stopPropagation();
- command.execute();
- return;
- }
- }
- }
- },
-};
-
-/**
- * The event type used for canExecute events.
- * @param {!Command} command The command that we are evaluating.
- * @extends {Event}
- * @constructor
- * @class
- */
-export function CanExecuteEvent(command) {
- const e = new Event('canExecute', {bubbles: true, cancelable: true});
- e.__proto__ = CanExecuteEvent.prototype;
- e.command = command;
- return e;
-}
-
-CanExecuteEvent.prototype = {
- __proto__: Event.prototype,
-
- /**
- * The current command
- * @type {Command}
- */
- command: null,
-
- /**
- * Whether the target can execute the command. Setting this also stops the
- * propagation and prevents the default. Callers can tell if an event has
- * been handled via |this.defaultPrevented|.
- * @type {boolean}
- */
- canExecute_: false,
- get canExecute() {
- return this.canExecute_;
- },
- set canExecute(canExecute) {
- this.canExecute_ = !!canExecute;
- this.stopPropagation();
- this.preventDefault();
- },
-};
diff --git a/chromium/chrome/browser/resources/ntp4/context_menu_handler.js b/chromium/chrome/browser/resources/ntp4/context_menu_handler.js
deleted file mode 100644
index 4f885bbcf5d..00000000000
--- a/chromium/chrome/browser/resources/ntp4/context_menu_handler.js
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// NOTE: This file depends on ui.js (or the autogenerated ui.m.js module
-// version). These files and all files that depend on them are deprecated, and
-// should only be used by legacy UIs that have not yet been updated to new
-// patterns. Use Web Components in any new code.
-
-// clang-format off
-import {assertInstanceof} from 'chrome://resources/js/assert_ts.js';
-import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-
-import {dispatchPropertyChange} from './cr_deprecated.js';
-import {Menu} from './menu.js';
-import {MenuItem} from './menu_item.js';
-import {HideType} from './menu_button.js';
-import {positionPopupAtPoint} from './position_util.js';
-import {decorate} from './ui.js';
-// clang-format on
-
-
-/**
- * Handles context menus.
- * @implements {EventListener}
- */
-class ContextMenuHandler extends EventTarget {
- constructor() {
- super();
- /** @private {!EventTracker} */
- this.showingEvents_ = new EventTracker();
-
- /**
- * The menu that we are currently showing.
- * @private {?Menu}
- */
- this.menu_ = null;
-
- /** @private {?number} */
- this.hideTimestamp_ = null;
-
- /** @private {boolean} */
- this.keyIsDown_ = false;
- }
-
- get menu() {
- return this.menu_;
- }
-
- /**
- * Shows a menu as a context menu.
- * @param {!Event} e The event triggering the show (usually a contextmenu
- * event).
- * @param {!Menu} menu The menu to show.
- */
- showMenu(e, menu) {
- assertInstanceof(e.currentTarget, Node);
- menu.updateCommands(e.currentTarget);
- if (!menu.hasVisibleItems()) {
- return;
- }
-
- this.menu_ = menu;
- menu.classList.remove('hide-delayed');
- menu.show({x: e.screenX, y: e.screenY});
- menu.contextElement = e.currentTarget;
-
- // When the menu is shown we steal a lot of events.
- const doc = menu.ownerDocument;
- const win = /** @type {!Window} */ (doc.defaultView);
- this.showingEvents_.add(doc, 'keydown', this, true);
- this.showingEvents_.add(doc, 'mousedown', this, true);
- this.showingEvents_.add(doc, 'touchstart', this, true);
- this.showingEvents_.add(doc, 'focus', this);
- this.showingEvents_.add(win, 'popstate', this);
- this.showingEvents_.add(win, 'resize', this);
- this.showingEvents_.add(win, 'blur', this);
- this.showingEvents_.add(menu, 'contextmenu', this);
- this.showingEvents_.add(menu, 'activate', this);
- this.positionMenu_(e, menu);
-
- const ev = new Event('show');
- ev.element = menu.contextElement;
- ev.menu = menu;
- this.dispatchEvent(ev);
- }
-
- /**
- * Hide the currently shown menu.
- * @param {HideType=} opt_hideType Type of hide.
- * default: HideType.INSTANT.
- */
- hideMenu(opt_hideType) {
- const menu = this.menu;
- if (!menu) {
- return;
- }
-
- if (opt_hideType === HideType.DELAYED) {
- menu.classList.add('hide-delayed');
- } else {
- menu.classList.remove('hide-delayed');
- }
- menu.hide();
- const originalContextElement = menu.contextElement;
- menu.contextElement = null;
- this.showingEvents_.removeAll();
- menu.selectedIndex = -1;
- this.menu_ = null;
-
- // On windows we might hide the menu in a right mouse button up and if
- // that is the case we wait some short period before we allow the menu
- // to be shown again.
- this.hideTimestamp_ = 0;
- // <if expr="is_win">
- this.hideTimestamp_ = Date.now();
- // </if>
-
- const ev = new Event('hide');
- ev.element = originalContextElement;
- ev.menu = menu;
- this.dispatchEvent(ev);
- }
-
- /**
- * Positions the menu
- * @param {!Event} e The event object triggering the showing.
- * @param {!Menu} menu The menu to position.
- * @private
- */
- positionMenu_(e, menu) {
- // TODO(arv): Handle scrolled documents when needed.
-
- const element = e.currentTarget;
- let x;
- let y;
- // When the user presses the context menu key (on the keyboard) we need
- // to detect this.
- if (this.keyIsDown_) {
- const rect = element.getRectForContextMenu ?
- element.getRectForContextMenu() :
- element.getBoundingClientRect();
- const offset = Math.min(rect.width, rect.height) / 2;
- x = rect.left + offset;
- y = rect.top + offset;
- } else {
- x = e.clientX;
- y = e.clientY;
- }
-
- positionPopupAtPoint(x, y, menu);
- }
-
- /**
- * Handles event callbacks.
- * @param {!Event} e The event object.
- */
- handleEvent(e) {
- // Keep track of keydown state so that we can use that to determine the
- // reason for the contextmenu event.
- switch (e.type) {
- case 'keydown':
- this.keyIsDown_ = !e.ctrlKey && !e.altKey &&
- // context menu key or Shift-F10
- (e.keyCode === 93 && !e.shiftKey || e.key === 'F10' && e.shiftKey);
- break;
-
- case 'keyup':
- this.keyIsDown_ = false;
- break;
- }
-
- // Context menu is handled even when we have no menu.
- if (e.type !== 'contextmenu' && !this.menu) {
- return;
- }
-
- switch (e.type) {
- case 'mousedown':
- if (!this.menu.contains(e.target)) {
- this.hideMenu();
- // <if expr="is_linux or is_macosx or chromeos_lacros">
- if (e.button === 0 /* Left button */) {
- // Emulate Mac and Linux, which swallow native 'mousedown' events
- // that close menus.
- e.preventDefault();
- e.stopPropagation();
- }
- // </if>
- } else {
- e.preventDefault();
- }
- break;
-
- case 'touchstart':
- if (!this.menu.contains(e.target)) {
- this.hideMenu();
- }
- break;
-
- case 'keydown':
- if (e.key === 'Escape') {
- this.hideMenu();
- e.stopPropagation();
- e.preventDefault();
-
- // If the menu is visible we let it handle all the keyboard events.
- } else if (this.menu) {
- this.menu.handleKeyDown(e);
- e.preventDefault();
- e.stopPropagation();
- }
- break;
-
- case 'activate':
- const hideDelayed = e.target instanceof MenuItem && e.target.checkable;
- this.hideMenu(hideDelayed ? HideType.DELAYED : HideType.INSTANT);
- break;
-
- case 'focus':
- if (!this.menu.contains(e.target)) {
- this.hideMenu();
- }
- break;
-
- case 'blur':
- this.hideMenu();
- break;
-
- case 'popstate':
- case 'resize':
- this.hideMenu();
- break;
-
- case 'contextmenu':
- if ((!this.menu || !this.menu.contains(e.target)) &&
- (!this.hideTimestamp_ || Date.now() - this.hideTimestamp_ > 50)) {
- this.showMenu(e, e.currentTarget.contextMenu);
- }
- e.preventDefault();
- // Don't allow elements further up in the DOM to show their menus.
- e.stopPropagation();
- break;
- }
- }
-
- /**
- * Adds a contextMenu property to an element or element class.
- * @param {!Element|!Function} elementOrClass The element or class to add
- * the contextMenu property to.
- */
- addContextMenuProperty(elementOrClass) {
- const target = typeof elementOrClass === 'function' ?
- elementOrClass.prototype :
- elementOrClass;
-
- // eslint-disable-next-line no-restricted-properties
- target.__defineGetter__('contextMenu', function() {
- return this.contextMenu_;
- });
- // eslint-disable-next-line no-restricted-properties
- target.__defineSetter__('contextMenu', function(menu) {
- const oldContextMenu = this.contextMenu;
-
- if (typeof menu === 'string' && menu[0] === '#') {
- menu = this.ownerDocument.getElementById(menu.slice(1));
- decorate(menu, Menu);
- }
-
- if (menu === oldContextMenu) {
- return;
- }
-
- if (oldContextMenu && !menu) {
- this.removeEventListener('contextmenu', contextMenuHandler);
- this.removeEventListener('keydown', contextMenuHandler);
- this.removeEventListener('keyup', contextMenuHandler);
- }
- if (menu && !oldContextMenu) {
- this.addEventListener('contextmenu', contextMenuHandler);
- this.addEventListener('keydown', contextMenuHandler);
- this.addEventListener('keyup', contextMenuHandler);
- }
-
- this.contextMenu_ = menu;
-
- if (menu && menu.id) {
- this.setAttribute('contextmenu', '#' + menu.id);
- }
-
- dispatchPropertyChange(this, 'contextMenu', menu, oldContextMenu);
- });
-
- if (!target.getRectForContextMenu) {
- /**
- * @return {!ClientRect} The rect to use for positioning the context
- * menu when the context menu is not opened using a mouse position.
- */
- target.getRectForContextMenu = function() {
- return this.getBoundingClientRect();
- };
- }
- }
-
- /**
- * Sets the given contextMenu to the given element. A contextMenu property
- * would be added if necessary.
- * @param {!Element} element The element or class to set the contextMenu to.
- * @param {!Menu} contextMenu The contextMenu property to be set.
- */
- setContextMenu(element, contextMenu) {
- if (!element.contextMenu) {
- this.addContextMenuProperty(element);
- }
- element.contextMenu = contextMenu;
- }
-}
-
-/**
- * The singleton context menu handler.
- * @type {!ContextMenuHandler}
- */
-export const contextMenuHandler = new ContextMenuHandler();
diff --git a/chromium/chrome/browser/resources/ntp4/cr_deprecated.js b/chromium/chrome/browser/resources/ntp4/cr_deprecated.js
deleted file mode 100644
index 08c05a43e64..00000000000
--- a/chromium/chrome/browser/resources/ntp4/cr_deprecated.js
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Leftover "cr." library functions to support some legacy
- * JavaScript users. Do not use this file in new code.
- */
-
-import {assertNotReached} from 'chrome://resources/js/assert_ts.js';
-
-/**
- * Dispatches a simple event on an event target.
- * @param {!EventTarget} target The event target to dispatch the event on.
- * @param {string} type The type of the event.
- * @param {boolean=} bubbles Whether the event bubbles or not.
- * @param {boolean=} cancelable Whether the default action of the event
- * can be prevented. Default is true.
- * @return {boolean} If any of the listeners called {@code preventDefault}
- * during the dispatch this will return false.
- */
-export function dispatchSimpleEvent(target, type, bubbles, cancelable) {
- const e = new Event(
- type,
- {bubbles: bubbles, cancelable: cancelable === undefined || cancelable});
- return target.dispatchEvent(e);
-}
-
-/**
- * Adds a {@code getInstance} static method that always return the same
- * instance object.
- * @param {!Function} ctor The constructor for the class to add the static
- * method to.
- */
-export function addSingletonGetter(ctor) {
- ctor.getInstance = function() {
- return ctor.instance_ || (ctor.instance_ = new ctor());
- };
-}
-
-/**
- * Fires a property change event on the target.
- * @param {!EventTarget} target The target to dispatch the event on.
- * @param {string} propertyName The name of the property that changed.
- * @param {*} newValue The new value for the property.
- * @param {*} oldValue The old value for the property.
- */
-export function dispatchPropertyChange(
- target, propertyName, newValue, oldValue) {
- const e = new Event(propertyName + 'Change');
- e.propertyName = propertyName;
- e.newValue = newValue;
- e.oldValue = oldValue;
- target.dispatchEvent(e);
-}
-
-/**
- * Converts a camelCase javascript property name to a hyphenated-lower-case
- * attribute name.
- * @param {string} jsName The javascript camelCase property name.
- * @return {string} The equivalent hyphenated-lower-case attribute name.
- */
-function getAttributeName(jsName) {
- return jsName.replace(/([A-Z])/g, '-$1').toLowerCase();
-}
-
-/**
- * The kind of property to define in {@code getPropertyDescriptor}.
- * @enum {string}
- * @const
- */
-export const PropertyKind = {
- /**
- * Plain old JS property where the backing data is stored as a "private"
- * field on the object.
- * Use for properties of any type. Type will not be checked.
- */
- JS: 'js',
-
- /**
- * The property backing data is stored as an attribute on an element.
- * Use only for properties of type {string}.
- */
- ATTR: 'attr',
-
- /**
- * The property backing data is stored as an attribute on an element. If the
- * element has the attribute then the value is true.
- * Use only for properties of type {boolean}.
- */
- BOOL_ATTR: 'boolAttr',
-};
-
-/**
- * Helper function for getPropertyDescriptor that returns the getter to use for
- * the property.
- * @param {string} name The name of the property.
- * @param {PropertyKind} kind The kind of the property.
- * @return {function():*} The getter for the property.
- */
-function getGetter(name, kind) {
- let attributeName;
- switch (kind) {
- case PropertyKind.JS:
- const privateName = name + '_';
- return function() {
- return this[privateName];
- };
- case PropertyKind.ATTR:
- attributeName = getAttributeName(name);
- return function() {
- return this.getAttribute(attributeName);
- };
- case PropertyKind.BOOL_ATTR:
- attributeName = getAttributeName(name);
- return function() {
- return this.hasAttribute(attributeName);
- };
- }
-
- assertNotReached();
-}
-
-/**
- * Helper function for getPropertyDescriptor that returns the setter of the
- * right kind.
- * @param {string} name The name of the property we are defining the setter
- * for.
- * @param {PropertyKind} kind The kind of property we are getting the
- * setter for.
- * @param {function(*, *):void=} setHook A function to run after the
- * property is set, but before the propertyChange event is fired.
- * @return {function(*):void} The function to use as a setter.
- */
-function getSetter(name, kind, setHook) {
- let attributeName;
- switch (kind) {
- case PropertyKind.JS:
- const privateName = name + '_';
- return function(value) {
- const oldValue = this[name];
- if (value !== oldValue) {
- this[privateName] = value;
- if (setHook) {
- setHook.call(this, value, oldValue);
- }
- dispatchPropertyChange(this, name, value, oldValue);
- }
- };
-
- case PropertyKind.ATTR:
- attributeName = getAttributeName(name);
- return function(value) {
- const oldValue = this[name];
- if (value !== oldValue) {
- if (value === undefined) {
- this.removeAttribute(attributeName);
- } else {
- this.setAttribute(attributeName, value);
- }
- if (setHook) {
- setHook.call(this, value, oldValue);
- }
- dispatchPropertyChange(this, name, value, oldValue);
- }
- };
-
- case PropertyKind.BOOL_ATTR:
- attributeName = getAttributeName(name);
- return function(value) {
- const oldValue = this[name];
- if (value !== oldValue) {
- if (value) {
- this.setAttribute(attributeName, name);
- } else {
- this.removeAttribute(attributeName);
- }
- if (setHook) {
- setHook.call(this, value, oldValue);
- }
- dispatchPropertyChange(this, name, value, oldValue);
- }
- };
- }
-
- assertNotReached();
-}
-
-/**
- * Returns a getter and setter to be used as property descriptor in
- * Object.defineProperty(). When the setter changes the value a property change
- * event with the type {@code name + 'Change'} is fired.
- * @param {string} name The name of the property.
- * @param {PropertyKind=} kind What kind of underlying storage to use.
- * @param {function(?, ?):void=} setHook A function to run after the
- * property is set, but before the propertyChange event is fired.
- */
-export function getPropertyDescriptor(name, kind = PropertyKind.JS, setHook) {
- return {
- get: getGetter(name, kind),
- set: getSetter(name, kind, setHook),
- };
-}
diff --git a/chromium/chrome/browser/resources/ntp4/dot_list.js b/chromium/chrome/browser/resources/ntp4/dot_list.js
deleted file mode 100644
index b2146f429ac..00000000000
--- a/chromium/chrome/browser/resources/ntp4/dot_list.js
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {define as crUiDefine} from './ui.js';
-import {hasKeyModifiers} from 'chrome://resources/js/util_ts.js';
-
-/**
- * @fileoverview DotList implementation
- */
-
-/**
- * Live list of the navigation dots.
- * @type {!HTMLCollection<!Element>}
- */
-let navDots;
-
-/**
- * Creates a new DotList object.
- * @constructor
- * @extends {HTMLUListElement}
- */
-export const DotList = crUiDefine('ul');
-
-DotList.prototype = {
- __proto__: HTMLUListElement.prototype,
-
- decorate() {
- this.addEventListener('keydown', this.onKeyDown_.bind(this));
- navDots = this.getElementsByClassName('dot');
- },
-
- /**
- * Live list of the navigation dots.
- * @type {!NodeList|undefined}
- */
- get dots() {
- return navDots;
- },
-
- /**
- * Handler for key events on the dot list. These keys will change the focus
- * element.
- * @param {!Event} e The KeyboardEvent.
- */
- onKeyDown_(e) {
- if (hasKeyModifiers(e)) {
- return;
- }
-
- let direction = 0;
- if (e.key === 'ArrowLeft') {
- direction = -1;
- } else if (e.key === 'ArrowRight') {
- direction = 1;
- } else {
- return;
- }
-
- const focusDot = this.querySelector('.dot:focus');
- if (!focusDot) {
- return;
- }
- const focusIndex = Array.prototype.indexOf.call(navDots, focusDot);
- let newFocusIndex = focusIndex + direction;
- if (focusIndex === newFocusIndex) {
- return;
- }
-
- newFocusIndex = (newFocusIndex + navDots.length) % navDots.length;
- navDots[newFocusIndex].tabIndex = 3;
- navDots[newFocusIndex].focus();
- focusDot.tabIndex = -1;
-
- e.stopPropagation();
- e.preventDefault();
- },
-};
diff --git a/chromium/chrome/browser/resources/ntp4/images/error_yellow900.svg b/chromium/chrome/browser/resources/ntp4/images/error_yellow900.svg
deleted file mode 100644
index 3e134e75ee9..00000000000
--- a/chromium/chrome/browser/resources/ntp4/images/error_yellow900.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="18" viewBox="0 0 18 18" width="18" fill="#E37400"><path d="M0 0h18v18H0z" fill="none"/><path d="M9 1.03c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zM10 13H8v-2h2v2zm0-3H8V5h2v5z"/></svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/ntp4/images/trash.png b/chromium/chrome/browser/resources/ntp4/images/trash.png
deleted file mode 100644
index ef1e62a1b3b..00000000000
--- a/chromium/chrome/browser/resources/ntp4/images/trash.png
+++ /dev/null
Binary files differ
diff --git a/chromium/chrome/browser/resources/ntp4/menu.js b/chromium/chrome/browser/resources/ntp4/menu.js
deleted file mode 100644
index 915b99b4a5e..00000000000
--- a/chromium/chrome/browser/resources/ntp4/menu.js
+++ /dev/null
@@ -1,407 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// NOTE: This file depends on ui.js (or the autogenerated ui.m.js module
-// version). These files and all files that depend on them are deprecated, and
-// should only be used by legacy UIs that have not yet been updated to new
-// patterns. Use Web Components in any new code.
-
-import {assert, assertInstanceof} from 'chrome://resources/js/assert_ts.js';
-
-import {getPropertyDescriptor, PropertyKind} from './cr_deprecated.js';
-import {MenuItem} from './menu_item.js';
-import {decorate, define as crUiDefine} from './ui.js';
-
-
-/**
- * Creates a new menu element. Menu dispatches all commands on the element it
- * was shown for.
- *
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {HTMLElement}
- */
-export const Menu = crUiDefine('cr-menu');
-
-Menu.prototype = {
- __proto__: HTMLElement.prototype,
-
- selectedIndex_: -1,
-
- /**
- * Element for which menu is being shown.
- */
- contextElement: null,
-
- /**
- * Initializes the menu element.
- */
- decorate() {
- this.addEventListener('mouseover', this.handleMouseOver_);
- this.addEventListener('mouseout', this.handleMouseOut_);
- this.addEventListener('mouseup', this.handleMouseUp_, true);
-
- this.classList.add('decorated');
- this.setAttribute('role', 'menu');
- this.hidden = true; // Hide the menu by default.
-
- // Decorate the children as menu items.
- const menuItems = this.menuItems;
- for (let i = 0, menuItem; menuItem = menuItems[i]; i++) {
- decorate(menuItem, MenuItem);
- }
- },
-
- /**
- * Adds menu item at the end of the list.
- * @param {Object} item Menu item properties.
- * @return {!MenuItem} The created menu item.
- */
- addMenuItem(item) {
- const menuItem = /** @type {!MenuItem} */ (
- this.ownerDocument.createElement('cr-menu-item'));
- this.appendChild(menuItem);
-
- decorate(menuItem, MenuItem);
-
- if (item.label) {
- menuItem.label = item.label;
- }
-
- if (item.iconUrl) {
- menuItem.iconUrl = item.iconUrl;
- }
-
- return menuItem;
- },
-
- /**
- * Adds separator at the end of the list.
- */
- addSeparator() {
- const separator = this.ownerDocument.createElement('hr');
- decorate(separator, MenuItem);
- this.appendChild(separator);
- },
-
- /**
- * Clears menu.
- */
- clear() {
- this.selectedItem = null;
- this.textContent = '';
- },
-
- /**
- * Walks up the ancestors of |node| until a menu item belonging to this menu
- * is found.
- * @param {Node} node The node to start searching from.
- * @return {MenuItem} The found menu item or null.
- * @private
- */
- findMenuItem_(node) {
- while (node && node.parentNode !== this && !(node instanceof MenuItem)) {
- node = node.parentNode;
- }
- if (!node) {
- return null;
- }
- assertInstanceof(node, MenuItem);
- return node;
- },
-
- /**
- * Handles mouseover events and selects the hovered item.
- * @param {Event} e The mouseover event.
- * @private
- */
- handleMouseOver_(e) {
- const overItem = this.findMenuItem_(/** @type {Element} */ (e.target));
- this.selectedItem = overItem;
- },
-
- /**
- * Handles mouseout events and deselects any selected item.
- * @param {Event} e The mouseout event.
- * @private
- */
- handleMouseOut_(e) {
- this.selectedItem = null;
- },
-
- /**
- * If there's a mouseup that happens quickly in about the same position,
- * stop it from propagating to items. This is to prevent accidentally
- * selecting a menu item that's created under the mouse cursor.
- * @param {Event} e A mouseup event on the menu (in capturing phase).
- * @private
- */
- handleMouseUp_(e) {
- assert(this.contains(/** @type {Element} */ (e.target)));
-
- if (!this.trustEvent_(e) || Date.now() - this.shown_.time > 200) {
- return;
- }
-
- const pos = this.shown_.mouseDownPos;
- if (!pos || Math.abs(pos.x - e.screenX) + Math.abs(pos.y - e.screenY) > 4) {
- return;
- }
-
- e.preventDefault();
- e.stopPropagation();
- },
-
- /**
- * @param {!Event} e
- * @return {boolean} Whether |e| can be trusted.
- * @private
- * @suppress {checkTypes}
- */
- trustEvent_(e) {
- return e.isTrusted || e.isTrustedForTesting;
- },
-
- get menuItems() {
- return this.querySelectorAll(this.menuItemSelector || '*');
- },
-
- /**
- * The selected menu item or null if none.
- * @type {MenuItem}
- */
- get selectedItem() {
- return this.menuItems[this.selectedIndex];
- },
- set selectedItem(item) {
- const index = Array.prototype.indexOf.call(this.menuItems, item);
- this.selectedIndex = index;
- },
-
- /**
- * Focuses the selected item. If selectedIndex is invalid, set it to 0
- * first.
- */
- focusSelectedItem() {
- const items = this.menuItems;
- if (this.selectedIndex < 0 || this.selectedIndex > items.length) {
- // Find first visible item to focus by default.
- for (let idx = 0; idx < items.length; idx++) {
- const item = items[idx];
- if (item.hasAttribute('hidden') || item.isSeparator()) {
- continue;
- }
- // If the item is disabled we accept it, but try to find the next
- // enabled item, but keeping the first disabled item.
- if (!item.disabled) {
- this.selectedIndex = idx;
- break;
- } else if (this.selectedIndex === -1) {
- this.selectedIndex = idx;
- }
- }
- }
-
- if (this.selectedItem) {
- this.selectedItem.focus();
- this.setAttribute('aria-activedescendant', this.selectedItem.id);
- }
- },
-
- /**
- * Menu length
- */
- get length() {
- return this.menuItems.length;
- },
-
- /**
- * Returns whether the given menu item is visible.
- * @param {!MenuItem} menuItem
- * @return {boolean}
- * @private
- */
- isItemVisible_(menuItem) {
- if (menuItem.hidden) {
- return false;
- }
- if (menuItem.offsetParent) {
- return true;
- }
- // A "position: fixed" element won't have an offsetParent, so we have to
- // do the full style computation.
- return window.getComputedStyle(menuItem).display !== 'none';
- },
-
- /**
- * Returns whether the menu has any visible items.
- * @return {boolean} True if the menu has visible item. Otherwise, false.
- */
- hasVisibleItems() {
- // Inspect items in reverse order to determine if the separator above each
- // set of items is required.
- for (const menuItem of this.menuItems) {
- if (this.isItemVisible_(menuItem)) {
- return true;
- }
- }
- return false;
- },
-
- /**
- * This is the function that handles keyboard navigation. This is usually
- * called by the element responsible for managing the menu.
- * @param {Event} e The keydown event object.
- * @return {boolean} Whether the event was handled be the menu.
- */
- handleKeyDown(e) {
- let item = this.selectedItem;
-
- const self = this;
- const selectNextAvailable = function(m) {
- const menuItems = self.menuItems;
- const len = menuItems.length;
- if (!len) {
- // Edge case when there are no items.
- return;
- }
- let i = self.selectedIndex;
- if (i === -1 && m === -1) {
- // Edge case when needed to go the last item first.
- i = 0;
- }
-
- // "i" may be negative(-1), so modulus operation and cycle below
- // wouldn't work as assumed. This trick makes startPosition positive
- // without altering it's modulo.
- const startPosition = (i + len) % len;
-
- while (true) {
- i = (i + m + len) % len;
-
- // Check not to enter into infinite loop if all items are hidden or
- // disabled.
- if (i === startPosition) {
- break;
- }
-
- item = menuItems[i];
- if (item && !item.isSeparator() && !item.disabled &&
- this.isItemVisible_(item)) {
- break;
- }
- }
- if (item && !item.disabled) {
- self.selectedIndex = i;
- }
- }.bind(this);
-
- switch (e.key) {
- case 'ArrowDown':
- selectNextAvailable(1);
- this.focusSelectedItem();
- return true;
- case 'ArrowUp':
- selectNextAvailable(-1);
- this.focusSelectedItem();
- return true;
- case 'Enter':
- case ' ':
- if (item) {
- // Store |contextElement| since it'll be removed when handling the
- // 'activate' event.
- const contextElement = this.contextElement;
- const activationEvent = document.createEvent('Event');
- activationEvent.initEvent('activate', true, true);
- activationEvent.originalEvent = e;
- if (item.dispatchEvent(activationEvent)) {
- if (item.command) {
- item.command.execute(contextElement);
- }
- }
- }
- return true;
- }
-
- return false;
- },
-
- hide() {
- this.hidden = true;
- delete this.shown_;
- },
-
- /** @param {{x: number, y: number}=} opt_mouseDownPos */
- show(opt_mouseDownPos) {
- this.shown_ = {mouseDownPos: opt_mouseDownPos, time: Date.now()};
- this.hidden = false;
- },
-
- /**
- * Updates menu items command according to context.
- * @param {Node=} node Node for which to actuate commands state.
- */
- updateCommands(node) {
- const menuItems = this.menuItems;
-
- for (const menuItem of menuItems) {
- if (!menuItem.isSeparator()) {
- menuItem.updateCommand(node);
- }
- }
-
- let separatorRequired = false;
- let lastSeparator = null;
- // Hide any separators without a visible item between them and the next
- // separator or the end of the menu.
- for (const menuItem of menuItems) {
- if (menuItem.isSeparator()) {
- if (separatorRequired) {
- lastSeparator = menuItem;
- }
- menuItem.hidden = true;
- separatorRequired = false;
- continue;
- }
- if (this.isItemVisible_(menuItem)) {
- if (lastSeparator) {
- lastSeparator.hidden = false;
- }
- separatorRequired = true;
- }
- }
- },
-};
-
-/** @suppress {globalThis} This standalone function is used like method. */
-function selectedIndexChanged(selectedIndex, oldSelectedIndex) {
- const oldSelectedItem = this.menuItems[oldSelectedIndex];
- if (oldSelectedItem) {
- oldSelectedItem.selected = false;
- oldSelectedItem.blur();
- }
- const item = this.selectedItem;
- if (item) {
- item.selected = true;
- }
-}
-
-/**
- * The selected menu item.
- * @type {number}
- */
-Menu.prototype.selectedIndex;
-Object.defineProperty(
- Menu.prototype, 'selectedIndex',
- getPropertyDescriptor(
- 'selectedIndex', PropertyKind.JS, selectedIndexChanged));
-
-/**
- * Selector for children which are menu items.
- * @type {string}
- */
-Menu.prototype.menuItemSelector;
-Object.defineProperty(
- Menu.prototype, 'menuItemSelector',
- getPropertyDescriptor('menuItemSelector', PropertyKind.ATTR));
diff --git a/chromium/chrome/browser/resources/ntp4/menu_button.js b/chromium/chrome/browser/resources/ntp4/menu_button.js
deleted file mode 100644
index 443df1b53f6..00000000000
--- a/chromium/chrome/browser/resources/ntp4/menu_button.js
+++ /dev/null
@@ -1,352 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// NOTE: This file depends on ui.js (or the autogenerated ui.m.js module
-// version). These files and all files that depend on them are deprecated, and
-// should only be used by legacy UIs that have not yet been updated to new
-// patterns. Use Web Components in any new code.
-
-import {assert} from 'chrome://resources/js/assert_ts.js';
-import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-import {isWindows} from 'chrome://resources/js/platform.js';
-
-import {Menu} from './menu.js';
-import {MenuItem} from './menu_item.js';
-import {AnchorType, positionPopupAroundElement} from './position_util.js';
-import {decorate, define as crUiDefine} from './ui.js';
-
-
-/**
- * Enum for type of hide. Delayed is used when called by clicking on a
- * checkable menu item.
- * @enum {number}
- */
-export const HideType = {
- INSTANT: 0,
- DELAYED: 1,
-};
-
-/** @const */
-
-/**
- * Creates a new menu button element.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {HTMLButtonElement}
- * @implements {EventListener}
- */
-export const MenuButton = crUiDefine('button');
-
-MenuButton.prototype = {
- __proto__: HTMLButtonElement.prototype,
-
- /**
- * Initializes the menu button.
- */
- decorate() {
- // Listen to the touch events on the document so that we can handle it
- // before cancelled by other UI components.
- this.ownerDocument.addEventListener('touchstart', this);
- this.addEventListener('mousedown', this);
- this.addEventListener('keydown', this);
- this.addEventListener('dblclick', this);
- this.addEventListener('blur', this);
-
- // Adding the 'custom-appearance' class prevents widgets.css from changing
- // the appearance of this element.
- this.classList.add('custom-appearance');
- this.classList.add('menu-button'); // For styles in menu_button.css.
-
- let menu;
- if ((menu = this.getAttribute('menu'))) {
- this.menu = menu;
- }
-
- // An event tracker for events we only connect to while the menu is
- // displayed.
- this.showingEvents_ = new EventTracker();
-
- this.anchorType = AnchorType.BELOW;
- this.invertLeftRight = false;
- },
-
- /**
- * The menu associated with the menu button.
- * @type {Menu}
- */
- get menu() {
- return this.menu_;
- },
- set menu(menu) {
- if (typeof menu === 'string' && menu[0] === '#') {
- menu = this.ownerDocument.getElementById(menu.slice(1));
- assert(menu);
- decorate(menu, Menu);
- }
-
- this.menu_ = menu;
- if (menu) {
- if (menu.id) {
- this.setAttribute('menu', '#' + menu.id);
- }
- }
- },
-
- /**
- * Whether to show the menu on press of the Up or Down arrow keys.
- */
- respondToArrowKeys: true,
-
- /**
- * Checks if the menu should be closed based on the target of a mouse click
- * or a touch event target.
- * @param {Event} e The event object.
- * @return {boolean}
- * @private
- */
- shouldDismissMenu_(e) {
- // The menu is dismissed when clicking outside the menu.
- // The button is excluded here because it should toggle show/hide the
- // menu and handled separately.
- return e.target instanceof Node && !this.contains(e.target) &&
- !this.menu.contains(e.target);
- },
-
- /**
- * Handles event callbacks.
- * @param {Event} e The event object.
- */
- handleEvent(e) {
- if (!this.menu) {
- return;
- }
-
- switch (e.type) {
- case 'touchstart':
- // Touch on the menu button itself is ignored to avoid that the menu
- // opened again by the mousedown event following the touch events.
- if (this.shouldDismissMenu_(e)) {
- this.hideMenuWithoutTakingFocus_();
- }
- break;
- case 'mousedown':
- if (e.currentTarget === this.ownerDocument) {
- if (this.shouldDismissMenu_(e)) {
- this.hideMenuWithoutTakingFocus_();
- } else {
- e.preventDefault();
- }
- } else {
- if (this.isMenuShown()) {
- this.hideMenuWithoutTakingFocus_();
- } else if (e.button === 0) { // Only show the menu when using left
- // mouse button.
- this.showMenu(false, {x: e.screenX, y: e.screenY});
-
- // Prevent the button from stealing focus on mousedown.
- e.preventDefault();
- }
- }
-
- // Hide the focus ring on mouse click.
- this.classList.add('using-mouse');
- break;
- case 'keydown':
- this.handleKeyDown(e);
- // If the menu is visible we let it handle all the keyboard events.
- if (this.isMenuShown() && e.currentTarget === this.ownerDocument) {
- this.menu.handleKeyDown(e);
- e.preventDefault();
- e.stopPropagation();
- }
-
- // Show the focus ring on keypress.
- this.classList.remove('using-mouse');
- break;
- case 'focus':
- if (this.shouldDismissMenu_(e)) {
- this.hideMenu();
- // Show the focus ring on focus - if it's come from a mouse event,
- // the focus ring will be hidden in the mousedown event handler,
- // executed after this.
- this.classList.remove('using-mouse');
- }
- break;
- case 'blur':
- // No need to hide the focus ring anymore, without having focus.
- this.classList.remove('using-mouse');
- break;
- case 'activate':
- const hideDelayed = e.target instanceof MenuItem && e.target.checkable;
- const hideType = hideDelayed ? HideType.DELAYED : HideType.INSTANT;
- if (e.originalEvent instanceof MouseEvent ||
- e.originalEvent instanceof TouchEvent) {
- this.hideMenuWithoutTakingFocus_(hideType);
- } else {
- // Keyboard. Take focus to continue keyboard operation.
- this.hideMenu(hideType);
- }
- break;
- case 'scroll':
- if (!(e.target === this.menu || this.menu.contains(e.target))) {
- this.hideMenu();
- }
- break;
- case 'popstate':
- case 'resize':
- this.hideMenu();
- break;
- case 'contextmenu':
- if ((!this.menu || !this.menu.contains(e.target)) &&
- (!this.hideTimestamp_ || Date.now() - this.hideTimestamp_ > 50)) {
- this.showMenu(true, {x: e.screenX, y: e.screenY});
- }
- e.preventDefault();
- // Don't allow elements further up in the DOM to show their menus.
- e.stopPropagation();
- break;
- case 'dblclick':
- // Don't allow double click events to propagate.
- e.preventDefault();
- e.stopPropagation();
- break;
- }
- },
-
- /**
- * Shows the menu.
- * @param {boolean} shouldSetFocus Whether to set focus on the
- * selected menu item.
- * @param {{x: number, y: number}=} opt_mousePos The position of the mouse
- * when shown (in screen coordinates).
- */
- showMenu(shouldSetFocus, opt_mousePos) {
- this.hideMenu();
-
- this.menu.updateCommands(this);
-
- const event = new UIEvent(
- 'menushow', {bubbles: true, cancelable: true, view: window});
- if (!this.dispatchEvent(event)) {
- return;
- }
-
- this.menu.show(opt_mousePos);
-
- this.setAttribute('menu-shown', '');
-
- // When the menu is shown we steal all keyboard events.
- const doc = this.ownerDocument;
- const win = doc.defaultView;
- this.showingEvents_.add(doc, 'keydown', this, true);
- this.showingEvents_.add(doc, 'mousedown', this, true);
- this.showingEvents_.add(doc, 'focus', this, true);
- this.showingEvents_.add(doc, 'scroll', this, true);
- this.showingEvents_.add(win, 'popstate', this);
- this.showingEvents_.add(win, 'resize', this);
- this.showingEvents_.add(this.menu, 'contextmenu', this);
- this.showingEvents_.add(this.menu, 'activate', this);
- this.positionMenu_();
-
- if (shouldSetFocus) {
- this.menu.focusSelectedItem();
- }
- },
-
- /**
- * Hides the menu. If your menu can go out of scope, make sure to call this
- * first.
- * @param {HideType=} opt_hideType Type of hide.
- * default: HideType.INSTANT.
- */
- hideMenu(opt_hideType) {
- this.hideMenuInternal_(true, opt_hideType);
- },
-
- /**
- * Hides the menu. If your menu can go out of scope, make sure to call this
- * first.
- * @param {HideType=} opt_hideType Type of hide.
- * default: HideType.INSTANT.
- */
- hideMenuWithoutTakingFocus_(opt_hideType) {
- this.hideMenuInternal_(false, opt_hideType);
- },
-
- /**
- * Hides the menu. If your menu can go out of scope, make sure to call this
- * first.
- * @param {boolean} shouldTakeFocus Moves the focus to the button if true.
- * @param {HideType=} opt_hideType Type of hide.
- * default: HideType.INSTANT.
- */
- hideMenuInternal_(shouldTakeFocus, opt_hideType) {
- if (!this.isMenuShown()) {
- return;
- }
-
- this.removeAttribute('menu-shown');
- if (opt_hideType === HideType.DELAYED) {
- this.menu.classList.add('hide-delayed');
- } else {
- this.menu.classList.remove('hide-delayed');
- }
- this.menu.hide();
-
- this.showingEvents_.removeAll();
- if (shouldTakeFocus) {
- this.focus();
- }
-
- const event = new UIEvent(
- 'menuhide', {bubbles: true, cancelable: false, view: window});
- this.dispatchEvent(event);
-
- // On windows we might hide the menu in a right mouse button up and if
- // that is the case we wait some short period before we allow the menu
- // to be shown again.
- this.hideTimestamp_ = isWindows ? Date.now() : 0;
- },
-
- /**
- * Whether the menu is shown.
- */
- isMenuShown() {
- return this.hasAttribute('menu-shown');
- },
-
- /**
- * Positions the menu below the menu button. At this point we do not use any
- * advanced positioning logic to ensure the menu fits in the viewport.
- * @private
- */
- positionMenu_() {
- positionPopupAroundElement(
- this, this.menu, this.anchorType, this.invertLeftRight);
- },
-
- /**
- * Handles the keydown event for the menu button.
- */
- handleKeyDown(e) {
- switch (e.key) {
- case 'ArrowDown':
- case 'ArrowUp':
- if (!this.respondToArrowKeys) {
- break;
- }
- case 'Enter':
- case ' ':
- if (!this.isMenuShown()) {
- this.showMenu(true);
- }
- e.preventDefault();
- break;
- case 'Escape':
- case 'Tab':
- this.hideMenu();
- break;
- }
- },
-};
diff --git a/chromium/chrome/browser/resources/ntp4/menu_item.js b/chromium/chrome/browser/resources/ntp4/menu_item.js
deleted file mode 100644
index c1a2ef6ad58..00000000000
--- a/chromium/chrome/browser/resources/ntp4/menu_item.js
+++ /dev/null
@@ -1,309 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// NOTE: This file depends on ui.js (or the autogenerated ui.m.js module
-// version). These files and all files that depend on them are deprecated, and
-// should only be used by legacy UIs that have not yet been updated to new
-// patterns. Use Web Components in any new code.
-
-// clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {assert} from 'chrome://resources/js/assert_ts.js';
-
-import {Command} from './command.js';
-import {getPropertyDescriptor, PropertyKind} from './cr_deprecated.js';
-import {define as crUiDefine, decorate, swallowDoubleClick} from './ui.js';
-// clang-format on
-
-
-/**
- * Creates a new menu item element.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {HTMLElement}
- * @implements {EventListener}
- */
-export const MenuItem = crUiDefine('cr-menu-item');
-
-/**
- * Creates a new menu separator element.
- * @return {!MenuItem} The new separator element.
- */
-MenuItem.createSeparator = function() {
- const el = /** @type {!MenuItem} */ (document.createElement('hr'));
- if (MenuItem.decorate) {
- MenuItem.decorate(el);
- }
- return el;
-};
-
-MenuItem.prototype = {
- __proto__: HTMLElement.prototype,
-
- /**
- * Initializes the menu item.
- */
- decorate() {
- let commandId;
- if ((commandId = this.getAttribute('command'))) {
- this.command = commandId;
- }
-
- this.addEventListener('mouseup', this.handleMouseUp_);
-
- // Adding the 'custom-appearance' class prevents widgets.css from changing
- // the appearance of this element.
- this.classList.add('custom-appearance');
-
- // Enable Text to Speech on the menu. Additionally, ID has to be set, since
- // it is used in element's aria-activedescendant attribute.
- if (!this.isSeparator()) {
- this.setAttribute('role', 'menuitem');
- this.setAttribute('tabindex', this.getAttribute('tabindex') || -1);
- }
-
- let iconUrl;
- if ((iconUrl = this.getAttribute('icon'))) {
- this.iconUrl = iconUrl;
- }
- },
-
- /**
- * The command associated with this menu item. If this is set to a string
- * of the form "#element-id" then the element is looked up in the document
- * of the command.
- * @type {Command}
- */
- command_: null,
- get command() {
- return this.command_;
- },
- set command(command) {
- if (this.command_) {
- this.command_.removeEventListener('labelChange', this);
- this.command_.removeEventListener('disabledChange', this);
- this.command_.removeEventListener('hiddenChange', this);
- this.command_.removeEventListener('checkedChange', this);
- }
-
- if (typeof command === 'string' && command[0] === '#') {
- command = this.ownerDocument.body.querySelector(command);
- assert(command);
- decorate(command, Command);
- }
-
- this.command_ = command;
- if (command) {
- if (command.id) {
- this.setAttribute('command', '#' + command.id);
- }
-
- if (typeof command.label === 'string') {
- this.label = command.label;
- }
- this.disabled = command.disabled;
- this.hidden = command.hidden;
- this.checked = command.checked;
-
- this.command_.addEventListener('labelChange', this);
- this.command_.addEventListener('disabledChange', this);
- this.command_.addEventListener('hiddenChange', this);
- this.command_.addEventListener('checkedChange', this);
- }
-
- this.updateShortcut_();
- },
-
- /**
- * The text label.
- * @type {string}
- */
- get label() {
- return this.textContent;
- },
- set label(label) {
- this.textContent = label;
- },
-
- /**
- * Menu icon.
- * @type {string}
- */
- get iconUrl() {
- return this.style.backgroundImage;
- },
- set iconUrl(url) {
- this.style.backgroundImage = 'url(' + url + ')';
- },
-
- /**
- * @return {boolean} Whether the menu item is a separator.
- */
- isSeparator() {
- return this.tagName === 'HR';
- },
-
- /**
- * Updates shortcut text according to associated command. If command has
- * multiple shortcuts, only first one is displayed.
- */
- updateShortcut_() {
- this.removeAttribute('shortcutText');
-
- if (!this.command_ || !this.command_.shortcut ||
- this.command_.hideShortcutText) {
- return;
- }
-
- const shortcuts = this.command_.shortcut.split(/\s+/);
-
- if (shortcuts.length === 0) {
- return;
- }
-
- const shortcut = shortcuts[0];
- const mods = {};
- let ident = '';
- shortcut.split('|').forEach(function(part) {
- const partUc = part.toUpperCase();
- switch (partUc) {
- case 'CTRL':
- case 'ALT':
- case 'SHIFT':
- case 'META':
- mods[partUc] = true;
- break;
- default:
- console.assert(!ident, 'Shortcut has two non-modifier keys');
- ident = part;
- }
- });
-
- let shortcutText = '';
-
- ['CTRL', 'ALT', 'SHIFT', 'META'].forEach(function(mod) {
- if (mods[mod]) {
- shortcutText += loadTimeData.getString('SHORTCUT_' + mod) + '+';
- }
- });
-
- if (ident === ' ') {
- ident = 'Space';
- }
-
- if (ident.length !== 1) {
- shortcutText += loadTimeData.getString('SHORTCUT_' + ident.toUpperCase());
- } else {
- shortcutText += ident.toUpperCase();
- }
-
- this.setAttribute('shortcutText', shortcutText);
- },
-
- /**
- * Handles mouseup events. This dispatches an activate event; if there is an
- * associated command, that command is executed.
- * @param {!Event} e The mouseup event object.
- * @private
- */
- handleMouseUp_(e) {
- e = /** @type {!MouseEvent} */ (e);
- // Only dispatch an activate event for left or middle click.
- if (e.button > 1) {
- return;
- }
-
- if (!this.disabled && !this.isSeparator() && this.selected) {
- // Store |contextElement| since it'll be removed by {Menu} on handling
- // 'activate' event.
- const contextElement =
- /** @type {{contextElement: Element}} */ (this.parentNode)
- .contextElement;
- const activationEvent = document.createEvent('Event');
- activationEvent.initEvent('activate', true, true);
- activationEvent.originalEvent = e;
- // Dispatch command event followed by executing the command object.
- if (this.dispatchEvent(activationEvent)) {
- const command = this.command;
- if (command) {
- command.execute(contextElement);
- swallowDoubleClick(e);
- }
- }
- }
- },
-
- /**
- * Updates command according to the node on which this menu was invoked.
- * @param {Node=} opt_node Node on which menu was opened.
- */
- updateCommand(opt_node) {
- if (this.command_) {
- this.command_.canExecuteChange(opt_node);
- }
- },
-
- /**
- * Handles changes to the associated command.
- * @param {Event} e The event object.
- */
- handleEvent(e) {
- switch (e.type) {
- case 'disabledChange':
- this.disabled = this.command.disabled;
- break;
- case 'hiddenChange':
- this.hidden = this.command.hidden;
- break;
- case 'labelChange':
- this.label = this.command.label;
- break;
- case 'checkedChange':
- this.checked = this.command.checked;
- break;
- }
- },
-};
-/**
- * Whether the menu item is disabled or not.
- * @type {boolean}
- */
-MenuItem.prototype.disabled;
-Object.defineProperty(
- MenuItem.prototype, 'disabled',
- getPropertyDescriptor('disabled', PropertyKind.BOOL_ATTR));
-
-/**
- * Whether the menu item is hidden or not.
- */
-Object.defineProperty(
- MenuItem.prototype, 'hidden',
- getPropertyDescriptor('hidden', PropertyKind.BOOL_ATTR));
-
-/**
- * Whether the menu item is selected or not.
- * @type {boolean}
- */
-MenuItem.prototype.selected;
-Object.defineProperty(
- MenuItem.prototype, 'selected',
- getPropertyDescriptor('selected', PropertyKind.BOOL_ATTR));
-
-/**
- * Whether the menu item is checked or not.
- * @type {boolean}
- */
-MenuItem.prototype.checked;
-Object.defineProperty(
- MenuItem.prototype, 'checked',
- getPropertyDescriptor('checked', PropertyKind.BOOL_ATTR));
-
-/**
- * Whether the menu item is checkable or not.
- * @type {boolean}
- */
-MenuItem.prototype.checkable;
-Object.defineProperty(
- MenuItem.prototype, 'checkable',
- getPropertyDescriptor('checkable', PropertyKind.BOOL_ATTR));
diff --git a/chromium/chrome/browser/resources/ntp4/nav_dot.css b/chromium/chrome/browser/resources/ntp4/nav_dot.css
deleted file mode 100644
index b011d84f962..00000000000
--- a/chromium/chrome/browser/resources/ntp4/nav_dot.css
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Copyright 2012 The Chromium Authors
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-/* TODO(estade): handle overflow better? I tried overflow-x: hidden and
- * overflow-y: visible (for the new dot animation), but this makes a scroll
- * bar appear */
-#dot-list {
- /* Expand to take up all available horizontal space. */
- -webkit-box-flex: 1;
- /* Center child dots. */
- -webkit-box-pack: center;
- display: -webkit-flex;
- height: 100%;
- list-style-type: none;
- margin: 0;
- padding: 0;
-}
-
-html.starting-up #dot-list {
- display: none;
-}
-
-.dot {
- box-sizing: border-box;
- cursor: pointer;
- /* max-width: Set in new_tab.js. See measureNavDots() */
- margin-inline-end: 10px;
- outline: none;
- padding-inline-start: 2px;
- text-align: left;
- transition: margin-inline-end 250ms, max-width 250ms, opacity 250ms;
-}
-
-.dot:last-child {
- margin-inline-end: 0;
-}
-
-.dot:only-of-type {
- cursor: default;
- opacity: 0;
- pointer-events: none;
-}
-
-.dot.small {
- margin-inline-end: 0;
- max-width: 0;
-}
-
-.dot .selection-bar {
- border-bottom: 5px solid;
- border-color: rgba(0, 0, 0, 0.1);
- height: 10px;
- transition: border-color 200ms;
-}
-
-.dot input {
- -webkit-appearance: none;
- background-color: transparent;
- border: none;
- cursor: inherit;
- font: inherit;
- height: auto;
- margin-inline-start: 2px;
- margin-top: 2px;
- padding: 2px 1px;
- transition: color 200ms;
- width: 90%;
-}
-
-.dot input:focus {
- cursor: auto;
-}
-
-/* Everything below here should be themed but we don't have appropriate colors
- * yet. */
-.dot input {
- color: #b2b2b2;
-}
-
-.dot:focus input,
-.dot:hover input,
-.dot.selected input {
- color: #7f7f7f;
-}
-
-.dot:focus .selection-bar,
-.dot:hover .selection-bar,
-.dot.drag-target .selection-bar {
- border-color: #b2b2b2;
-}
-
-.dot.selected .selection-bar {
- border-color: #7f7f7f;
-}
diff --git a/chromium/chrome/browser/resources/ntp4/nav_dot.js b/chromium/chrome/browser/resources/ntp4/nav_dot.js
deleted file mode 100644
index 2d2cff76bfe..00000000000
--- a/chromium/chrome/browser/resources/ntp4/nav_dot.js
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {DragWrapper} from 'chrome://resources/js/drag_wrapper.js';
-
-import {getCardSlider, saveAppPageName} from './new_tab.js';
-import {getCurrentlyDraggingTile, setCurrentDropEffect, TilePage} from './tile_page.js';
-
-/**
- * @fileoverview Nav dot
- * This is the class for the navigation controls that appear along the bottom
- * of the NTP.
- */
-
-
-/**
- * Creates a new navigation dot.
- * @param {TilePage} page The associated TilePage.
- * @param {string} title The title of the navigation dot.
- * @param {boolean} titleIsEditable If true, the title can be changed.
- * @param {boolean} animate If true, animates into existence.
- * @constructor
- * @extends {HTMLLIElement}
- * @implements {DragWrapperDelegate}
- */
-export function NavDot(page, title, titleIsEditable, animate) {
- const dot = /** @type {!NavDot} */ (document.createElement('li'));
- dot.__proto__ = NavDot.prototype;
- dot.initialize(page, title, titleIsEditable, animate);
-
- return dot;
-}
-
-NavDot.prototype = {
- __proto__: HTMLLIElement.prototype,
-
- initialize(page, title, titleIsEditable, animate) {
- this.className = 'dot';
- this.setAttribute('role', 'button');
-
- this.page_ = page;
-
- const selectionBar = this.ownerDocument.createElement('div');
- selectionBar.className = 'selection-bar';
- this.appendChild(selectionBar);
-
- // TODO(estade): should there be some limit to the number of characters?
- this.input_ = this.ownerDocument.createElement('input');
- this.input_.setAttribute('spellcheck', false);
- this.input_.value = title;
- // Take the input out of the tab-traversal focus order.
- this.input_.disabled = true;
- this.appendChild(this.input_);
-
- this.displayTitle = title;
- this.titleIsEditable_ = titleIsEditable;
-
- this.addEventListener('keydown', this.onKeyDown_);
- this.addEventListener('click', this.onClick_);
- this.addEventListener('dblclick', this.onDoubleClick_);
- this.dragWrapper_ = new DragWrapper(this, this);
- this.addEventListener('transitionend', this.onTransitionEnd_);
-
- this.input_.addEventListener('blur', this.onInputBlur_.bind(this));
- this.input_.addEventListener(
- 'mousedown', this.onInputMouseDown_.bind(this));
- this.input_.addEventListener('keydown', this.onInputKeyDown_.bind(this));
-
- if (animate) {
- this.classList.add('small');
- const self = this;
- window.setTimeout(function() {
- self.classList.remove('small');
- }, 0);
- }
- },
-
- /**
- * @return {TilePage} The associated TilePage.
- */
- get page() {
- return this.page_;
- },
-
- /**
- * Sets/gets the display title.
- * @type {string} title The display name for this nav dot.
- */
- get displayTitle() {
- return this.title;
- },
- set displayTitle(title) {
- this.title = this.input_.value = title;
- },
-
- /**
- * Removes the dot from the page. If |opt_animate| is truthy, we first
- * transition the element to 0 width.
- * @param {boolean=} opt_animate Whether to animate the removal or not.
- */
- remove(opt_animate) {
- if (opt_animate) {
- this.classList.add('small');
- } else {
- this.parentNode.removeChild(this);
- }
- },
-
- /**
- * Navigates the card slider to the page for this dot.
- */
- switchToPage() {
- getCardSlider().selectCardByValue(this.page_, true);
- },
-
- /**
- * Handler for keydown event on the dot.
- * @param {Event} e The KeyboardEvent.
- */
- onKeyDown_(e) {
- if (e.key === 'Enter') {
- this.onClick_(e);
- e.stopPropagation();
- }
- },
-
- /**
- * Clicking causes the associated page to show.
- * @param {Event} e The click event.
- * @private
- */
- onClick_(e) {
- this.switchToPage();
- // The explicit focus call is necessary because of overriding the default
- // handling in onInputMouseDown_.
- if (this.ownerDocument.activeElement !== this.input_) {
- this.focus();
- }
-
- e.stopPropagation();
- },
-
- /**
- * Double clicks allow the user to edit the page title.
- * @param {Event} e The click event.
- * @private
- */
- onDoubleClick_(e) {
- if (this.titleIsEditable_) {
- this.input_.disabled = false;
- this.input_.focus();
- this.input_.select();
- }
- },
-
- /**
- * Prevent mouse down on the input from selecting it.
- * @param {Event} e The click event.
- * @private
- */
- onInputMouseDown_(e) {
- if (this.ownerDocument.activeElement !== this.input_) {
- e.preventDefault();
- }
- },
-
- /**
- * Handle keypresses on the input.
- * @param {Event} e The click event.
- * @private
- */
- onInputKeyDown_(e) {
- switch (e.key) {
- case 'Escape': // Escape cancels edits.
- this.input_.value = this.displayTitle;
- case 'Enter': // Fall through.
- this.input_.blur();
- break;
- }
- },
-
- /**
- * When the input blurs, commit the edited changes.
- * @param {Event} e The blur event.
- * @private
- */
- onInputBlur_(e) {
- window.getSelection().removeAllRanges();
- this.displayTitle = this.input_.value;
- saveAppPageName(this.page_, this.displayTitle);
- this.input_.disabled = true;
- },
-
- shouldAcceptDrag(e) {
- return this.page_.shouldAcceptDrag(e);
- },
-
- /** @override */
- doDragEnter(e) {
- const self = this;
- function navPageClearTimeout() {
- self.switchToPage();
- self.dragNavTimeout = null;
- }
- this.dragNavTimeout = window.setTimeout(navPageClearTimeout, 500);
-
- this.doDragOver(e);
- },
-
- /** @override */
- doDragOver(e) {
- // Prevent default handling so the <input> won't act as a drag target.
- e.preventDefault();
-
- if (!this.dragWrapper_.isCurrentDragTarget) {
- setCurrentDropEffect(e.dataTransfer, 'none');
- } else {
- this.page_.setDropEffect(e.dataTransfer);
- }
- },
-
- /** @override */
- doDrop(e) {
- e.stopPropagation();
- const tile = getCurrentlyDraggingTile();
- if (tile && tile.tilePage !== this.page_) {
- this.page_.appendDraggingTile();
- }
- // TODO(estade): handle non-tile drags.
-
- this.cancelDelayedSwitch_();
- },
-
- /** @override */
- doDragLeave(e) {
- this.cancelDelayedSwitch_();
- },
-
- /**
- * Cancels the timer for page switching.
- * @private
- */
- cancelDelayedSwitch_() {
- if (this.dragNavTimeout) {
- window.clearTimeout(this.dragNavTimeout);
- this.dragNavTimeout = null;
- }
- },
-
- /**
- * A transition has ended.
- * @param {Event} e The transition end event.
- * @private
- */
- onTransitionEnd_(e) {
- if (e.propertyName === 'max-width' && this.classList.contains('small')) {
- this.parentNode.removeChild(this);
- }
- },
-};
diff --git a/chromium/chrome/browser/resources/ntp4/new_tab.css b/chromium/chrome/browser/resources/ntp4/new_tab.css
deleted file mode 100644
index 1be787c7f4b..00000000000
--- a/chromium/chrome/browser/resources/ntp4/new_tab.css
+++ /dev/null
@@ -1,275 +0,0 @@
-/* Copyright 2012 The Chromium Authors
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-html {
- /* It's necessary to put this here instead of in body in order to get the
- background-size of 100% to work properly */
- height: 100%;
- overflow: hidden;
-}
-
-body {
- /* Don't highlight links when they're tapped. Safari has bugs here that
- show up as flicker when dragging in some situations */
- -webkit-tap-highlight-color: transparent;
- background-size: auto 100%;
- margin: 0;
- /* Don't allow selecting text - can occur when dragging */
- user-select: none;
-}
-
-/* [hidden] does display:none, but its priority is too low in some cases. */
-[hidden] {
- display: none !important;
-}
-
-[is='action-link'] {
- margin-inline-start: 0.5em;
-}
-
-#card-slider-frame {
- /* Must match #footer height. */
- bottom: 50px;
- overflow: hidden;
- /* We want this to fill the window except for the region used
- * by footer. */
- position: fixed;
- top: 0;
- width: 100%;
-}
-
-#page-list {
- /* fill the apps-frame */
- display: -webkit-box;
- height: 100%;
-}
-
-#deprecated-apps-link-container {
- align-items: center;
- display: inline-flex;
- margin-top: 10px;
- padding-inline-end: 20px;
- position: fixed;
- right: 0;
-}
-
-html[dir='rtl'] #deprecated-apps-link-container {
- left: 0;
- right: auto;
-}
-
-#deprecated-apps-link-container img {
- height: 16px;
- width: 16px;
-}
-
-#attribution {
- bottom: 0;
- margin-left: 8px; /* csschecker-disable-line left-right */
- /* Leave room for the scrollbar. */
- margin-right: 13px; /* csschecker-disable-line left-right */
- position: absolute;
- z-index: -5;
-}
-
-html[dir='rtl'] #attribution {
- left: 0;
- right: auto;
- text-align: right; /* csschecker-disable-line left-right */
-}
-
-#attribution > span {
- display: block;
-}
-
-#footer {
- background-color: white;
- bottom: 0;
- color: #666;
- font-size: 0.9em;
- font-weight: bold;
- overflow: hidden;
- position: fixed;
- width: 100%;
- z-index: 5;
-}
-
-/* TODO(estade): remove this border hack and replace with a webkit-gradient
- * border-image on #footer once WebKit supports border-image-slice.
- * See https://bugs.webkit.org/show_bug.cgi?id=20127 */
-#footer-border {
- height: 1px;
-}
-
-#footer-content {
- -webkit-align-items: center;
- -webkit-justify-content: space-between;
- display: -webkit-flex;
- height: 49px;
-}
-
-#footer-content > * {
- margin: 0 9px;
-}
-
-#logo-img {
- display: inline-block;
- margin-top: 4px;
- overflow: hidden;
- position: relative;
-}
-
-.starting-up * {
- transition: none !important;
-}
-
-/* Trash. *********************************************************************/
-
-#trash {
- color: #222;
- height: 100%;
- opacity: 0;
- padding-inline-start: 10px;
- position: absolute;
- right: 0;
- top: 50px;
- transition: top 200ms, opacity 0ms;
- transition-delay: 0ms, 200ms;
- width: auto;
-}
-
-html[dir='rtl'] #trash {
- left: 0;
- right: auto;
-}
-
-#footer.showing-trash-mode #trash {
- opacity: 0.75;
- top: 0;
- transition-delay: 0ms, 0ms;
- transition-duration: 0ms, 200ms;
-}
-
-#footer.showing-trash-mode #trash.drag-target {
- opacity: 1;
-}
-
-#trash > .trash-text {
- border: 1px dashed #7f7f7f;
- border-radius: 4px;
- display: inline-block;
- padding-bottom: 9px;
- padding-inline-end: 7px;
- padding-inline-start: 30px;
- padding-top: 10px;
- position: relative;
- top: 7px;
-}
-
-#trash > .lid,
-#trash > .can {
- left: 18px;
- top: 18px;
-}
-
-html[dir='rtl'] #trash > .lid,
-html[dir='rtl'] #trash > .can {
- right: 18px;
-}
-
-#footer.showing-trash-mode #trash.drag-target .lid {
- transform: rotate(-45deg);
-}
-
-html[dir='rtl'] #footer.showing-trash-mode #trash.drag-target .lid {
- transform: rotate(45deg);
-}
-
-#fontMeasuringDiv {
- /* The font attributes match the nav inputs. */
- font-size: 0.9em;
- font-weight: bold;
- pointer-events: none;
- position: absolute;
- visibility: hidden;
-}
-
-/* Page switcher buttons. *****************************************************/
-
-.page-switcher {
- background-color: transparent;
- border: none;
- bottom: 0;
- font-size: 40px;
- margin: 0;
- max-width: 150px;
- min-width: 90px;
- outline: none;
- padding: 0;
- position: absolute;
- top: 0;
- transition: width 150ms, right 150ms, background-color 150ms;
- z-index: 5;
-}
-
-/* Footer buttons. ************************************************************/
-
-#chrome-web-store-link {
- -webkit-order: 3;
- color: rgb(26, 115, 232);
- cursor: pointer;
- display: inline-block;
- margin: 0;
- padding-inline-end: 12px;
- text-decoration: none;
- transition-delay: 100ms;
- white-space: nowrap;
-}
-
-#chrome-web-store-title {
- background: url(chrome://theme/IDR_WEBSTORE_ICON_24) right 50% no-repeat;
- display: inline-block;
- line-height: 49px;
- padding-inline-end: 36px;
- padding-inline-start: 15px;
-}
-
-html[dir='rtl'] #chrome-web-store-title {
- background-position-x: left;
-}
-
-/* In trash mode, hide the menus and web store link. */
-#footer.showing-trash-mode .menu-container {
- opacity: 0;
- transition-delay: 0ms;
- visibility: hidden;
-}
-
-#footer .menu-container {
- -webkit-align-items: center;
- -webkit-flex-direction: row;
- -webkit-justify-content: flex-end;
- /* Put menus in a box so the order can easily be swapped. */
- display: -webkit-flex;
- height: 100%;
- margin: 0;
- min-width: -webkit-min-content;
-}
-
-#other-sessions-menu-button {
- -webkit-order: 0;
-}
-
-.other-sessions-promo-message {
- display: none;
- padding: 0;
-}
-
-.other-sessions-promo-message:only-child {
- display: block;
-}
-
-.other-sessions-promo-message p {
- margin: 0;
-}
diff --git a/chromium/chrome/browser/resources/ntp4/new_tab.html b/chromium/chrome/browser/resources/ntp4/new_tab.html
deleted file mode 100644
index bbd51e6a186..00000000000
--- a/chromium/chrome/browser/resources/ntp4/new_tab.html
+++ /dev/null
@@ -1,109 +0,0 @@
-<!doctype html>
-<html class="starting-up" dir="$i18n{textdirection}" lang="$i18n{language}">
-<head>
-<meta charset="utf-8">
-<title>$i18n{title}</title>
-<!-- Don't scale the viewport in either portrait or landscape mode.
- Note that this means apps will be reflowed when rotated (like iPad).
- If we wanted to maintain position we could remove 'maximum-scale' so
- that we'd zoom out in portrait mode, but then there would be a bunch
- of unusable space at the bottom.
--->
-<meta name="viewport"
- content="user-scalable=no, width=device-width, maximum-scale=1.0">
-
-<link id="themecss" rel="stylesheet">
-<script>
-// Until themes can clear the cache, force-reload the theme stylesheet.
-document.getElementById('themecss').href = 'chrome://theme/css/new_tab_theme.css?' + Date.now();
-</script>
-<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
-<link rel="stylesheet" href="chrome://resources/css/menu.css">
-<link rel="stylesheet" href="chrome://resources/css/menu_button.css">
-<link rel="stylesheet" href="chrome://resources/css/widgets.css">
-<link rel="stylesheet" href="apps_page.css">
-<link rel="stylesheet" href="nav_dot.css">
-<link rel="stylesheet" href="new_tab.css">
-<link rel="stylesheet" href="tile_page.css">
-<link rel="stylesheet" href="trash.css">
-
-<script type="module" src="new_tab.js"></script>
-</head>
-
-<body>
- <div id="card-slider-frame">
- <button id="page-switcher-start" class="page-switcher custom-appearance"
- tabindex="2" hidden>‹
- </button>
- <div id="page-list"></div>
- <button id="page-switcher-end" class="page-switcher custom-appearance"
- tabindex="2" hidden>›
- </button>
- <div id="attribution">
- <span>$i18n{attributionintro}</span>
- <img id="attribution-img">
- </div>
- </div>
-
- <div id="deprecated-apps-link-container" hidden>
- <img src="images/error_yellow900.svg">
- <a is="action-link" id="deprecated-apps-link" href="#"></a>
- </div>
-
- <div id="footer">
- <div id="footer-border"></div>
- <div id="footer-content">
- <div id="logo-img">
- <img alt="" src="chrome://theme/IDR_PRODUCT_LOGO_32">
- </div>
-
- <ul id="dot-list">
- </ul>
-
- <div id="footer-menu-container" class="menu-container">
- <a id="chrome-web-store-link">
- <span id="chrome-web-store-title">$i18n{webStoreTitleShort}</span>
- </a>
- </div>
-
- <div id="trash" class="trash">
- <span class="lid"></span>
- <span class="can"></span>
- <span class="trash-text">$i18n{appuninstall}</span>
- </div>
- </div>
- </div>
-</body>
-
-<!-- A div to hold all the templates, and in the darkness bind them. -->
-<div hidden>
-
-<!-- App Contents w/ Large Icon -->
-<div id="app-large-icon-template" class="app-contents">
- <div class="app-img-container" aria-hidden="true">
- <img class="invisible" alt="">
- </div>
- <div class="app-title-container">
- <span class="title"></span>
- </div>
-</div>
-
-<!-- App Contents w/ Small Icon -->
-<div id="app-small-icon-template" class="app-contents">
- <div class="app-icon-div" aria-hidden="true">
- <div class="app-img-container">
- <img class="invisible" alt="">
- </div>
- <div class="color-stripe"></div>
- </div>
- <div class="app-title-container">
- <span class="title"></span>
- </div>
-</div>
-
-</div>
-
-<!-- This is used to measure text in the current locale. It is not visible. -->
-<div id="fontMeasuringDiv"></div>
-
-</html>
diff --git a/chromium/chrome/browser/resources/ntp4/new_tab.js b/chromium/chrome/browser/resources/ntp4/new_tab.js
deleted file mode 100644
index 20faaf59ae6..00000000000
--- a/chromium/chrome/browser/resources/ntp4/new_tab.js
+++ /dev/null
@@ -1,372 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import './strings.m.js';
-
-import {assert} from 'chrome://resources/js/assert_ts.js';
-import {addWebUiListener} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {$, appendParam, getRequiredElement} from 'chrome://resources/js/util_ts.js';
-
-import {AppInfo} from './app_info.js';
-import {APP_LAUNCH, AppsPage} from './apps_page.js';
-import {PageListView} from './page_list_view.js';
-
-/**
- * @fileoverview New tab page 4
- * This is the main code for a previous version of the Chrome NTP ("NTP4").
- * Some parts of this are still used for the chrome://apps page.
- */
-
-// Use an anonymous function to enable strict mode just for this file (which
-// will be concatenated with other files when embedded in Chrome
-
-/** @type {!NewTabView|undefined} */
-let newTabView;
-
-/**
- * The time when all sections are ready.
- * @type {number|undefined}
- * @private
- */
-let startTime;
-
-/**
- * The number of sections to wait on.
- * @type {number}
- */
-let sectionsToWaitFor = -1;
-
-/**
- * The time in milliseconds for most transitions. This should match what's
- * in new_tab.css. Unfortunately there's no better way to try to time
- * something to occur until after a transition has completed.
- * @type {number}
- * @const
- */
-const DEFAULT_TRANSITION_TIME = 500;
-
-/**
- * Creates a NewTabView object. NewTabView extends PageListView with
- * new tab UI specific logics.
- * @constructor
- * @extends {PageListView}
- */
-function NewTabView() {
- const pageSwitcherStart =
- /** @type {!Element} */ (getRequiredElement('page-switcher-start'));
- const pageSwitcherEnd =
- /** @type {!Element} */ (getRequiredElement('page-switcher-end'));
- this.initialize(
- getRequiredElement('page-list'), getRequiredElement('dot-list'),
- getRequiredElement('card-slider-frame'), getRequiredElement('trash'),
- pageSwitcherStart, pageSwitcherEnd);
-}
-
-// TODO(dbeam): NewTabView is now the only extender of PageListView; these
-// classes should be merged.
-NewTabView.prototype = {
- __proto__: PageListView.prototype,
-};
-
-/**
- * Invoked at startup once the DOM is available to initialize the app.
- */
-function onLoad() {
- sectionsToWaitFor = 1;
- measureNavDots();
-
- newTabView = new NewTabView();
-
- if (!loadTimeData.getBoolean('showWebStoreIcon')) {
- const webStoreIcon = $('chrome-web-store-link');
- // Not all versions of the NTP have a footer, so this may not exist.
- if (webStoreIcon) {
- webStoreIcon.hidden = true;
- }
- } else {
- const webStoreLink = loadTimeData.getString('webStoreLink');
- const url = appendParam(webStoreLink, 'utm_source', 'chrome-ntp-launcher');
- $('chrome-web-store-link').href = url;
- $('chrome-web-store-link')
- .addEventListener('auxclick', onChromeWebStoreButtonClick);
- $('chrome-web-store-link')
- .addEventListener('click', onChromeWebStoreButtonClick);
- }
-
- // We need to wait for all the footer menu setup to be completed before
- // we can compute its layout.
- layoutFooter();
-
- doWhenAllSectionsReady(function() {
- // Tell the slider about the pages.
- newTabView.updateSliderCards();
- // Mark the current page.
- newTabView.cardSlider.currentCardValue.navigationDot.classList.add(
- 'selected');
-
- document.documentElement.classList.remove('starting-up');
-
- startTime = Date.now();
-
- addWebUiListener('theme-changed', () => {
- $('themecss').href = 'chrome://theme/css/new_tab_theme.css?' + Date.now();
- });
- chrome.send('observeThemeChanges');
- });
-}
-
-/**
- * Launches the chrome web store app with the chrome-ntp-launcher
- * source.
- * @param {!Event} e The click/auxclick event.
- */
-function onChromeWebStoreButtonClick(e) {
- if (e.button > 1) {
- return;
- } // Ignore buttons other than left and middle.
- chrome.send(
- 'recordAppLaunchByURL',
- [e.currentTarget.href, APP_LAUNCH.NTP_WEBSTORE_FOOTER]);
-}
-
-/**
- * Launches the deprecated apps deletion dialog on click.
- * @param {!Event} e The click/auxclick event.
- */
-function onChromeDeprecatedAppsDeletionLinkClick(e) {
- if (/** @type {MouseEvent} */ (e).button > 1) {
- return;
- }
- e.preventDefault();
- chrome.send('deprecatedDialogLinkClicked');
-}
-
-/**
- * Queued callbacks which lie in wait for all sections to be ready.
- * @type {Array}
- */
-const readyCallbacks = [];
-
-/**
- * Fired as each section of pages becomes ready.
- */
-document.addEventListener('sectionready', function(e) {
- if (--sectionsToWaitFor <= 0) {
- while (readyCallbacks.length) {
- readyCallbacks.shift()();
- }
- }
-});
-
-/**
- * This is used to simulate a fire-once event (i.e. $(document).ready() in
- * jQuery or Y.on('domready') in YUI. If all sections are ready, the callback
- * is fired right away. If all pages are not ready yet, the function is queued
- * for later execution.
- * @param {Function} callback The work to be done when ready.
- */
-function doWhenAllSectionsReady(callback) {
- assert(typeof callback === 'function');
- if (sectionsToWaitFor > 0) {
- readyCallbacks.push(callback);
- } else {
- window.setTimeout(callback, 0);
- } // Do soon after, but asynchronously.
-}
-
-/**
- * Measure the width of a nav dot with a given title.
- * @param {string} id The loadTimeData ID of the desired title.
- * @return {number} The width of the nav dot.
- */
-function measureNavDot(id) {
- const measuringDiv = $('fontMeasuringDiv');
- measuringDiv.textContent = loadTimeData.getString(id);
- // The 4 is for border and padding.
- return Math.max(measuringDiv.clientWidth * 1.15 + 4, 80);
-}
-
-/**
- * Fills in an invisible div with the longest dot title string so that
- * its length may be measured and the nav dots sized accordingly.
- */
-function measureNavDots() {
- const styleElement = document.createElement('style');
- styleElement.type = 'text/css';
- // max-width is used because if we run out of space, the nav dots will be
- // shrunk.
- const pxWidth = measureNavDot('appDefaultPageName');
- styleElement.textContent = '.dot { max-width: ' + pxWidth + 'px; }';
- document.querySelector('head').appendChild(styleElement);
-}
-
-/**
- * Layout the footer so that the nav dots stay centered.
- */
-function layoutFooter() {
- // We need the image to be loaded.
- const logo = $('logo-img');
- const logoImg = logo.querySelector('img');
-
- // Only compare the width after the footer image successfully loaded.
- if (!logoImg.complete || logoImg.width === 0) {
- logoImg.onload = layoutFooter;
- return;
- }
-
- const menu = $('footer-menu-container');
- if (menu.clientWidth > logoImg.width) {
- logo.style.WebkitFlex = '0 1 ' + menu.clientWidth + 'px';
- } else {
- menu.style.WebkitFlex = '0 1 ' + logoImg.width + 'px';
- }
-}
-
-/**
- * Set the dominant color for a node. This will be called in response to
- * getFaviconDominantColor. The node represented by |id| better have a setter
- * for stripeColor.
- * @param {string} id The ID of a node.
- * @param {string} color The color represented as a CSS string.
- */
-function setFaviconDominantColor(id, color) {
- const node = $(id);
- if (node) {
- node.stripeColor = color;
- }
-}
-
-/**
- * Wrappers to forward the callback to corresponding PageListView member.
- */
-
-/**
- * Called by chrome when a new app has been added to chrome or has been
- * enabled if previously disabled.
- * @param {AppInfo} appData A data structure full of relevant information for
- * the app.
- * @param {boolean=} opt_highlight Whether the app about to be added should
- * be highlighted.
- */
-function appAdded(appData, opt_highlight) {
- newTabView.appAdded(appData, opt_highlight);
-}
-
-/**
- * Called by chrome when an app has changed positions.
- * @param {AppInfo} appData The data for the app. This contains page and
- * position indices.
- */
-function appMoved(appData) {
- newTabView.appMoved(appData);
-}
-
-/**
- * Called by chrome when an existing app has been disabled or
- * removed/uninstalled from chrome.
- * @param {AppInfo} appData A data structure full of relevant information for
- * the app.
- * @param {boolean} isUninstall True if the app is being uninstalled;
- * false if the app is being disabled.
- * @param {boolean} fromPage True if the removal was from the current page.
- */
-function appRemoved(appData, isUninstall, fromPage) {
- newTabView.appRemoved(appData, isUninstall, fromPage);
-}
-
-/**
- * Callback invoked by chrome whenever an app preference changes.
- * @param {Object} data An object with all the data on available
- * applications.
- */
-function appsPrefChangeCallback(data) {
- newTabView.appsPrefChangedCallback(data);
-}
-
-/**
- * Called whenever tiles should be re-arranging themselves out of the way
- * of a moving or insert tile.
- */
-export function enterRearrangeMode() {
- newTabView.enterRearrangeMode();
-}
-
-/**
- * Callback invoked by chrome with the apps available.
- *
- * Note that calls to this function can occur at any time, not just in
- * response to a getApps request. For example, when a user
- * installs/uninstalls an app on another synchronized devices.
- * @param {{apps: Array<AppInfo>, appPageNames: Array<string>,
- * deprecatedAppsDialogLinkText: string}} data An object with all the data
- * on available applications.
- */
-function getAppsCallback(data) {
- newTabView.getAppsCallback(data);
- setUpDeprecatedAppsDialogLink(data.deprecatedAppsDialogLinkText);
-}
-
-/**
- * Called whenever there are deprecated apps on the page, to set up the link
- * to trigger the deprecated apps dialog.
- * @param {string} linkText The link text to trigger the deprecated apps dialog.
- */
-function setUpDeprecatedAppsDialogLink(linkText) {
- if (linkText) {
- $('deprecated-apps-link').textContent = linkText;
- $('deprecated-apps-link')
- .addEventListener('click', onChromeDeprecatedAppsDeletionLinkClick);
- $('deprecated-apps-link-container').hidden = false;
- } else {
- $('deprecated-apps-link-container').hidden = true;
- }
-}
-
-/**
- * Return the index of the given apps page.
- * @param {AppsPage} page The AppsPage we wish to find.
- * @return {number} The index of |page| or -1 if it is not in the collection.
- */
-export function getAppsPageIndex(page) {
- return newTabView.getAppsPageIndex(page);
-}
-
-export function getCardSlider() {
- return newTabView.cardSlider;
-}
-
-/**
- * Invoked whenever some app is released
- */
-export function leaveRearrangeMode() {
- newTabView.leaveRearrangeMode();
-}
-
-/**
- * Save the name of an apps page.
- * Store the apps page name into the preferences store.
- * @param {AppsPage} appPage The app page for which we wish to save.
- * @param {string} name The name of the page.
- */
-export function saveAppPageName(appPage, name) {
- newTabView.saveAppPageName(appPage, name);
-}
-
-// Return an object with all the exports
-const exports = {
- appAdded,
- appMoved,
- appRemoved,
- appsPrefChangeCallback,
- getAppsCallback,
- setFaviconDominantColor,
-};
-
-window['ntp'] = window['ntp'] || {};
-for (const key of Object.keys(exports)) {
- window['ntp'][key] = exports[key];
-}
-
-document.addEventListener('DOMContentLoaded', onLoad);
diff --git a/chromium/chrome/browser/resources/ntp4/page_list_view.js b/chromium/chrome/browser/resources/ntp4/page_list_view.js
deleted file mode 100644
index 26dae02707d..00000000000
--- a/chromium/chrome/browser/resources/ntp4/page_list_view.js
+++ /dev/null
@@ -1,736 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assert} from 'chrome://resources/js/assert_ts.js';
-import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {$, isRTL} from 'chrome://resources/js/util_ts.js';
-
-import {AppInfo} from './app_info.js';
-import {App, AppsPage} from './apps_page.js';
-import {CardSlider} from './card_slider.js';
-import {dispatchSimpleEvent} from './cr_deprecated.js';
-import {DotList} from './dot_list.js';
-import {NavDot} from './nav_dot.js';
-import {initializePageSwitcher, PageSwitcher} from './page_switcher.js';
-import {getCurrentlyDraggingTile, TilePage} from './tile_page.js';
-import {Trash} from './trash.js';
-import {decorate} from './ui.js';
-
-/**
- * @fileoverview PageListView implementation.
- * PageListView manages page list, dot list, switcher buttons and handles apps
- * pages callbacks from backend.
- *
- * Note that you need to have AppLauncherHandler in your WebUI to use this code.
- */
-
-/**
- * Creates a PageListView object.
- * @constructor
- * @extends {Object}
- */
-export function PageListView() {}
-
-PageListView.prototype = {
- /**
- * The CardSlider object to use for changing app pages.
- * @type {CardSlider|undefined}
- */
- cardSlider: undefined,
-
- /**
- * The frame div for this.cardSlider.
- * @type {!Element|undefined}
- */
- sliderFrame: undefined,
-
- /**
- * The 'page-list' element.
- * @type {!Element|undefined}
- */
- pageList: undefined,
-
- /**
- * A list of all 'tile-page' elements.
- * @type {!HTMLCollection<!TilePage>|undefined}
- */
- tilePages: undefined,
-
- /**
- * A list of all 'apps-page' elements.
- * @type {!HTMLCollection<!AppsPage>|undefined}
- */
- appsPages: undefined,
-
- /**
- * The 'dots-list' element.
- * @type {!DotList|undefined}
- */
- dotList: undefined,
-
- /**
- * The left and right paging buttons.
- * @type {!PageSwitcher|undefined}
- */
- pageSwitcherStart: undefined,
- pageSwitcherEnd: undefined,
-
- /**
- * The 'trash' element. Note that technically this is unnecessary,
- * JavaScript creates the object for us based on the id. But I don't want
- * to rely on the ID being the same, and JSCompiler doesn't know about it.
- * @type {!Element|undefined}
- */
- trash: undefined,
-
- /**
- * The index of the page that is currently shown. For example if the third
- * page is showing, this will be 2.
- * @type {number}
- */
- shownPageIndex: 0,
-
- /**
- * EventTracker for managing event listeners for page events.
- * @type {!EventTracker}
- */
- eventTracker: new EventTracker(),
-
- /**
- * If non-null, this is the ID of the app to highlight to the user the next
- * time getAppsCallback runs. "Highlight" in this case means to switch to
- * the page and run the new tile animation.
- * @type {?string}
- */
- highlightAppId: null,
-
- /**
- * Initializes page list view.
- * @param {!Element} pageList A DIV element to host all pages.
- * @param {!Element} dotList An UL element to host nav dots. Each dot
- * represents a page.
- * @param {!Element} cardSliderFrame The card slider frame that hosts
- * pageList and switcher buttons.
- * @param {!Element|undefined} opt_trash Optional trash element.
- * @param {!Element|undefined} opt_pageSwitcherStart Optional start
- * page switcher button.
- * @param {!Element|undefined} opt_pageSwitcherEnd Optional end
- * page switcher button.
- */
- initialize(
- pageList, dotList, cardSliderFrame, opt_trash, opt_pageSwitcherStart,
- opt_pageSwitcherEnd) {
- this.pageList = pageList;
-
- this.dotList = /** @type {!DotList} */ (dotList);
- decorate(this.dotList, DotList);
-
- this.trash = opt_trash;
- if (this.trash) {
- new Trash(this.trash);
- }
-
- this.pageSwitcherStart =
- /** @type {!PageSwitcher} */ (opt_pageSwitcherStart);
- if (this.pageSwitcherStart) {
- initializePageSwitcher(this.pageSwitcherStart);
- }
-
- this.pageSwitcherEnd = /** @type {!PageSwitcher} */ (opt_pageSwitcherEnd);
- if (this.pageSwitcherEnd) {
- initializePageSwitcher(this.pageSwitcherEnd);
- }
-
- this.shownPageIndex = loadTimeData.getInteger('shown_page_index');
-
- // Request data on the apps so we can fill them in.
- // Note that this is kicked off asynchronously. 'getAppsCallback' will
- // be invoked at some point after this function returns.
- chrome.send('getApps');
-
- document.addEventListener('keydown', this.onDocKeyDown_.bind(this));
-
- this.tilePages = /** @type {!HTMLCollection<!TilePage>} */ (
- this.pageList.getElementsByClassName('tile-page'));
- this.appsPages = /** @type {!HTMLCollection<!AppsPage>} */ (
- this.pageList.getElementsByClassName('apps-page'));
-
- // Initialize the cardSlider without any cards at the moment.
- this.sliderFrame = cardSliderFrame;
- this.cardSlider = new CardSlider(
- this.sliderFrame, this.pageList, this.sliderFrame.offsetWidth);
-
- // Prevent touch events from triggering any sort of native scrolling if
- // there are multiple cards in the slider frame.
- const cardSlider = this.cardSlider;
- cardSliderFrame.addEventListener('touchmove', function(e) {
- if (cardSlider.cardCount <= 1) {
- return;
- }
- e.preventDefault();
- }, true);
-
- // Handle mousewheel events anywhere in the card slider, so that wheel
- // events on the page switchers will still scroll the page.
- // This listener must be added before the card slider is initialized,
- // because it needs to be called before the card slider's handler.
- cardSliderFrame.addEventListener('mousewheel', function(e) {
- if (/** @type {!TilePage} */ (cardSlider.currentCardValue)
- .handleMouseWheel(e)) {
- e.preventDefault(); // Prevent default scroll behavior.
- e.stopImmediatePropagation(); // Prevent horizontal card flipping.
- }
- });
-
- this.cardSlider.initialize(
- loadTimeData.getBoolean('isSwipeTrackingFromScrollEventsEnabled'));
-
- // Handle events from the card slider.
- this.pageList.addEventListener(
- 'cardSlider:card_changed', this.onCardChanged_.bind(this));
- this.pageList.addEventListener(
- 'cardSlider:card_added', this.onCardAdded_.bind(this));
- this.pageList.addEventListener(
- 'cardSlider:card_removed', this.onCardRemoved_.bind(this));
-
- // Ensure the slider is resized appropriately with the window.
- window.addEventListener('resize', this.onWindowResize_.bind(this));
-
- // Update apps when online state changes.
- window.addEventListener(
- 'online', this.updateOfflineEnabledApps_.bind(this));
- window.addEventListener(
- 'offline', this.updateOfflineEnabledApps_.bind(this));
- },
-
- /**
- * Appends a tile page.
- *
- * @param {!TilePage} page The page element.
- * @param {string} title The title of the tile page.
- * @param {boolean} titleIsEditable If true, the title can be changed.
- * @param {TilePage=} opt_refNode Optional reference node to insert in
- * front of.
- * When opt_refNode is falsey, |page| will just be appended to the end of
- * the page list.
- */
- appendTilePage(page, title, titleIsEditable, opt_refNode) {
- if (opt_refNode) {
- const refIndex = this.getTilePageIndex(opt_refNode);
- this.cardSlider.addCardAtIndex(page, refIndex);
- } else {
- this.cardSlider.appendCard(page);
- }
-
- // If we're appending an AppsPage and it's a temporary page, animate it.
- const animate =
- page instanceof AppsPage && page.classList.contains('temporary');
- // Make a deep copy of the dot template to add a new one.
- const newDot = new NavDot(page, title, titleIsEditable, animate);
- page.navigationDot = newDot;
- this.dotList.insertBefore(
- newDot, opt_refNode ? opt_refNode.navigationDot : null);
- // Set a tab index on the first dot.
- if (this.dotList.dots.length === 1) {
- newDot.tabIndex = 3;
- }
-
- this.eventTracker.add(page, 'pagelayout', this.onPageLayout_.bind(this));
- },
-
- /**
- * Called by chrome when an app has changed positions.
- * @param {AppInfo} appData The data for the app. This contains page and
- * position indices.
- */
- appMoved(appData) {
- const app = /** @type {App} */ ($(appData.id));
- if (!app) {
- return;
- }
- app.remove(false);
-
- this.appsPages[appData.page_index].insertApp(appData, false);
- },
-
- /**
- * Called by chrome when an existing app has been disabled or
- * removed/uninstalled from chrome.
- * @param {AppInfo} appData A data structure full of relevant information
- * for the app.
- * @param {boolean} isUninstall True if the app is being uninstalled;
- * false if the app is being disabled.
- * @param {boolean} fromPage True if the removal was from the current page.
- */
- appRemoved(appData, isUninstall, fromPage) {
- const app = /** @type {App} */ ($(appData.id));
- if (!app) {
- return;
- }
-
- if (!isUninstall) {
- app.replaceAppData(appData);
- } else {
- app.remove(!!fromPage);
- }
- },
-
- /**
- * @return {boolean} If the page is still starting up.
- * @private
- */
- isStartingUp_() {
- return document.documentElement.classList.contains('starting-up');
- },
-
- /**
- * Tracks whether apps have been loaded at least once.
- * @type {boolean}
- * @private
- */
- appsLoaded_: false,
-
- /**
- * Callback invoked by chrome with the apps available.
- *
- * Note that calls to this function can occur at any time, not just in
- * response to a getApps request. For example, when a user
- * installs/uninstalls an app on another synchronized devices.
- * @param {{apps: Array<AppInfo>, appPageNames: Array<string>,
- * deprecatedAppsDialogLinkText: string}} data An object with all the data
- * on available applications.
- */
- getAppsCallback(data) {
- // Remember this to select the correct card when done rebuilding.
- const prevCurrentCard = this.cardSlider.currentCard;
-
- // Make removal of pages and dots as quick as possible with less DOM
- // operations, reflows, or repaints. We set currentCard = 0 and remove
- // from the end to not encounter any auto-magic card selections in the
- // process and we hide the card slider throughout.
- this.cardSlider.currentCard = 0;
-
- // Clear any existing apps pages and dots.
- // TODO(rbyers): It might be nice to preserve animation of dots after an
- // uninstall. Could we re-use the existing page and dot elements? It
- // seems unfortunate to have Chrome send us the entire apps list after an
- // uninstall.
- while (this.appsPages.length > 0) {
- this.removeTilePageAndDot_(this.appsPages[this.appsPages.length - 1]);
- }
-
- // Get the array of apps and add any special synthesized entries
- const apps = data.apps;
-
- // Get a list of page names
- const pageNames = data.appPageNames;
-
- function stringListIsEmpty(list) {
- for (let i = 0; i < list.length; i++) {
- if (list[i]) {
- return false;
- }
- }
- return true;
- }
-
- // Sort by launch ordinal
- apps.sort(function(a, b) {
- return a.app_launch_ordinal > b.app_launch_ordinal ?
- 1 :
- a.app_launch_ordinal < b.app_launch_ordinal ? -1 : 0;
- });
-
- // An app to animate (in case it was just installed).
- let highlightApp;
-
- // If there are any pages after the apps, add new pages before them.
- const lastAppsPage = (this.appsPages.length > 0) ?
- this.appsPages[this.appsPages.length - 1] :
- null;
- const lastAppsPageIndex = (lastAppsPage != null) ?
- Array.prototype.indexOf.call(this.tilePages, lastAppsPage) :
- -1;
- const nextPageAfterApps =
- lastAppsPageIndex !== -1 ? this.tilePages[lastAppsPageIndex + 1] : null;
-
- // Add the apps, creating pages as necessary
- for (let i = 0; i < apps.length; i++) {
- const app = apps[i];
- const pageIndex = app.page_index || 0;
- while (pageIndex >= this.appsPages.length) {
- let pageName = loadTimeData.getString('appDefaultPageName');
- if (this.appsPages.length < pageNames.length) {
- pageName = pageNames[this.appsPages.length];
- }
-
- const origPageCount = this.appsPages.length;
- this.appendTilePage(new AppsPage(), pageName, true, nextPageAfterApps);
- // Confirm that appsPages is a live object, updated when a new page is
- // added (otherwise we'd have an infinite loop)
- assert(
- this.appsPages.length === origPageCount + 1, 'expected new page');
- }
-
- if (app.id === this.highlightAppId) {
- highlightApp = app;
- } else {
- this.appsPages[pageIndex].insertApp(app, false);
- }
- }
-
- this.cardSlider.currentCard = prevCurrentCard;
-
- if (highlightApp) {
- this.appAdded(highlightApp, true);
- }
-
- // Tell the slider about the pages and mark the current page.
- this.updateSliderCards();
- this.cardSlider.currentCardValue.navigationDot.classList.add('selected');
-
- if (!this.appsLoaded_) {
- this.appsLoaded_ = true;
- dispatchSimpleEvent(document, 'sectionready', true, true);
- }
- },
-
- /**
- * Called by chrome when a new app has been added to chrome or has been
- * enabled if previously disabled.
- * @param {AppInfo} appData A data structure full of relevant information
- * for the app.
- * @param {boolean=} opt_highlight Whether the app about to be added should
- * be highlighted.
- */
- appAdded(appData, opt_highlight) {
- if (appData.id === this.highlightAppId) {
- opt_highlight = true;
- this.highlightAppId = null;
- }
-
- const pageIndex = appData.page_index || 0;
-
- if (pageIndex >= this.appsPages.length) {
- while (pageIndex >= this.appsPages.length) {
- this.appendTilePage(
- new AppsPage(), loadTimeData.getString('appDefaultPageName'), true);
- }
- this.updateSliderCards();
- }
-
- const page = this.appsPages[pageIndex];
- const app = /** @type {?App} */ ($(appData.id));
- if (app) {
- app.replaceAppData(appData);
- } else if (opt_highlight) {
- page.insertAndHighlightApp(appData);
- this.setShownPage_(appData.page_index);
- } else {
- page.insertApp(appData, false);
- }
- },
-
- /**
- * Callback invoked by chrome whenever an app preference changes.
- * @param {Object} data An object with all the data on available
- * applications.
- */
- appsPrefChangedCallback(data) {
- for (let i = 0; i < data.apps.length; ++i) {
- $(data.apps[i].id).appData = data.apps[i];
- }
-
- // Set the App dot names.
- const dots = this.dotList.getElementsByClassName('dot');
- for (let i = 0; i < dots.length; ++i) {
- dots[i].displayTitle = data.appPageNames[i] || '';
- }
- },
-
- /**
- * Invoked whenever the pages in apps-page-list have changed so that
- * the Slider knows about the new elements.
- */
- updateSliderCards() {
- const pageNo = Math.max(
- 0, Math.min(this.cardSlider.currentCard, this.tilePages.length - 1));
- this.cardSlider.setCards(
- Array.prototype.slice.call(this.tilePages), pageNo);
- this.cardSlider.selectCardByValue(this.appsPages[Math.min(
- this.shownPageIndex, this.appsPages.length - 1)]);
- },
-
- /**
- * Called whenever tiles should be re-arranging themselves out of the way
- * of a moving or insert tile.
- */
- enterRearrangeMode() {
- const tempPage = new AppsPage();
- tempPage.classList.add('temporary');
- const pageName = loadTimeData.getString('appDefaultPageName');
- this.appendTilePage(tempPage, pageName, true);
-
- if (getCurrentlyDraggingTile().firstChild.canBeRemoved()) {
- $('footer').classList.add('showing-trash-mode');
- $('footer-menu-container').style.minWidth = $('trash').offsetWidth -
- $('chrome-web-store-link').offsetWidth + 'px';
- }
-
- document.documentElement.classList.add('dragging-mode');
- },
-
- /**
- * Invoked whenever some app is released
- */
- leaveRearrangeMode() {
- const tempPage = /** @type {AppsPage} */ (
- document.querySelector('.tile-page.temporary'));
- if (tempPage) {
- const dot = tempPage.navigationDot;
- if (!tempPage.tileCount &&
- tempPage !== this.cardSlider.currentCardValue) {
- this.removeTilePageAndDot_(tempPage, true);
- } else {
- tempPage.classList.remove('temporary');
- this.saveAppPageName(
- tempPage, loadTimeData.getString('appDefaultPageName'));
- }
- }
-
- $('footer').classList.remove('showing-trash-mode');
- $('footer-menu-container').style.minWidth = '';
- document.documentElement.classList.remove('dragging-mode');
- },
-
- /**
- * Callback for the 'pagelayout' event.
- * @param {Event} e The event.
- */
- onPageLayout_(e) {
- if (Array.prototype.indexOf.call(this.tilePages, e.currentTarget) !==
- this.cardSlider.currentCard) {
- return;
- }
-
- this.updatePageSwitchers();
- },
-
- /**
- * Adjusts the size and position of the page switchers according to the
- * layout of the current card, and updates the aria-label attributes of
- * the page switchers.
- */
- updatePageSwitchers() {
- if (!this.pageSwitcherStart || !this.pageSwitcherEnd) {
- return;
- }
-
- const page =
- /** @type {?TilePage} */ (this.cardSlider.currentCardValue);
-
- this.pageSwitcherStart.hidden =
- !page || (this.cardSlider.currentCard === 0);
- this.pageSwitcherEnd.hidden = !page ||
- (this.cardSlider.currentCard === this.cardSlider.cardCount - 1);
-
- if (!page) {
- return;
- }
-
- const pageSwitcherLeft =
- isRTL() ? this.pageSwitcherEnd : this.pageSwitcherStart;
- const pageSwitcherRight =
- isRTL() ? this.pageSwitcherStart : this.pageSwitcherEnd;
- const scrollbarWidth = page.scrollbarWidth;
- pageSwitcherLeft.style.width = (page.sideMargin + 13) + 'px';
- pageSwitcherLeft.style.left = '0';
- pageSwitcherRight.style.width =
- (page.sideMargin - scrollbarWidth + 13) + 'px';
- pageSwitcherRight.style.right = scrollbarWidth + 'px';
-
- const offsetTop = page.querySelector('.tile-page-content').offsetTop + 'px';
- pageSwitcherLeft.style.top = offsetTop;
- pageSwitcherRight.style.top = offsetTop;
- pageSwitcherLeft.style.paddingBottom = offsetTop;
- pageSwitcherRight.style.paddingBottom = offsetTop;
-
- // Update the aria-label attributes of the two page switchers.
- this.pageSwitcherStart.updateButtonAccessibleLabel(this.dotList.dots);
- this.pageSwitcherEnd.updateButtonAccessibleLabel(this.dotList.dots);
- },
-
- /**
- * Returns the index of the given apps page.
- * @param {AppsPage} page The AppsPage we wish to find.
- * @return {number} The index of |page| or -1 if it is not in the
- * collection.
- */
- getAppsPageIndex(page) {
- return Array.prototype.indexOf.call(this.appsPages, page);
- },
-
- /**
- * Handler for cardSlider:card_changed events from this.cardSlider.
- * @param {Event} e The cardSlider:card_changed event.
- * @private
- */
- onCardChanged_(e) {
- const page = e.cardSlider.currentCardValue;
-
- // Don't change shownPage until startup is done (and page changes actually
- // reflect user actions).
- if (!this.isStartingUp_()) {
- // TODO(dbeam): is this ever false?
- if (page.classList.contains('apps-page')) {
- this.setShownPage_(this.getAppsPageIndex(page));
- } else {
- console.error('unknown page selected');
- }
- }
-
- // Update the active dot
- const curDot = this.dotList.getElementsByClassName('selected')[0];
- if (curDot) {
- curDot.classList.remove('selected');
- }
- page.navigationDot.classList.add('selected');
- this.updatePageSwitchers();
- },
-
- /**
- * Saves/updates the newly selected page to open when first loading the NTP.
- * @param {number} shownPageIndex The new shown page index.
- * @private
- */
- setShownPage_(shownPageIndex) {
- assert(shownPageIndex >= 0);
- this.shownPageIndex = shownPageIndex;
- chrome.send('pageSelected', [this.shownPageIndex]);
- },
-
- /**
- * Listen for card additions to update the page switchers or the current
- * card accordingly.
- * @param {Event} e A card removed or added event.
- */
- onCardAdded_(e) {
- // When the second arg passed to insertBefore is falsey, it acts just like
- // appendChild.
- this.pageList.insertBefore(e.addedCard, this.tilePages[e.addedIndex]);
- this.onCardAddedOrRemoved_();
- },
-
- /**
- * Listen for card removals to update the page switchers or the current card
- * accordingly.
- * @param {Event} e A card removed or added event.
- */
- onCardRemoved_(e) {
- e.removedCard.parentNode.removeChild(e.removedCard);
- this.onCardAddedOrRemoved_();
- },
-
- /**
- * Called when a card is removed or added.
- * @private
- */
- onCardAddedOrRemoved_() {
- if (this.isStartingUp_()) {
- return;
- }
-
- // Without repositioning there were issues - http://crbug.com/133457.
- this.cardSlider.repositionFrame();
- this.updatePageSwitchers();
- },
-
- /**
- * Save the name of an apps page.
- * Store the apps page name into the preferences store.
- * @param {AppsPage} appPage The app page for which we wish to save.
- * @param {string} name The name of the page.
- */
- saveAppPageName(appPage, name) {
- const index = this.getAppsPageIndex(appPage);
- assert(index !== -1);
- chrome.send('saveAppPageName', [name, index]);
- },
-
- /**
- * Window resize handler.
- * @private
- */
- onWindowResize_(e) {
- this.cardSlider.resize(this.sliderFrame.offsetWidth);
- this.updatePageSwitchers();
- },
-
- /**
- * Listener for offline status change events. Updates apps that are
- * not offline-enabled to be grayscale if the browser is offline.
- * @private
- */
- updateOfflineEnabledApps_() {
- const apps =
- /** @type {!NodeList<!App>} */ (document.querySelectorAll('.app'));
- for (let i = 0; i < apps.length; ++i) {
- if (apps[i].appData.enabled && !apps[i].appData.offlineEnabled) {
- apps[i].setIcon();
- apps[i].loadIcon();
- }
- }
- },
-
- /**
- * Handler for key events on the page. Ctrl-Arrow will switch the visible
- * page.
- * @param {Event} e The KeyboardEvent.
- * @private
- */
- onDocKeyDown_(e) {
- if (!e.ctrlKey || e.altKey || e.metaKey || e.shiftKey) {
- return;
- }
-
- let direction = 0;
- if (e.key === 'ArrowLeft') {
- direction = -1;
- } else if (e.key === 'ArrowRight') {
- direction = 1;
- } else {
- return;
- }
-
- const cardIndex =
- (this.cardSlider.currentCard + direction + this.cardSlider.cardCount) %
- this.cardSlider.cardCount;
- this.cardSlider.selectCard(cardIndex, true);
-
- e.stopPropagation();
- },
-
- /**
- * Returns the index of a given tile page.
- * @param {TilePage} page The TilePage we wish to find.
- * @return {number} The index of |page| or -1 if it is not in the
- * collection.
- */
- getTilePageIndex(page) {
- return Array.prototype.indexOf.call(this.tilePages, page);
- },
-
- /**
- * Removes a page and navigation dot (if the navdot exists).
- * @param {TilePage} page The page to be removed.
- * @param {boolean=} opt_animate If the removal should be animated.
- */
- removeTilePageAndDot_(page, opt_animate) {
- if (page.navigationDot) {
- page.navigationDot.remove(opt_animate);
- }
- this.cardSlider.removeCard(page);
- },
-};
diff --git a/chromium/chrome/browser/resources/ntp4/page_switcher.js b/chromium/chrome/browser/resources/ntp4/page_switcher.js
deleted file mode 100644
index 5989bde9d68..00000000000
--- a/chromium/chrome/browser/resources/ntp4/page_switcher.js
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {DragWrapper} from 'chrome://resources/js/drag_wrapper.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-
-import {getCardSlider} from './new_tab.js';
-import {getCurrentlyDraggingTile} from './tile_page.js';
-
-/**
- * @fileoverview Page switcher
- * This is the class for the left and right navigation arrows that switch
- * between pages.
- */
-
-/**
- * @constructor
- * @extends {HTMLButtonElement}
- */
-export function PageSwitcher() {}
-
-PageSwitcher.prototype = {
- __proto__: HTMLButtonElement.prototype,
-
- /**
- * Activate the switcher (go to the next card).
- * @private
- */
- activate_() {
- getCardSlider().selectCard(this.nextCardIndex_(), true);
- },
-
- /**
- * Calculate the index of the card that this button will switch to.
- * @private
- */
- nextCardIndex_() {
- const cardSlider = getCardSlider();
- const index = cardSlider.currentCard + this.direction_;
- const numCards = cardSlider.cardCount - 1;
- return Math.max(0, Math.min(index, numCards));
- },
-
- /**
- * Update the accessible label attribute of this button, based on the
- * current position in the card slider and the names of the cards.
- * @param {NodeList} dots The dot elements which display the names of the
- * cards.
- */
- updateButtonAccessibleLabel(dots) {
- const currentIndex = getCardSlider().currentCard;
- const nextCardIndex = this.nextCardIndex_();
- if (nextCardIndex === currentIndex) {
- this.setAttribute('aria-label', ''); // No next card.
- return;
- }
-
- const currentDot = dots[currentIndex];
- const nextDot = dots[nextCardIndex];
- if (!currentDot || !nextDot) {
- this.setAttribute('aria-label', ''); // Dots not initialised yet.
- return;
- }
-
- const currentPageTitle = currentDot.displayTitle;
- const nextPageTitle = nextDot.displayTitle;
- const msgName = (currentPageTitle === nextPageTitle) ?
- 'page_switcher_same_title' :
- 'page_switcher_change_title';
- const ariaLabel = loadTimeData.getStringF(msgName, nextPageTitle);
- this.setAttribute('aria-label', ariaLabel);
- },
-
- shouldAcceptDrag(e) {
- // Only allow page switching when a drop could happen on the page being
- // switched to.
- const nextPage = getCardSlider().getCardAtIndex(this.nextCardIndex_());
- return nextPage.shouldAcceptDrag(e);
- },
-
- doDragEnter(e) {
- this.scheduleDelayedSwitch_(e);
- this.doDragOver(e);
- },
-
- doDragLeave(e) {
- this.cancelDelayedSwitch_();
- },
-
- doDragOver(e) {
- e.preventDefault();
- const targetPage = getCardSlider().currentCardValue;
- if (targetPage.shouldAcceptDrag(e)) {
- targetPage.setDropEffect(e.dataTransfer);
- }
- },
-
- doDrop(e) {
- e.stopPropagation();
- this.cancelDelayedSwitch_();
-
- const tile = getCurrentlyDraggingTile();
- if (!tile) {
- return;
- }
-
- const sourcePage = tile.tilePage;
- const targetPage = getCardSlider().currentCardValue;
- if (targetPage === sourcePage || !targetPage.shouldAcceptDrag(e)) {
- return;
- }
-
- targetPage.appendDraggingTile();
- },
-
- /**
- * Starts a timer to activate the switcher. The timer repeats until
- * cancelled by cancelDelayedSwitch_.
- * @private
- */
- scheduleDelayedSwitch_(e) {
- // Stop switching when the next page can't be dropped onto.
- const nextPage = getCardSlider().getCardAtIndex(this.nextCardIndex_());
- if (!nextPage.shouldAcceptDrag(e)) {
- return;
- }
-
- const self = this;
- function navPageClearTimeout() {
- self.activate_();
- self.dragNavTimeout_ = null;
- self.scheduleDelayedSwitch_(e);
- }
- this.dragNavTimeout_ = window.setTimeout(navPageClearTimeout, 500);
- },
-
- /**
- * Cancels the timer that activates the switcher while dragging.
- * @private
- */
- cancelDelayedSwitch_() {
- if (this.dragNavTimeout_) {
- window.clearTimeout(this.dragNavTimeout_);
- this.dragNavTimeout_ = null;
- }
- },
-
-};
-
-export function initializePageSwitcher(el) {
- el.__proto__ = PageSwitcher.prototype;
-
- el.addEventListener('click', el.activate_);
-
- el.direction_ = el.id === 'page-switcher-start' ? -1 : 1;
-
- el.dragWrapper_ = new DragWrapper(el, el);
-}
diff --git a/chromium/chrome/browser/resources/ntp4/position_util.js b/chromium/chrome/browser/resources/ntp4/position_util.js
deleted file mode 100644
index 8dd01c81048..00000000000
--- a/chromium/chrome/browser/resources/ntp4/position_util.js
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview This file provides utility functions for position popups.
- */
-
-/**
- * Type def for rects as returned by getBoundingClientRect.
- * @typedef {{left: number, top: number, width: number, height: number,
- * right: number, bottom: number}}
- */
-let Rect;
-
-/**
- * Enum for defining how to anchor a popup to an anchor element.
- * @enum {number}
- */
-export const AnchorType = {
- /**
- * The popup's right edge is aligned with the left edge of the anchor.
- * The popup's top edge is aligned with the top edge of the anchor.
- */
- BEFORE: 1, // p: right, a: left, p: top, a: top
-
- /**
- * The popop's left edge is aligned with the right edge of the anchor.
- * The popup's top edge is aligned with the top edge of the anchor.
- */
- AFTER: 2, // p: left a: right, p: top, a: top
-
- /**
- * The popop's bottom edge is aligned with the top edge of the anchor.
- * The popup's left edge is aligned with the left edge of the anchor.
- */
- ABOVE: 3, // p: bottom, a: top, p: left, a: left
-
- /**
- * The popop's top edge is aligned with the bottom edge of the anchor.
- * The popup's left edge is aligned with the left edge of the anchor.
- */
- BELOW: 4, // p: top, a: bottom, p: left, a: left
-};
-
-/**
- * Helper function for positionPopupAroundElement and positionPopupAroundRect.
- * @param {!Rect} anchorRect The rect for the anchor.
- * @param {!HTMLElement} popupElement The element used for the popup.
- * @param {AnchorType} type The type of anchoring to do.
- * @param {boolean=} opt_invertLeftRight Whether to invert the right/left
- * alignment.
- */
-function positionPopupAroundRect(
- anchorRect, popupElement, type, opt_invertLeftRight) {
- const popupRect = popupElement.getBoundingClientRect();
- let availRect;
- const ownerDoc = popupElement.ownerDocument;
- const cs = ownerDoc.defaultView.getComputedStyle(popupElement);
- const docElement = ownerDoc.documentElement;
-
- if (cs.position === 'fixed') {
- // For 'fixed' positioned popups, the available rectangle should be based
- // on the viewport rather than the document.
- availRect = {
- height: docElement.clientHeight,
- width: docElement.clientWidth,
- top: 0,
- bottom: docElement.clientHeight,
- left: 0,
- right: docElement.clientWidth,
- };
- } else {
- availRect = popupElement.offsetParent.getBoundingClientRect();
- }
-
- if (cs.direction === 'rtl') {
- opt_invertLeftRight = !opt_invertLeftRight;
- }
-
- // Flip BEFORE, AFTER based on alignment.
- if (opt_invertLeftRight) {
- if (type === AnchorType.BEFORE) {
- type = AnchorType.AFTER;
- } else if (type === AnchorType.AFTER) {
- type = AnchorType.BEFORE;
- }
- }
-
- // Flip type based on available size
- switch (type) {
- case AnchorType.BELOW:
- if (anchorRect.bottom + popupRect.height > availRect.height &&
- popupRect.height <= anchorRect.top) {
- type = AnchorType.ABOVE;
- }
- break;
- case AnchorType.ABOVE:
- if (popupRect.height > anchorRect.top &&
- anchorRect.bottom + popupRect.height <= availRect.height) {
- type = AnchorType.BELOW;
- }
- break;
- case AnchorType.AFTER:
- if (anchorRect.right + popupRect.width > availRect.width &&
- popupRect.width <= anchorRect.left) {
- type = AnchorType.BEFORE;
- }
- break;
- case AnchorType.BEFORE:
- if (popupRect.width > anchorRect.left &&
- anchorRect.right + popupRect.width <= availRect.width) {
- type = AnchorType.AFTER;
- }
- break;
- }
- // flipping done
-
- const style = popupElement.style;
- // Reset all directions.
- style.left = style.right = style.top = style.bottom = 'auto';
-
- // Primary direction
- switch (type) {
- case AnchorType.BELOW:
- if (anchorRect.bottom + popupRect.height <= availRect.height) {
- style.top = anchorRect.bottom + 'px';
- } else {
- style.bottom = '0';
- }
- break;
- case AnchorType.ABOVE:
- if (availRect.height - anchorRect.top >= 0) {
- style.bottom = availRect.height - anchorRect.top + 'px';
- } else {
- style.top = '0';
- }
- break;
- case AnchorType.AFTER:
- if (anchorRect.right + popupRect.width <= availRect.width) {
- style.left = anchorRect.right + 'px';
- } else {
- style.right = '0';
- }
- break;
- case AnchorType.BEFORE:
- if (availRect.width - anchorRect.left >= 0) {
- style.right = availRect.width - anchorRect.left + 'px';
- } else {
- style.left = '0';
- }
- break;
- }
-
- // Secondary direction
- switch (type) {
- case AnchorType.BELOW:
- case AnchorType.ABOVE:
- if (opt_invertLeftRight) {
- // align right edges
- if (anchorRect.right - popupRect.width >= 0) {
- style.right = availRect.width - anchorRect.right + 'px';
-
- // align left edges
- } else if (anchorRect.left + popupRect.width <= availRect.width) {
- style.left = anchorRect.left + 'px';
-
- // not enough room on either side
- } else {
- style.right = '0';
- }
- } else {
- // align left edges
- if (anchorRect.left + popupRect.width <= availRect.width) {
- style.left = anchorRect.left + 'px';
-
- // align right edges
- } else if (anchorRect.right - popupRect.width >= 0) {
- style.right = availRect.width - anchorRect.right + 'px';
-
- // not enough room on either side
- } else {
- style.left = '0';
- }
- }
- break;
-
- case AnchorType.AFTER:
- case AnchorType.BEFORE:
- // align top edges
- if (anchorRect.top + popupRect.height <= availRect.height) {
- style.top = anchorRect.top + 'px';
-
- // align bottom edges
- } else if (anchorRect.bottom - popupRect.height >= 0) {
- style.bottom = availRect.height - anchorRect.bottom + 'px';
-
- // not enough room on either side
- } else {
- style.top = '0';
- }
- break;
- }
-}
-
-/**
- * Positions a popup element relative to an anchor element. The popup element
- * should have position set to absolute and it should be a child of the body
- * element.
- * @param {!HTMLElement} anchorElement The element that the popup is anchored
- * to.
- * @param {!HTMLElement} popupElement The popup element we are positioning.
- * @param {AnchorType} type The type of anchoring we want.
- * @param {boolean=} opt_invertLeftRight Whether to invert the right/left
- * alignment.
- */
-export function positionPopupAroundElement(
- anchorElement, popupElement, type, opt_invertLeftRight) {
- const anchorRect = anchorElement.getBoundingClientRect();
- positionPopupAroundRect(
- anchorRect, popupElement, type, !!opt_invertLeftRight);
-}
-
-/**
- * Positions a popup around a point.
- * @param {number} x The client x position.
- * @param {number} y The client y position.
- * @param {!HTMLElement} popupElement The popup element we are positioning.
- * @param {AnchorType=} opt_anchorType The type of anchoring we want.
- */
-export function positionPopupAtPoint(x, y, popupElement, opt_anchorType) {
- const rect = {left: x, top: y, width: 0, height: 0, right: x, bottom: y};
-
- const anchorType = opt_anchorType || AnchorType.BELOW;
- positionPopupAroundRect(rect, popupElement, anchorType);
-}
diff --git a/chromium/chrome/browser/resources/ntp4/tile_page.css b/chromium/chrome/browser/resources/ntp4/tile_page.css
deleted file mode 100644
index 531203f92da..00000000000
--- a/chromium/chrome/browser/resources/ntp4/tile_page.css
+++ /dev/null
@@ -1,189 +0,0 @@
-/* Copyright 2012 The Chromium Authors
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-.tile-page {
- -webkit-box-orient: vertical;
- display: -webkit-box;
- height: 100%;
- position: relative;
- width: 100%;
-}
-
-.tile-page-scrollbar {
- -webkit-box-sizing: border-box;
- margin: 0 4px;
- pointer-events: none;
- position: absolute;
- right: 0;
- width: 5px;
- z-index: 5;
-}
-
-.tile-page-content {
- -webkit-box-flex: 1;
- /* Don't apply clip mask to padding. */
- -webkit-mask-clip: content-box;
- /* TODO(estade): this mask is disabled for technical reasons. It negatively
- * impacts performance of page switching, also it causes problems with Mac
- * text: http://crbug.com/86955
- -webkit-mask-image: -webkit-linear-gradient(bottom, transparent, black 30px);
- */
- /* The following four properties are necessary so that the mask won't clip
- * the scrollbar. */
- box-sizing: border-box;
- overflow-y: scroll;
- /* Scrollbar width(13px) + balance right padding. */
- padding-left: 93px;
- padding-right: 80px;
- /* This value is mirrored in TilePage.updateTopMargin_ */
- padding-top: 60px;
- position: relative;
- text-align: center;
- width: 100%;
-}
-
-.top-margin {
- /* The only reason height is set to 1px, rather than left at 0, is that
- * otherwise webkit collapses the top and bottom margins. */
- height: 1px;
-}
-
-.tile-grid {
- position: relative;
- width: 100%;
-}
-
-.tile {
- -webkit-print-color-adjust: exact;
- /* Don't offer the context menu on long-press. */
- -webkit-touch-callout: none;
- -webkit-user-drag: element;
- display: inline-block;
- position: absolute;
-}
-
-/* NOTE: Dopplegangers nest themselves inside of other tiles, so don't
- * accidentally double apply font-size to them. */
-.tile:not(.doppleganger) {
- font-size: 1.2em;
-}
-
-/* Not real but not a doppleganger: show nothing. This state exists for a
- * webstore tile that's on the same page as a [+]. */
-.tile:not(.real):not(.doppleganger) {
- display: none;
-}
-
-/* I don't know why this is necessary. -webkit-user-drag: element on .tile
- * should be enough. If we don't do this, we get 2 drag representations for
- * the image. */
-.tile img {
- -webkit-user-drag: none;
-}
-
-.doppleganger {
- left: 0 !important;
- right: 0 !important;
- top: 0 !important;
-}
-
-.tile.dragging {
- opacity: 0;
-}
-
-.tile.drag-representation {
- pointer-events: none;
- position: fixed;
- transition: opacity 200ms;
- z-index: 3;
-}
-
-.tile.drag-representation.placing > * {
- transition: transform 200ms;
-}
-
-/* When a drag finishes while we're not showing the page where the tile
- * belongs, the tile shrinks to a dot. */
-.tile.drag-representation.dropped-on-other-page > * {
- transform: scale(0) rotate(0);
-}
-
-.tile.drag-representation.deleting > * {
- transform: scale(0) rotate(360deg);
- transition: transform 600ms;
-}
-
-.animating-tile-page .tile,
-.tile.drag-representation.placing {
- transition: left 200ms, right 200ms, top 200ms;
-}
-
-.hovering-on-trash {
- opacity: 0.6;
-}
-
-.animating-tile-page .top-margin {
- transition: margin-bottom 200ms;
-}
-
-@keyframes bounce {
- 0% {
- transform: scale(0, 0);
- }
-
- 60% {
- transform: scale(1.2, 1.2);
- }
-
- 100% {
- transform: scale(1, 1);
- }
-}
-
-.tile > .new-tile-contents {
- animation: bounce 500ms ease-in-out;
-}
-
-@keyframes blipout {
- 0% {
- transform: scale(1, 1);
- }
-
- 60% {
- animation-timing-function: ease-in;
- opacity: 1;
- transform: scale(1.3, 0.02);
- }
-
- 90% {
- opacity: 0.7;
- transform: scale(0.3, 0.02);
- }
-
- 100% {
- animation-timing-function: linear;
- opacity: 0;
- transform: scale(0.3, 0.02);
- }
-}
-
-.tile > .removing-tile-contents {
- animation: blipout 300ms;
- animation-fill-mode: forwards;
- pointer-events: none;
-}
-
-.tile-page:not(.selected-card) * {
- transition: none !important;
-}
-
-/** Scrollbars ****************************************************************/
-
-.tile-page-content::-webkit-scrollbar {
- width: 13px;
-}
-
-.tile-page-content::-webkit-scrollbar-button {
- display: none;
-}
diff --git a/chromium/chrome/browser/resources/ntp4/tile_page.js b/chromium/chrome/browser/resources/ntp4/tile_page.js
deleted file mode 100644
index 925cae22549..00000000000
--- a/chromium/chrome/browser/resources/ntp4/tile_page.js
+++ /dev/null
@@ -1,1359 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
-import {DragWrapper} from 'chrome://resources/js/drag_wrapper.js';
-import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {$, isRTL} from 'chrome://resources/js/util_ts.js';
-
-import {App} from './apps_page.js';
-import {dispatchSimpleEvent} from './cr_deprecated.js';
-import {enterRearrangeMode, getCardSlider, leaveRearrangeMode} from './new_tab.js';
-import {toCssPx} from './ui.js';
-import {findAncestorByClass} from './util.js';
-
-// We can't pass the currently dragging tile via dataTransfer because of
-// http://crbug.com/31037
-let currentlyDraggingTile = null;
-export function getCurrentlyDraggingTile() {
- return currentlyDraggingTile;
-}
-
-function setCurrentlyDraggingTile(tile) {
- currentlyDraggingTile = tile;
- if (tile) {
- enterRearrangeMode();
- } else {
- leaveRearrangeMode();
- }
-}
-
-/**
- * Changes the current dropEffect of a drag. This modifies the native cursor
- * and serves as an indicator of what we should do at the end of the drag as
- * well as give indication to the user if a drop would succeed if they let go.
- * @param {DataTransfer} dataTransfer A dataTransfer object from a drag event.
- * @param {string} effect A drop effect to change to (i.e. copy, move, none).
- */
-export function setCurrentDropEffect(dataTransfer, effect) {
- dataTransfer.dropEffect = effect;
- if (currentlyDraggingTile) {
- currentlyDraggingTile.lastDropEffect = dataTransfer.dropEffect;
- }
-}
-
-/**
- * Creates a new Tile object. Tiles wrap content on a TilePage, providing
- * some styling and drag functionality.
- * @constructor
- * @extends {HTMLDivElement}
- */
-function Tile(contents) {
- const tile = /** @type {!Tile} */ (document.createElement('div'));
- tile.__proto__ = Tile.prototype;
- tile.initialize(contents);
-
- return tile;
-}
-
-Tile.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- initialize(contents) {
- // 'real' as opposed to doppleganger.
- this.className = 'tile real';
- this.appendChild(contents);
- contents.tile = this;
-
- this.addEventListener('dragstart', this.onDragStart_);
- this.addEventListener('drag', this.onDragMove_);
- this.addEventListener('dragend', this.onDragEnd_);
-
- this.firstChild.addEventListener(
- 'animationend', this.onContentsAnimationEnd_.bind(this));
-
- this.eventTracker = new EventTracker();
- },
-
- get index() {
- return Array.prototype.indexOf.call(this.tilePage.tileElements_, this);
- },
-
- get tilePage() {
- return findAncestorByClass(this, 'tile-page');
- },
-
- /**
- * Position the tile at |x, y|, and store this as the grid location, i.e.
- * where the tile 'belongs' when it's not being dragged.
- * @param {number} x The x coordinate, in pixels.
- * @param {number} y The y coordinate, in pixels.
- */
- setGridPosition(x, y) {
- this.gridX = x;
- this.gridY = y;
- this.moveTo(x, y);
- },
-
- /**
- * Position the tile at |x, y|.
- * @param {number} x The x coordinate, in pixels.
- * @param {number} y The y coordinate, in pixels.
- */
- moveTo(x, y) {
- // left overrides right in LTR, and right takes precedence in RTL.
- this.style.left = toCssPx(x);
- this.style.right = toCssPx(x);
- this.style.top = toCssPx(y);
- },
-
- /**
- * The handler for dragstart events fired on |this|.
- * @param {Event} e The event for the drag.
- * @private
- */
- onDragStart_(e) {
- // The user may start dragging again during a previous drag's finishing
- // animation.
- if (this.classList.contains('dragging')) {
- this.finalizeDrag_();
- }
-
- setCurrentlyDraggingTile(this);
-
- e.dataTransfer.effectAllowed = 'copyMove';
- /** @type {!App} */ (this.firstChild).setDragData(e.dataTransfer);
-
- // The drag clone is the node we use as a representation during the drag.
- // It's attached to the top level document element so that it floats above
- // image masks.
- this.dragClone = this.cloneNode(true);
- this.dragClone.style.right = '';
- this.dragClone.classList.add('drag-representation');
- $('card-slider-frame').appendChild(this.dragClone);
- this.eventTracker.add(
- this.dragClone, 'transitionend',
- this.onDragCloneTransitionEnd_.bind(this));
-
- this.classList.add('dragging');
- // offsetLeft is mirrored in RTL. Un-mirror it.
- const offsetLeft = isRTL() ? this.parentNode.clientWidth - this.offsetLeft :
- this.offsetLeft;
- this.dragOffsetX = e.x - offsetLeft - this.parentNode.offsetLeft;
- this.dragOffsetY = e.y - this.offsetTop -
- // Unlike offsetTop, this value takes scroll position into account.
- this.parentNode.getBoundingClientRect().top;
-
- this.onDragMove_(e);
- },
-
- /**
- * The handler for drag events fired on |this|.
- * @param {Event} e The event for the drag.
- * @private
- */
- onDragMove_(e) {
- if (e.view !== window || (e.x === 0 && e.y === 0)) {
- this.dragClone.hidden = true;
- return;
- }
-
- this.dragClone.hidden = false;
- this.dragClone.style.left = toCssPx(e.x - this.dragOffsetX);
- this.dragClone.style.top = toCssPx(e.y - this.dragOffsetY);
- },
-
- /**
- * The handler for dragend events fired on |this|.
- * @param {Event} e The event for the drag.
- * @private
- */
- onDragEnd_(e) {
- this.dragClone.hidden = false;
- this.dragClone.classList.add('placing');
-
- setCurrentlyDraggingTile(null);
-
- // tilePage will be null if we've already been removed.
- const tilePage = this.tilePage;
- if (tilePage) {
- tilePage.positionTile_(this.index);
- }
-
- // Take an appropriate action with the drag clone.
- if (this.landedOnTrash) {
- this.dragClone.classList.add('deleting');
- } else if (tilePage) {
- // TODO(dbeam): Until we fix dropEffect to the correct behavior it will
- // differ on windows - crbug.com/39399. That's why we use the custom
- // this.lastDropEffect instead of e.dataTransfer.dropEffect.
- if (tilePage.selected && this.lastDropEffect !== 'copy') {
- // The drag clone can still be hidden from the last drag move event.
- this.dragClone.hidden = false;
- // The tile's contents may have moved following the respositioning;
- // adjust for that.
- const contentDiffX =
- this.dragClone.firstChild.offsetLeft - this.firstChild.offsetLeft;
- const contentDiffY =
- this.dragClone.firstChild.offsetTop - this.firstChild.offsetTop;
- this.dragClone.style.left =
- toCssPx(this.gridX + this.parentNode.offsetLeft - contentDiffX);
- this.dragClone.style.top = toCssPx(
- this.gridY + this.parentNode.getBoundingClientRect().top -
- contentDiffY);
- } else if (this.dragClone.hidden) {
- this.finalizeDrag_();
- } else {
- // The CSS3 transitions spec intentionally leaves it up to individual
- // user agents to determine when styles should be applied. On some
- // platforms (at the moment, Windows), when you apply both classes
- // immediately a transition may not occur correctly. That's why we're
- // using a setTimeout here to queue adding the class until the
- // previous class (currently: .placing) sets up a transition.
- // http://dev.w3.org/csswg/css3-transitions/#starting
- window.setTimeout(function() {
- if (this.dragClone) {
- this.dragClone.classList.add('dropped-on-other-page');
- }
- }.bind(this), 0);
- }
- }
-
- delete this.lastDropEffect;
- this.landedOnTrash = false;
- },
-
- /**
- * Creates a clone of this node offset by the coordinates. Used for the
- * dragging effect where a tile appears to float off one side of the grid
- * and re-appear on the other.
- * @param {number} x x-axis offset, in pixels.
- * @param {number} y y-axis offset, in pixels.
- */
- showDoppleganger(x, y) {
- // We always have to clear the previous doppleganger to make sure we get
- // style updates for the contents of this tile.
- this.clearDoppleganger();
-
- const clone = this.cloneNode(true);
- clone.classList.remove('real');
- clone.classList.add('doppleganger');
- const clonelets = clone.querySelectorAll('.real');
- for (let i = 0; i < clonelets.length; i++) {
- clonelets[i].classList.remove('real');
- }
-
- this.appendChild(clone);
- this.doppleganger_ = clone;
-
- if (isRTL()) {
- x *= -1;
- }
-
- this.doppleganger_.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
- },
-
- /**
- * Destroys the current doppleganger.
- */
- clearDoppleganger() {
- if (this.doppleganger_) {
- this.removeChild(this.doppleganger_);
- this.doppleganger_ = null;
- }
- },
-
- /**
- * Returns status of doppleganger.
- * @return {boolean} True if there is a doppleganger showing for |this|.
- */
- hasDoppleganger() {
- return !!this.doppleganger_;
- },
-
- /**
- * Cleans up after the drag is over. This is either called when the
- * drag representation finishes animating to the final position, or when
- * the next drag starts (if the user starts a 2nd drag very quickly).
- * @private
- */
- finalizeDrag_() {
- assert(this.classList.contains('dragging'));
-
- const clone = this.dragClone;
- this.dragClone = null;
-
- clone.parentNode.removeChild(clone);
- this.eventTracker.remove(clone, 'transitionend');
- this.classList.remove('dragging');
- if (this.firstChild.finalizeDrag) {
- this.firstChild.finalizeDrag();
- }
- },
-
- /**
- * Called when the drag representation node is done migrating to its final
- * resting spot.
- * @param {Event} e The transition end event.
- */
- onDragCloneTransitionEnd_(e) {
- if (this.classList.contains('dragging') &&
- (e.propertyName === 'left' || e.propertyName === 'top' ||
- e.propertyName === 'transform')) {
- this.finalizeDrag_();
- }
- },
-
- /**
- * Called when an app is removed from Chrome. Animates its disappearance.
- * @param {boolean=} opt_animate Whether the animation should be animated.
- */
- doRemove(opt_animate) {
- if (opt_animate) {
- this.firstChild.classList.add('removing-tile-contents');
- } else {
- this.tilePage.removeTile(this, false);
- }
- },
-
- /**
- * Callback for the animationend event on the tile's contents.
- * @param {Event} e The event object.
- */
- onContentsAnimationEnd_(e) {
- if (this.firstChild.classList.contains('new-tile-contents')) {
- this.firstChild.classList.remove('new-tile-contents');
- }
- if (this.firstChild.classList.contains('removing-tile-contents')) {
- this.tilePage.removeTile(this, true);
- }
- },
-};
-
-/**
- * Gives the proportion of the row width that is devoted to a single icon.
- * @param {number} rowTileCount The number of tiles in a row.
- * @param {number} tileSpacingFraction The proportion of the tile width which
- * will be used as spacing between tiles.
- * @return {number} The ratio between icon width and row width.
- */
-function tileWidthFraction(rowTileCount, tileSpacingFraction) {
- return rowTileCount + (rowTileCount - 1) * tileSpacingFraction;
-}
-
-/**
- * Calculates an assortment of tile-related values for a grid with the
- * given dimensions.
- * @param {number} width The pixel width of the grid.
- * @param {number} numRowTiles The number of tiles in a row.
- * @param {number} tileSpacingFraction The proportion of the tile width which
- * will be used as spacing between tiles.
- * @return {Object} A mapping of pixel values.
- */
-function tileValuesForGrid(width, numRowTiles, tileSpacingFraction) {
- const tileWidth = width / tileWidthFraction(numRowTiles, tileSpacingFraction);
- const offsetX = tileWidth * (1 + tileSpacingFraction);
- const interTileSpacing = offsetX - tileWidth;
-
- return {
- tileWidth: tileWidth,
- offsetX: offsetX,
- interTileSpacing: interTileSpacing,
- };
-}
-
-// The smallest amount of horizontal blank space to display on the sides when
-// displaying a wide arrangement. There is an additional 26px of margin from
-// the tile page padding.
-const MIN_WIDE_MARGIN = 18;
-
-/**
- * Creates a new TilePage object. This object contains tiles and controls
- * their layout.
- * @param {Object} gridValues Pixel values that define the size and layout
- * of the tile grid.
- * @constructor
- * @extends {HTMLDivElement}
- * @implements {DragWrapperDelegate}
- */
-export function TilePage(gridValues) {
- const el = /** @type {!TilePage} */ (document.createElement('div'));
- el.gridValues_ = gridValues;
- el.__proto__ = TilePage.prototype;
- el.initialize();
-
- return el;
-}
-
-/**
- * @typedef {{
- * minColCount: number,
- * maxColCount: number,
- * minTileWidth: number,
- * maxTileWidth: number,
- * tileSpacingFraction: number
- * }}
- */
-let GridValues;
-
-/**
- * Takes a collection of grid layout pixel values and updates them with
- * additional tiling values that are calculated from TilePage constants.
- * @param {!GridValues} grid The grid layout pixel values to update.
- */
-TilePage.initGridValues = function(grid) {
- // The amount of space we need to display a narrow grid (all narrow grids
- // are this size).
- grid.narrowWidth = grid.minTileWidth *
- tileWidthFraction(grid.minColCount, grid.tileSpacingFraction);
- // The minimum amount of space we need to display a wide grid.
- grid.minWideWidth = grid.minTileWidth *
- tileWidthFraction(grid.maxColCount, grid.tileSpacingFraction);
- // The largest we will ever display a wide grid.
- grid.maxWideWidth = grid.maxTileWidth *
- tileWidthFraction(grid.maxColCount, grid.tileSpacingFraction);
- // Tile-related pixel values for the narrow display.
- grid.narrowTileValues = tileValuesForGrid(
- grid.narrowWidth, grid.minColCount, grid.tileSpacingFraction);
- // Tile-related pixel values for the minimum narrow display.
- grid.wideTileValues = tileValuesForGrid(
- grid.minWideWidth, grid.maxColCount, grid.tileSpacingFraction);
-};
-
-TilePage.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- initialize() {
- this.className = 'tile-page';
-
- // Div that acts as a custom scrollbar. The scrollbar has to live
- // outside the content div so it doesn't flicker when scrolling (due to
- // repainting after the scroll, then repainting again when moved in the
- // onScroll handler). |scrollbar_| is only aesthetic, and it only
- // represents the thumb. Actual events are still handled by the invisible
- // native scrollbars. This div gives us more flexibility with the visuals.
- this.scrollbar_ = this.ownerDocument.createElement('div');
- this.scrollbar_.className = 'tile-page-scrollbar';
- this.scrollbar_.hidden = true;
- this.appendChild(this.scrollbar_);
-
- // This contains everything but the scrollbar.
- this.content_ = this.ownerDocument.createElement('div');
- this.content_.className = 'tile-page-content';
- this.appendChild(this.content_);
-
- // Div that sets the vertical position of the tile grid.
- this.topMargin_ = this.ownerDocument.createElement('div');
- this.topMargin_.className = 'top-margin';
- this.content_.appendChild(this.topMargin_);
-
- // Div that holds the tiles.
- this.tileGrid_ = this.ownerDocument.createElement('div');
- this.tileGrid_.className = 'tile-grid';
- this.tileGrid_.style.minWidth = this.gridValues_.narrowWidth + 'px';
- this.tileGrid_.setAttribute('role', 'menu');
- this.tileGrid_.setAttribute(
- 'aria-label',
- loadTimeData.getString(
- 'tile_grid_screenreader_accessible_description'));
-
- this.content_.appendChild(this.tileGrid_);
-
- // Ordered list of our tiles.
- this.tileElements_ = this.tileGrid_.getElementsByClassName('tile real');
- // Ordered list of the elements which want to accept keyboard focus. These
- // elements will not be a part of the normal tab order; the tile grid
- // initially gets focused and then these elements can be focused via the
- // arrow keys.
- this.focusableElements_ =
- this.tileGrid_.getElementsByClassName('focusable');
-
- // These are properties used in updateTopMargin.
- this.animatedTopMarginPx_ = 0;
- this.topMarginPx_ = 0;
-
- this.eventTracker = new EventTracker();
- this.eventTracker.add(window, 'resize', this.onResize_.bind(this));
-
- this.addEventListener(
- 'DOMNodeInsertedIntoDocument', this.onNodeInsertedIntoDocument_);
-
- this.content_.addEventListener('scroll', this.onScroll.bind(this));
-
- this.dragWrapper_ = new DragWrapper(this.tileGrid_, this);
-
- this.addEventListener('cardselected', this.handleCardSelection_);
- this.addEventListener('carddeselected', this.handleCardDeselection_);
- this.addEventListener('focus', this.handleFocus_);
- this.addEventListener('keydown', this.handleKeyDown_);
- this.addEventListener('mousedown', this.handleMouseDown_);
-
- this.focusElementIndex_ = -1;
- },
-
- get tiles() {
- return this.tileElements_;
- },
-
- get tileCount() {
- return this.tileElements_.length;
- },
-
- get selected() {
- return Array.prototype.indexOf.call(this.parentNode.children, this) ===
- getCardSlider().currentCard;
- },
-
- /**
- * The size of the margin (unused space) on the sides of the tile grid, in
- * pixels.
- * @type {number}
- */
- get sideMargin() {
- return this.layoutValues_.leftMargin;
- },
-
- /**
- * Returns the width of the scrollbar, in pixels, if it is active, or 0
- * otherwise.
- * @type {number}
- */
- get scrollbarWidth() {
- return this.scrollbar_.hidden ? 0 : 13;
- },
-
- /**
- * Fetches the size, in pixels, of the padding-top of the tile contents.
- * @type {number}
- */
- get contentPadding() {
- if (typeof this.contentPadding_ === 'undefined') {
- this.contentPadding_ =
- parseInt(window.getComputedStyle(this.content_).paddingTop, 10);
- }
- return this.contentPadding_;
- },
-
- /**
- * Removes the tilePage from the DOM and cleans up event handlers.
- *
- * TODO(dbeam): this method now conflicts with HTMLElement#remove(). Rename.
- */
- remove() {
- // This checks arguments.length as most remove functions have a boolean
- // |opt_animate| argument, but that's not necesarilly applicable to
- // removing a tilePage. Selecting a different card in an animated way and
- // deleting the card afterward is probably a better choice.
- assert(
- typeof arguments[0] !== 'boolean',
- 'This function takes no |opt_animate| argument.');
- this.tearDown_();
- this.parentNode.removeChild(this);
- },
-
- /**
- * Cleans up resources that are no longer needed after this TilePage
- * instance is removed from the DOM.
- * @private
- */
- tearDown_() {
- this.eventTracker.removeAll();
- },
-
- /**
- * Appends a tile to the end of the tile grid.
- * @param {HTMLElement} tileElement The contents of the tile.
- * @param {boolean} animate If true, the append will be animated.
- * @protected
- */
- appendTile(tileElement, animate) {
- this.addTileAt(tileElement, this.tileElements_.length, animate);
- },
-
- /**
- * Adds the given element to the tile grid.
- * @param {Node} tileElement The tile object/node to insert.
- * @param {number} index The location in the tile grid to insert it at.
- * @param {boolean} animate If true, the tile in question will be
- * animated (other tiles, if they must reposition, do not animate).
- * @protected
- */
- addTileAt(tileElement, index, animate) {
- this.classList.remove('animating-tile-page');
- if (animate) {
- tileElement.classList.add('new-tile-contents');
- }
-
- // Make sure the index is positive and either in the the bounds of
- // this.tileElements_ or at the end (meaning append).
- assert(index >= 0 && index <= this.tileElements_.length);
-
- const wrapperDiv = new Tile(tileElement);
- // If is out of the bounds of the tile element list, .insertBefore() will
- // act just like appendChild().
- this.tileGrid_.insertBefore(wrapperDiv, this.tileElements_[index]);
- this.calculateLayoutValues_();
- this.heightChanged_();
-
- this.repositionTiles_();
-
- // If this is the first tile being added, make it focusable after add.
- if (this.focusableElements_.length === 1) {
- this.updateFocusableElement();
- }
- this.fireAddedEvent(wrapperDiv, index, animate);
- },
-
- /**
- * Notify interested subscribers that a tile has been removed from this
- * page.
- * @param {Tile} tile The newly added tile.
- * @param {number} index The index of the tile that was added.
- * @param {boolean} wasAnimated Whether the removal was animated.
- */
- fireAddedEvent(tile, index, wasAnimated) {
- const e = document.createEvent('Event');
- e.initEvent('tilePage:tile_added', true, true);
- e.addedIndex = index;
- e.addedTile = tile;
- e.wasAnimated = wasAnimated;
- this.dispatchEvent(e);
- },
-
- /**
- * Removes the given tile and animates the repositioning of the other tiles.
- * @param {boolean=} opt_animate Whether the removal should be animated.
- * @param {boolean=} opt_dontNotify Whether a page should be removed if the
- * last tile is removed from it.
- */
- removeTile(tile, opt_animate, opt_dontNotify) {
- if (opt_animate) {
- this.classList.add('animating-tile-page');
- }
-
- const index = tile.index;
- tile.parentNode.removeChild(tile);
- this.calculateLayoutValues_();
- this.cleanupDrag();
- this.updateFocusableElement();
-
- if (!opt_dontNotify) {
- this.fireRemovedEvent(tile, index, !!opt_animate);
- }
- },
-
- /**
- * Notify interested subscribers that a tile has been removed from this
- * page.
- * @param {Tile} tile The tile that was removed.
- * @param {number} oldIndex Where the tile was positioned before removal.
- * @param {boolean} wasAnimated Whether the removal was animated.
- */
- fireRemovedEvent(tile, oldIndex, wasAnimated) {
- const e = document.createEvent('Event');
- e.initEvent('tilePage:tile_removed', true, true);
- e.removedIndex = oldIndex;
- e.removedTile = tile;
- e.wasAnimated = wasAnimated;
- this.dispatchEvent(e);
- },
-
- /**
- * Removes all tiles from the page.
- */
- removeAllTiles() {
- this.tileGrid_.innerHTML = trustedTypes.emptyHTML;
- },
-
- /**
- * Called when the page is selected (in the card selector).
- * @param {Event} e A custom cardselected event.
- * @private
- */
- handleCardSelection_(e) {
- this.updateFocusableElement();
-
- // When we are selected, we re-calculate the layout values. (See comment
- // in doDrop.)
- this.calculateLayoutValues_();
- },
-
- /**
- * Called when the page loses selection (in the card selector).
- * @param {Event} e A custom carddeselected event.
- * @private
- */
- handleCardDeselection_(e) {
- if (this.currentFocusElement_) {
- this.currentFocusElement_.tabIndex = -1;
- }
- },
-
- /**
- * When we get focus, pass it on to the focus element.
- * @param {Event} e The focus event.
- * @private
- */
- handleFocus_(e) {
- if (this.focusableElements_.length === 0) {
- return;
- }
-
- this.updateFocusElement_();
- },
-
- /**
- * Since we are doing custom focus handling, we have to manually
- * set focusability on click (as well as keyboard nav above).
- * @param {Event} e The focus event.
- * @private
- */
- handleMouseDown_(e) {
- const focusable =
- findAncestorByClass(/** @type {Element} */ (e.target), 'focusable');
- if (focusable) {
- this.focusElementIndex_ =
- Array.prototype.indexOf.call(this.focusableElements_, focusable);
- this.updateFocusElement_();
- }
- },
-
- /**
- * Handle arrow key focus nav.
- * @param {Event} e The focus event.
- * @private
- */
- handleKeyDown_(e) {
- // We only handle up, down, left, right without control keys.
- if (e.metaKey || e.shiftKey || e.altKey || e.ctrlKey) {
- return;
- }
-
- // Wrap the given index to |this.focusableElements_|.
- const wrap = function(idx) {
- return (idx + this.focusableElements_.length) %
- this.focusableElements_.length;
- }.bind(this);
-
- let direction;
- switch (e.key) {
- case 'ArrowRight':
- case 'ArrowLeft':
- direction = e.key === 'ArrowRight' ? 1 : -1;
- this.focusElementIndex_ = wrap(this.focusElementIndex_ + direction);
- break;
- case 'ArrowUp':
- case 'ArrowDown':
- // Look through all focusable elements. Find the first one that is
- // in the same column.
- direction = e.key === 'ArrowUp' ? -1 : 1;
- const currentIndex = Array.prototype.indexOf.call(
- this.focusableElements_, this.currentFocusElement_);
- let newFocusIdx = wrap(currentIndex + direction);
- const tile = this.currentFocusElement_.parentNode;
- for (;; newFocusIdx = wrap(newFocusIdx + direction)) {
- const newTile = this.focusableElements_[newFocusIdx].parentNode;
- const rowTiles = this.layoutValues_.numRowTiles;
- if ((newTile.index - tile.index) % rowTiles === 0) {
- break;
- }
- }
-
- this.focusElementIndex_ = newFocusIdx;
- break;
-
- default:
- return;
- }
-
- this.updateFocusElement_();
-
- e.preventDefault();
- e.stopPropagation();
- },
-
- /**
- * Ensure 0 <= this.focusElementIndex_ < this.focusableElements_.length,
- * make the focusable element at this.focusElementIndex_ (if any) eligible
- * for tab focus, and the previously-focused element not eligible.
- * @protected
- */
- updateFocusableElement() {
- if (this.focusableElements_.length === 0 || !this.selected) {
- this.focusElementIndex_ = -1;
- return;
- }
-
- this.focusElementIndex_ =
- Math.min(this.focusableElements_.length - 1, this.focusElementIndex_);
- this.focusElementIndex_ = Math.max(0, this.focusElementIndex_);
-
- const newFocusElement = this.focusableElements_[this.focusElementIndex_];
- const lastFocusElement = this.currentFocusElement_;
- if (lastFocusElement && lastFocusElement !== newFocusElement) {
- lastFocusElement.tabIndex = -1;
- }
-
- newFocusElement.tabIndex = 1;
- },
-
- /**
- * Focuses the element at |this.focusElementIndex_|. Makes the previous
- * focus element, if any, no longer eligible for tab focus.
- * @private
- */
- updateFocusElement_() {
- this.updateFocusableElement();
- if (this.focusElementIndex_ >= 0) {
- this.focusableElements_[this.focusElementIndex_].focus();
- }
- },
-
- /**
- * The current focus element is that element which is eligible for focus.
- * @type {HTMLElement} The node.
- * @private
- */
- get currentFocusElement_() {
- return this.querySelector('.focusable[tabindex="1"]');
- },
-
- /**
- * Makes some calculations for tile layout. These change depending on
- * height, width, and the number of tiles.
- * TODO(estade): optimize calls to this function. Do nothing if the page is
- * hidden, but call before being shown.
- * @private
- */
- calculateLayoutValues_() {
- const grid = this.gridValues_;
- const availableSpace = this.tileGrid_.clientWidth - 2 * MIN_WIDE_MARGIN;
- const wide = availableSpace >= grid.minWideWidth;
- const numRowTiles = wide ? grid.maxColCount : grid.minColCount;
-
- const effectiveGridWidth = wide ?
- Math.min(
- Math.max(availableSpace, grid.minWideWidth), grid.maxWideWidth) :
- grid.narrowWidth;
- const realTileValues = tileValuesForGrid(
- effectiveGridWidth, numRowTiles, grid.tileSpacingFraction);
-
- // leftMargin centers the grid within the avaiable space.
- const minMargin = wide ? MIN_WIDE_MARGIN : 0;
- const leftMargin = Math.max(
- minMargin, (this.tileGrid_.clientWidth - effectiveGridWidth) / 2);
-
- const rowHeight = this.heightForWidth(realTileValues.tileWidth) +
- realTileValues.interTileSpacing;
-
- this.layoutValues_ = {
- colWidth: realTileValues.offsetX,
- gridWidth: effectiveGridWidth,
- leftMargin: leftMargin,
- numRowTiles: numRowTiles,
- rowHeight: rowHeight,
- tileWidth: realTileValues.tileWidth,
- wide: wide,
- };
-
- // We need to update the top margin as well.
- this.updateTopMargin_();
-
- this.firePageLayoutEvent_();
- },
-
- /**
- * Dispatches the custom pagelayout event.
- * @private
- */
- firePageLayoutEvent_() {
- dispatchSimpleEvent(this, 'pagelayout', true, true);
- },
-
- /**
- * @return {number} The amount of margin that should be animated (in pixels)
- * for the current grid layout.
- */
- getAnimatedLeftMargin_() {
- if (this.layoutValues_.wide) {
- return 0;
- }
-
- const grid = this.gridValues_;
- return (grid.minWideWidth - MIN_WIDE_MARGIN - grid.narrowWidth) / 2;
- },
-
- /**
- * Calculates the x/y coordinates for an element and moves it there.
- * @param {number} index The index of the element to be positioned.
- * @param {number=} opt_indexOffset If provided, this is added to |index|
- * when positioning the tile. The effect is that the tile will be
- * positioned in a non-default location.
- * @private
- */
- positionTile_(index, opt_indexOffset) {
- const grid = this.gridValues_;
- const layout = this.layoutValues_;
-
- const indexOffset = opt_indexOffset || 0;
- // Add the offset _after_ the modulus division. We might want to show the
- // tile off the side of the grid.
- const col = index % layout.numRowTiles + indexOffset;
- const row = Math.floor(index / layout.numRowTiles);
- // Calculate the final on-screen position for the tile.
- const realX = col * layout.colWidth + layout.leftMargin;
- const realY = row * layout.rowHeight;
-
- // Calculate the portion of the tile's position that should be animated.
- const animatedTileValues =
- layout.wide ? grid.wideTileValues : grid.narrowTileValues;
- // Animate the difference between three-wide and six-wide.
- const animatedLeftMargin = this.getAnimatedLeftMargin_();
- const animatedX = col * animatedTileValues.offsetX + animatedLeftMargin;
- const animatedY = row *
- (this.heightForWidth(animatedTileValues.tileWidth) +
- animatedTileValues.interTileSpacing);
-
- const tile = this.tileElements_[index];
- tile.setGridPosition(animatedX, animatedY);
- tile.firstChild.setBounds(
- layout.tileWidth, realX - animatedX, realY - animatedY);
-
- // This code calculates whether the tile needs to show a clone of itself
- // wrapped around the other side of the tile grid.
- const offTheRight = col === layout.numRowTiles ||
- (col === layout.numRowTiles - 1 && tile.hasDoppleganger());
- const offTheLeft = col === -1 || (col === 0 && tile.hasDoppleganger());
- if (this.isCurrentDragTarget && (offTheRight || offTheLeft)) {
- const sign = offTheRight ? 1 : -1;
- tile.showDoppleganger(
- -layout.numRowTiles * layout.colWidth * sign,
- layout.rowHeight * sign);
- } else {
- tile.clearDoppleganger();
- }
-
- if (index === this.tileElements_.length - 1) {
- this.tileGrid_.style.height = (realY + layout.rowHeight) + 'px';
- this.queueUpdateScrollbars_();
- }
- },
-
- /**
- * Gets the index of the tile that should occupy coordinate (x, y). Note
- * that this function doesn't care where the tiles actually are, and will
- * return an index even for the space between two tiles. This function is
- * effectively the inverse of |positionTile_|.
- * @param {number} x The x coordinate, in pixels, relative to the left of
- * |this|.
- * @param {number} y The y coordinate, in pixels, relative to the top of
- * |this|.
- * @return {number}
- * @private
- */
- getWouldBeIndexForPoint_(x, y) {
- const grid = this.gridValues_;
- const layout = this.layoutValues_;
-
- const gridClientRect = this.tileGrid_.getBoundingClientRect();
- let col = Math.floor(
- (x - gridClientRect.left - layout.leftMargin) / layout.colWidth);
- if (col < 0 || col >= layout.numRowTiles) {
- return -1;
- }
-
- if (isRTL()) {
- col = layout.numRowTiles - 1 - col;
- }
-
- const row = Math.floor((y - gridClientRect.top) / layout.rowHeight);
- return row * layout.numRowTiles + col;
- },
-
- /**
- * Window resize event handler. Window resizes may trigger re-layouts.
- * @param {Object} e The resize event.
- */
- onResize_(e) {
- if (this.lastWidth_ === this.clientWidth &&
- this.lastHeight_ === this.clientHeight) {
- return;
- }
-
- this.calculateLayoutValues_();
-
- this.lastWidth_ = this.clientWidth;
- this.lastHeight_ = this.clientHeight;
- this.classList.add('animating-tile-page');
- this.heightChanged_();
-
- this.repositionTiles_();
- },
-
- /**
- * The tile grid has an image mask which fades at the edges. We only show
- * the mask when there is an active drag; it obscures doppleganger tiles
- * as they enter or exit the grid.
- * @private
- */
- updateMask_() {
- if (!this.isCurrentDragTarget) {
- this.tileGrid_.style.WebkitMaskBoxImage = '';
- return;
- }
-
- const leftMargin = this.layoutValues_.leftMargin;
- // The fade distance is the space between tiles.
- let fadeDistance =
- (this.gridValues_.tileSpacingFraction * this.layoutValues_.tileWidth);
- fadeDistance = Math.min(leftMargin, fadeDistance);
- // On Skia we don't use any fade because it works very poorly. See
- // http://crbug.com/99373
- // <if expr="not is_macosx">
- fadeDistance = 1;
- // </if>
- const gradient = '-webkit-linear-gradient(left,' +
- 'transparent, ' +
- 'transparent ' + (leftMargin - fadeDistance) + 'px, ' +
- 'black ' + leftMargin + 'px, ' +
- 'black ' + (this.tileGrid_.clientWidth - leftMargin) + 'px, ' +
- 'transparent ' +
- (this.tileGrid_.clientWidth - leftMargin + fadeDistance) + 'px, ' +
- 'transparent)';
- this.tileGrid_.style.WebkitMaskBoxImage = gradient;
- },
-
- updateTopMargin_() {
- const layout = this.layoutValues_;
-
- // The top margin is set so that the vertical midpoint of the grid will
- // be 1/3 down the page.
- const numTiles = this.tileCount +
- (this.isCurrentDragTarget && !this.withinPageDrag_ ? 1 : 0);
- const numRows = Math.max(1, Math.ceil(numTiles / layout.numRowTiles));
- const usedHeight = layout.rowHeight * numRows;
- let newMargin = document.documentElement.clientHeight / 3 - usedHeight / 3 -
- this.contentPadding;
- // The 'height' style attribute of topMargin is non-zero to work around
- // webkit's collapsing margin behavior, so we have to factor that into
- // our calculations here.
- newMargin = Math.max(newMargin, 0) - this.topMargin_.offsetHeight;
-
- // |newMargin| is the final margin we actually want to show. However,
- // part of that should be animated and part should not (for the same
- // reason as with leftMargin). The approach is to consider differences
- // when the layout changes from wide to narrow or vice versa as
- // 'animatable'. These differences accumulate in animatedTopMarginPx_,
- // while topMarginPx_ caches the real (total) margin. Either of these
- // calculations may come out to be negative, so we use margins as the
- // css property.
-
- if (typeof this.topMarginIsForWide_ === 'undefined') {
- this.topMarginIsForWide_ = layout.wide;
- }
- if (this.topMarginIsForWide_ !== layout.wide) {
- this.animatedTopMarginPx_ += newMargin - this.topMarginPx_;
- this.topMargin_.style.marginBottom = toCssPx(this.animatedTopMarginPx_);
- }
-
- this.topMarginIsForWide_ = layout.wide;
- this.topMarginPx_ = newMargin;
- this.topMargin_.style.marginTop =
- toCssPx(this.topMarginPx_ - this.animatedTopMarginPx_);
- },
-
- /**
- * Handles final setup that can only happen after |this| is inserted into
- * the page.
- * @private
- */
- onNodeInsertedIntoDocument_(e) {
- this.calculateLayoutValues_();
- this.heightChanged_();
- },
-
- /**
- * Called when the height of |this| has changed: update the size of
- * tileGrid.
- * @private
- */
- heightChanged_() {
- // The tile grid will expand to the bottom footer, or enough to hold all
- // the tiles, whichever is greater. It would be nicer if tilePage were
- // a flex box, and the tile grid could be box-flex: 1, but this exposes a
- // bug where repositioning tiles will cause the scroll position to reset.
- this.tileGrid_.style.minHeight = this.clientHeight -
- this.tileGrid_.offsetTop - this.content_.offsetTop + 'px';
- },
-
- /**
- * Scrolls the page in response to an mousewheel event, although the event
- * may have been triggered on a different element. Return true if the
- * event triggered scrolling, and false otherwise.
- * This is called explicitly, which allows a consistent experience whether
- * the user scrolls on the page or on the page switcher, because this
- * function provides a common conversion factor between wheel delta and
- * scroll delta.
- * @param {Event} e The mousewheel event.
- */
- handleMouseWheel(e) {
- // The ctrl-wheel should triggle the zoom in/out actions in Chromium for
- // all pages.
- if (e.wheelDeltaY === 0 || e.ctrlKey) {
- return false;
- }
-
- this.content_.scrollTop -= e.wheelDeltaY / 3;
- return true;
- },
-
- /**
- * Handler for the 'scroll' event on |content_|.
- * @param {Event} e The scroll event.
- * @protected
- */
- onScroll(e) {
- this.queueUpdateScrollbars_();
- },
-
- /**
- * ID of scrollbar update timer. If 0, there's no scrollbar re-calc queued.
- * @private
- */
- scrollbarUpdate_: 0,
-
- /**
- * Queues an update on the custom scrollbar. Used for two reasons: first,
- * coalescing of multiple updates, and second, because action like
- * repositioning a tile can require a delay before they affect values
- * like clientHeight.
- * @private
- */
- queueUpdateScrollbars_() {
- if (this.scrollbarUpdate_) {
- return;
- }
-
- this.scrollbarUpdate_ =
- window.setTimeout(this.doUpdateScrollbars_.bind(this), 0);
- },
-
- /**
- * Does the work of calculating the visibility, height and position of the
- * scrollbar thumb (there is no track or buttons).
- * @private
- */
- doUpdateScrollbars_() {
- this.scrollbarUpdate_ = 0;
-
- const content = this.content_;
-
- // Adjust scroll-height to account for possible header-bar.
- const adjustedScrollHeight = content.scrollHeight - content.offsetTop;
-
- if (adjustedScrollHeight <= content.clientHeight) {
- this.scrollbar_.hidden = true;
- return;
- } else {
- this.scrollbar_.hidden = false;
- }
-
- const thumbTop = content.offsetTop +
- content.scrollTop / adjustedScrollHeight * content.clientHeight;
- const thumbHeight =
- content.clientHeight / adjustedScrollHeight * this.clientHeight;
-
- this.scrollbar_.style.top = thumbTop + 'px';
- this.scrollbar_.style.height = thumbHeight + 'px';
- this.firePageLayoutEvent_();
- },
-
- /**
- * Get the height for a tile of a certain width. Override this function to
- * get non-square tiles.
- * @param {number} width The pixel width of a tile.
- * @return {number} The height for |width|.
- */
- heightForWidth(width) {
- return width;
- },
-
- /** Dragging **/
-
- get isCurrentDragTarget() {
- return this.dragWrapper_.isCurrentDragTarget;
- },
-
- /** @override */
- doDragLeave(e) {
- this.cleanupDrag();
- },
-
- /** @override */
- doDragEnter(e) {
- // Applies the mask so doppleganger tiles disappear into the fog.
- this.updateMask_();
-
- this.classList.add('animating-tile-page');
- this.withinPageDrag_ = this.contains(currentlyDraggingTile);
- this.dragItemIndex_ = this.withinPageDrag_ ? currentlyDraggingTile.index :
- this.tileElements_.length;
- this.currentDropIndex_ = this.dragItemIndex_;
-
- // The new tile may change the number of rows, hence the top margin
- // will change.
- if (!this.withinPageDrag_) {
- this.updateTopMargin_();
- }
-
- this.doDragOver(e);
- },
-
- /** @override */
- doDragOver(e) {
- e.preventDefault();
-
- this.setDropEffect(e.dataTransfer);
- let newDragIndex = this.getWouldBeIndexForPoint_(e.pageX, e.pageY);
- if (newDragIndex < 0 || newDragIndex >= this.tileElements_.length) {
- newDragIndex = this.dragItemIndex_;
- }
- this.updateDropIndicator_(newDragIndex);
- },
-
- /** @override */
- doDrop(e) {
- e.stopPropagation();
- e.preventDefault();
-
- const index = this.currentDropIndex_;
- // Only change data if this was not a 'null drag'.
- if (!((index === this.dragItemIndex_) && this.withinPageDrag_)) {
- const adjustedIndex =
- this.currentDropIndex_ + (index > this.dragItemIndex_ ? 1 : 0);
- if (this.withinPageDrag_) {
- this.tileGrid_.insertBefore(
- currentlyDraggingTile, this.tileElements_[adjustedIndex]);
- this.tileMoved(currentlyDraggingTile, this.dragItemIndex_);
- } else {
- const originalPage =
- currentlyDraggingTile ? currentlyDraggingTile.tilePage : null;
- this.addDragData(e.dataTransfer, adjustedIndex);
- if (originalPage) {
- originalPage.cleanupDrag();
- }
- }
-
- // Dropping the icon may cause topMargin to change, but changing it
- // now would cause everything to move (annoying), so we leave it
- // alone. The top margin will be re-calculated next time the window is
- // resized or the page is selected.
- }
-
- this.classList.remove('animating-tile-page');
- this.cleanupDrag();
- },
-
- /**
- * Appends the currently dragged tile to the end of the page. Called
- * from outside the page, e.g. when dropping on a nav dot.
- */
- appendDraggingTile() {
- const originalPage = currentlyDraggingTile.tilePage;
- if (originalPage === this) {
- return;
- }
-
- this.addDragData(null, this.tileElements_.length);
- if (originalPage) {
- originalPage.cleanupDrag();
- }
- },
-
- /**
- * Makes sure all the tiles are in the right place after a drag is over.
- */
- cleanupDrag() {
- this.repositionTiles_(currentlyDraggingTile);
- // Remove the drag mask.
- this.updateMask_();
- },
-
- /**
- * Reposition all the tiles (possibly ignoring one).
- * @param {Node=} opt_ignoreNode An optional node to ignore.
- * @private
- */
- repositionTiles_(opt_ignoreNode) {
- for (let i = 0; i < this.tileElements_.length; i++) {
- if (!opt_ignoreNode || opt_ignoreNode !== this.tileElements_[i]) {
- this.positionTile_(i);
- }
- }
- },
-
- /**
- * Updates the visual indicator for the drop location for the active drag.
- * @param {number} newDragIndex
- * @private
- */
- updateDropIndicator_(newDragIndex) {
- const oldDragIndex = this.currentDropIndex_;
- if (newDragIndex === oldDragIndex) {
- return;
- }
-
- const repositionStart = Math.min(newDragIndex, oldDragIndex);
- const repositionEnd = Math.max(newDragIndex, oldDragIndex);
-
- for (let i = repositionStart; i <= repositionEnd; i++) {
- if (i === this.dragItemIndex_) {
- continue;
- }
-
- const adjustment = i > this.dragItemIndex_ ? i <= newDragIndex ? -1 : 0 :
- i >= newDragIndex ? 1 : 0;
- this.positionTile_(i, adjustment);
- }
- this.currentDropIndex_ = newDragIndex;
- },
-
- /**
- * Checks if a page can accept a drag with the given data.
- * @param {Event} e The drag event if the drag object. Implementations will
- * likely want to check |e.dataTransfer|.
- * @return {boolean} True if this page can handle the drag.
- */
- shouldAcceptDrag(e) {
- return false;
- },
-
- /**
- * Called to accept a drag drop. Will not be called for in-page drops.
- * @param {Object} dataTransfer The data transfer object that holds the drop
- * data. This should only be used if currentlyDraggingTile is null.
- * @param {number} index The tile index at which the drop occurred.
- */
- addDragData(dataTransfer, index) {
- assertNotReached();
- },
-
- /**
- * Called when a tile has been moved (via dragging). Override this to make
- * backend updates.
- * @param {Node} draggedTile The tile that was dropped.
- * @param {number} prevIndex The previous index of the tile.
- */
- tileMoved(draggedTile, prevIndex) {},
-
- /**
- * Sets the drop effect on |dataTransfer| to the desired value (e.g.
- * 'copy').
- * @param {DataTransfer} dataTransfer The drag event dataTransfer object.
- */
- setDropEffect(dataTransfer) {
- assertNotReached();
- },
-};
diff --git a/chromium/chrome/browser/resources/ntp4/touch_handler.js b/chromium/chrome/browser/resources/ntp4/touch_handler.js
deleted file mode 100644
index c2c2af238d1..00000000000
--- a/chromium/chrome/browser/resources/ntp4/touch_handler.js
+++ /dev/null
@@ -1,878 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assert, assertInstanceof} from 'chrome://resources/js/assert_ts.js';
-import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-
-/**
- * @fileoverview Touch Handler. Class that handles all touch events and
- * uses them to interpret higher level gestures and behaviors. TouchEvent is a
- * built in mobile safari type:
- * http://developer.apple.com/safari/library/documentation/UserExperience/Reference/TouchEventClassReference/TouchEvent/TouchEvent.html.
- * This class is intended to work with all webkit browsers, tested on Chrome and
- * iOS.
- *
- * The following types of gestures are currently supported. See the definition
- * of TouchHandler.EventType for details.
- *
- * Single Touch:
- * This provides simple single-touch events. Any secondary touch is
- * ignored.
- *
- * Drag:
- * A single touch followed by some movement. This behavior will handle all
- * of the required events and report the properties of the drag to you
- * while the touch is happening and at the end of the drag sequence. This
- * behavior will NOT perform the actual dragging (redrawing the element)
- * for you, this responsibility is left to the client code.
- *
- * Long press:
- * When your element is touched and held without any drag occuring, the
- * LONG_PRESS event will fire.
- */
-
-
-/**
- * A TouchHandler attaches to an Element, listents for low-level touch (or
- * mouse) events and dispatching higher-level events on the element.
- * @param {!Element} element The element to listen on and fire events
- * for.
- * @constructor
- */
-export function TouchHandler(element) {
- /**
- * @type {!Element}
- * @private
- */
- this.element_ = element;
-
- /**
- * The absolute sum of all touch y deltas.
- * @type {number}
- * @private
- */
- this.totalMoveY_ = 0;
-
- /**
- * The absolute sum of all touch x deltas.
- * @type {number}
- * @private
- */
- this.totalMoveX_ = 0;
-
- /**
- * An array of tuples where the first item is the horizontal component of a
- * recent relevant touch and the second item is the touch's time stamp. Old
- * touches are removed based on the max tracking time and when direction
- * changes.
- * @type {!Array<number>}
- * @private
- */
- this.recentTouchesX_ = [];
-
- /**
- * An array of tuples where the first item is the vertical component of a
- * recent relevant touch and the second item is the touch's time stamp. Old
- * touches are removed based on the max tracking time and when direction
- * changes.
- * @type {!Array<number>}
- * @private
- */
- this.recentTouchesY_ = [];
-
- /**
- * Used to keep track of all events we subscribe to so we can easily clean
- * up
- * @type {EventTracker}
- * @private
- */
- this.events_ = new EventTracker();
-}
-
-
-/**
- * DOM Events that may be fired by the TouchHandler at the element
- * @enum {string}
- */
-TouchHandler.EventType = {
- // Fired whenever the element is touched as the only touch to the device.
- // enableDrag defaults to false, set to true to permit dragging.
- TOUCH_START: 'touchHandler:touch_start',
-
- // Fired when an element is held for a period of time. Prevents dragging
- // from occuring (even if enableDrag was set to true).
- LONG_PRESS: 'touchHandler:long_press',
-
- // If enableDrag was set to true at TOUCH_START, DRAG_START will fire when
- // the touch first moves sufficient distance. enableDrag is set to true but
- // can be reset to false to cancel the drag.
- DRAG_START: 'touchHandler:drag_start',
-
- // If enableDrag was true after DRAG_START, DRAG_MOVE will fire whenever the
- // touch is moved.
- DRAG_MOVE: 'touchHandler:drag_move',
-
- // Fired just before TOUCH_END when a drag is released. Correlates 1:1 with
- // a DRAG_START.
- DRAG_END: 'touchHandler:drag_end',
-
- // Fired whenever a touch that is being tracked has been released.
- // Correlates 1:1 with a TOUCH_START.
- TOUCH_END: 'touchHandler:touch_end',
-
- // Fired whenever the element is tapped in a short time and no dragging is
- // detected.
- TAP: 'touchHandler:tap',
-};
-
-
-/**
- * The type of event sent by TouchHandler
- * @constructor
- * @extends {Event}
- * @param {!TouchHandler.EventType} type The type of event.
- * @param {boolean} bubbles Whether or not the event should bubble.
- * @param {number} clientX The X location of the touch.
- * @param {number} clientY The Y location of the touch.
- * @param {!Element} touchedElement The element at the current location of the
- * touch.
- */
-TouchHandler.Event = function(type, bubbles, clientX, clientY, touchedElement) {
- const event = document.createEvent('Event');
- event.initEvent(type, bubbles, true);
- event.__proto__ = TouchHandler.Event.prototype;
-
- /**
- * The X location of the touch affected
- * @type {number}
- */
- event.clientX = clientX;
-
- /**
- * The Y location of the touch affected
- * @type {number}
- */
- event.clientY = clientY;
-
- /**
- * The element at the current location of the touch.
- * @type {!Element}
- */
- event.touchedElement = touchedElement;
-
- return event;
-};
-
-TouchHandler.Event.prototype = {
- __proto__: Event.prototype,
-
- /**
- * For TOUCH_START and DRAG START events, set to true to enable dragging or
- * false to disable dragging.
- * @type {boolean|undefined}
- */
- enableDrag: undefined,
-
- /**
- * For DRAG events, provides the horizontal component of the
- * drag delta. Drag delta is defined as the delta of the start touch
- * position and the current drag position.
- * @type {number|undefined}
- */
- dragDeltaX: undefined,
-
- /**
- * For DRAG events, provides the vertical component of the
- * drag delta.
- * @type {number|undefined}
- */
- dragDeltaY: undefined,
-};
-
-/**
- * Maximum movement of touch required to be considered a tap.
- * @type {number}
- * @private
- */
-TouchHandler.MAX_TRACKING_FOR_TAP_ = 8;
-
-/**
- * The maximum number of ms to track a touch event. After an event is older
- * than this value, it will be ignored in velocity calculations.
- * @type {number}
- * @private
- */
-TouchHandler.MAX_TRACKING_TIME_ = 250;
-
-/**
- * The maximum number of touches to track.
- * @type {number}
- * @private
- */
-TouchHandler.MAX_TRACKING_TOUCHES_ = 5;
-
-/**
- * The maximum velocity to return, in pixels per millisecond, that is used
- * to guard against errors in calculating end velocity of a drag. This is a
- * very fast drag velocity.
- * @type {number}
- * @private
- */
-TouchHandler.MAXIMUM_VELOCITY_ = 5;
-
-/**
- * The velocity to return, in pixel per millisecond, when the time stamps on
- * the events are erroneous. The browser can return bad time stamps if the
- * thread is blocked for the duration of the drag. This is a low velocity to
- * prevent the content from moving quickly after a slow drag. It is less
- * jarring if the content moves slowly after a fast drag.
- * @type {number}
- * @private
- */
-TouchHandler.VELOCITY_FOR_INCORRECT_EVENTS_ = 1;
-
-/**
- * The time, in milliseconds, that a touch must be held to be considered
- * 'long'.
- * @type {number}
- * @private
- */
-TouchHandler.TIME_FOR_LONG_PRESS_ = 500;
-
-TouchHandler.prototype = {
- /**
- * If defined, the identifer of the single touch that is active. Note that
- * 0 is a valid touch identifier - it should not be treated equivalently to
- * undefined.
- * @type {number|undefined}
- * @private
- */
- activeTouch_: undefined,
-
- /**
- * @type {boolean|undefined}
- * @private
- */
- tracking_: undefined,
-
- /**
- * @type {number|undefined}
- * @private
- */
- startTouchX_: undefined,
-
- /**
- * @type {number|undefined}
- * @private
- */
- startTouchY_: undefined,
-
- /**
- * @type {number|undefined}
- * @private
- */
- endTouchX_: undefined,
-
- /**
- * @type {number|undefined}
- * @private
- */
- endTouchY_: undefined,
-
- /**
- * Time of the touchstart event.
- * @type {number|undefined}
- * @private
- */
- startTime_: undefined,
-
- /**
- * The time of the touchend event.
- * @type {number|undefined}
- * @private
- */
- endTime_: undefined,
-
- /**
- * @type {number|undefined}
- * @private
- */
- lastTouchX_: undefined,
-
- /**
- * @type {number|undefined}
- * @private
- */
- lastTouchY_: undefined,
-
- /**
- * @type {number|undefined}
- * @private
- */
- lastMoveX_: undefined,
-
- /**
- * @type {number|undefined}
- * @private
- */
- lastMoveY_: undefined,
-
- /**
- * @type {number|undefined}
- * @private
- */
- longPressTimeout_: undefined,
-
- /**
- * If defined and true, the next click event should be swallowed
- * @type {boolean|undefined}
- * @private
- */
- swallowNextClick_: undefined,
-
- /**
- * @type {boolean}
- * @private
- */
- draggingEnabled_: false,
-
- /**
- * Start listenting for events.
- * @param {boolean=} opt_capture True if the TouchHandler should listen to
- * during the capture phase.
- * @param {boolean=} opt_mouse True if the TouchHandler should generate
- * events for mouse input (in addition to touch input).
- */
- enable(opt_capture, opt_mouse) {
- const capture = !!opt_capture;
-
- // Just listen to start events for now. When a touch is occuring we'll
- // want to be subscribed to move and end events on the document, but we
- // don't want to incur the cost of lots of no-op handlers on the document.
- this.events_.add(
- this.element_, 'touchstart', this.onStart_.bind(this), capture);
- if (opt_mouse) {
- this.events_.add(
- this.element_, 'mousedown',
- this.mouseToTouchCallback_(this.onStart_.bind(this)), capture);
- }
-
- // If the element is long-pressed, we may need to swallow a click
- this.events_.add(this.element_, 'click', this.onClick_.bind(this), true);
- },
-
- /**
- * Stop listening to all events.
- */
- disable() {
- this.stopTouching_();
- this.events_.removeAll();
- },
-
- /**
- * Wraps a callback with translations of mouse events to touch events.
- * NOTE: These types really should be function(Event) but then we couldn't
- * use this with bind (which operates on any type of function). Doesn't
- * JSDoc support some sort of polymorphic types?
- * @param {Function} callback The event callback.
- * @return {Function} The wrapping callback.
- * @private
- */
- mouseToTouchCallback_(callback) {
- return function(e) {
- // Note that there may be synthesizes mouse events caused by touch
- // events (a mouseDown after a touch-click). We leave it up to the
- // client to worry about this if it matters to them (typically a short
- // mouseDown/mouseUp without a click is no big problem and it's not
- // obvious how we identify such synthesized events in a general way).
- const touch = {
- // any fixed value will do for the identifier - there will only
- // ever be a single active 'touch' when using the mouse.
- identifier: 0,
- clientX: e.clientX,
- clientY: e.clientY,
- target: e.target,
- };
- e.touches = [];
- e.targetTouches = [];
- e.changedTouches = [touch];
- if (e.type !== 'mouseup') {
- e.touches[0] = touch;
- e.targetTouches[0] = touch;
- }
- callback(e);
- };
- },
-
- /**
- * Begin tracking the touchable element, it is eligible for dragging.
- * @private
- */
- beginTracking_() {
- this.tracking_ = true;
- },
-
- /**
- * Stop tracking the touchable element, it is no longer dragging.
- * @private
- */
- endTracking_() {
- this.tracking_ = false;
- this.dragging_ = false;
- this.totalMoveY_ = 0;
- this.totalMoveX_ = 0;
- },
-
- /**
- * Reset the touchable element as if we never saw the touchStart
- * Doesn't dispatch any end events - be careful of existing listeners.
- */
- cancelTouch() {
- this.stopTouching_();
- this.endTracking_();
- // If clients needed to be aware of this, we could fire a cancel event
- // here.
- },
-
- /**
- * Record that touching has stopped
- * @private
- */
- stopTouching_() {
- // Mark as no longer being touched
- this.activeTouch_ = undefined;
-
- // If we're waiting for a long press, stop
- window.clearTimeout(this.longPressTimeout_);
-
- // Stop listening for move/end events until there's another touch.
- // We don't want to leave handlers piled up on the document.
- // Note that there's no harm in removing handlers that weren't added, so
- // rather than track whether we're using mouse or touch we do both.
- this.events_.remove(document, 'touchmove');
- this.events_.remove(document, 'touchend');
- this.events_.remove(document, 'touchcancel');
- this.events_.remove(document, 'mousemove');
- this.events_.remove(document, 'mouseup');
- },
-
- /**
- * Touch start handler.
- * @param {!TouchEvent} e The touchstart event.
- * @private
- */
- onStart_(e) {
- // Only process single touches. If there is already a touch happening, or
- // two simultaneous touches then just ignore them.
- if (e.touches.length > 1) {
- // Note that we could cancel an active touch here. That would make
- // simultaneous touch behave similar to near-simultaneous. However, if
- // the user is dragging something, an accidental second touch could be
- // quite disruptive if it cancelled their drag. Better to just ignore
- // it.
- return;
- }
-
- // It's still possible there could be an active "touch" if the user is
- // simultaneously using a mouse and a touch input.
- if (this.activeTouch_ !== undefined) {
- return;
- }
-
- const touch = e.targetTouches[0];
- this.activeTouch_ = touch.identifier;
-
- // We've just started touching so shouldn't swallow any upcoming click
- if (this.swallowNextClick_) {
- this.swallowNextClick_ = false;
- }
-
- this.disableTap_ = false;
-
- // Sign up for end/cancel notifications for this touch.
- // Note that we do this on the document so that even if the user drags
- // their finger off the element, we'll still know what they're doing.
- if (e.type === 'mousedown') {
- this.events_.add(
- document, 'mouseup',
- this.mouseToTouchCallback_(this.onEnd_.bind(this)), false);
- } else {
- this.events_.add(document, 'touchend', this.onEnd_.bind(this), false);
- this.events_.add(document, 'touchcancel', this.onEnd_.bind(this), false);
- }
-
- // This timeout is cleared on touchEnd and onDrag
- // If we invoke the function then we have a real long press
- window.clearTimeout(this.longPressTimeout_);
- this.longPressTimeout_ = window.setTimeout(
- this.onLongPress_.bind(this), TouchHandler.TIME_FOR_LONG_PRESS_);
-
- // Dispatch the TOUCH_START event
- this.draggingEnabled_ =
- !!this.dispatchEvent_(TouchHandler.EventType.TOUCH_START, touch);
-
- // We want dragging notifications
- if (e.type === 'mousedown') {
- this.events_.add(
- document, 'mousemove',
- this.mouseToTouchCallback_(this.onMove_.bind(this)), false);
- } else {
- this.events_.add(document, 'touchmove', this.onMove_.bind(this), false);
- }
-
- this.startTouchX_ = this.lastTouchX_ = touch.clientX;
- this.startTouchY_ = this.lastTouchY_ = touch.clientY;
- this.startTime_ = e.timeStamp;
-
- this.recentTouchesX_ = [];
- this.recentTouchesY_ = [];
- this.recentTouchesX_.push(touch.clientX, e.timeStamp);
- this.recentTouchesY_.push(touch.clientY, e.timeStamp);
-
- this.beginTracking_();
- },
-
- /**
- * Given a list of Touches, find the one matching our activeTouch
- * identifier. Note that Chrome currently always uses 0 as the identifier.
- * In that case we'll end up always choosing the first element in the list.
- * @param {TouchList} touches The list of Touch objects to search.
- * @return {!Touch|undefined} The touch matching our active ID if any.
- * @private
- */
- findActiveTouch_(touches) {
- assert(this.activeTouch_ !== undefined, 'Expecting an active touch');
- // A TouchList isn't actually an array, so we shouldn't use
- // Array.prototype.filter/some, etc.
- for (let i = 0; i < touches.length; i++) {
- if (touches[i].identifier === this.activeTouch_) {
- return touches[i];
- }
- }
- return undefined;
- },
-
- /**
- * Touch move handler.
- * @param {!TouchEvent} e The touchmove event.
- * @private
- */
- onMove_(e) {
- if (!this.tracking_) {
- return;
- }
-
- // Our active touch should always be in the list of touches still active
- assert(this.findActiveTouch_(e.touches), 'Missing touchEnd');
-
- const that = this;
- const touch = this.findActiveTouch_(e.changedTouches);
- if (!touch) {
- return;
- }
-
- const clientX = touch.clientX;
- const clientY = touch.clientY;
-
- const moveX = this.lastTouchX_ - clientX;
- const moveY = this.lastTouchY_ - clientY;
- this.totalMoveX_ += Math.abs(moveX);
- this.totalMoveY_ += Math.abs(moveY);
- this.lastTouchX_ = clientX;
- this.lastTouchY_ = clientY;
-
- const couldBeTap = this.totalMoveY_ <= TouchHandler.MAX_TRACKING_FOR_TAP_ ||
- this.totalMoveX_ <= TouchHandler.MAX_TRACKING_FOR_TAP_;
-
- if (!couldBeTap) {
- this.disableTap_ = true;
- }
-
- if (this.draggingEnabled_ && !this.dragging_ && !couldBeTap) {
- // If we're waiting for a long press, stop
- window.clearTimeout(this.longPressTimeout_);
-
- // Dispatch the DRAG_START event and record whether dragging should be
- // allowed or not. Note that this relies on the current value of
- // startTouchX/Y - handlers may use the initial drag delta to determine
- // if dragging should be permitted.
- this.dragging_ =
- this.dispatchEvent_(TouchHandler.EventType.DRAG_START, touch);
-
- if (this.dragging_) {
- // Update the start position here so that drag deltas have better
- // values but don't touch the recent positions so that velocity
- // calculations can still use touchstart position in the time and
- // distance delta.
- this.startTouchX_ = clientX;
- this.startTouchY_ = clientY;
- this.startTime_ = e.timeStamp;
- } else {
- this.endTracking_();
- }
- }
-
- if (this.dragging_) {
- this.dispatchEvent_(TouchHandler.EventType.DRAG_MOVE, touch);
-
- this.removeTouchesInWrongDirection_(
- this.recentTouchesX_, this.lastMoveX_, moveX);
- this.removeTouchesInWrongDirection_(
- this.recentTouchesY_, this.lastMoveY_, moveY);
- this.removeOldTouches_(this.recentTouchesX_, e.timeStamp);
- this.removeOldTouches_(this.recentTouchesY_, e.timeStamp);
- this.recentTouchesX_.push(clientX, e.timeStamp);
- this.recentTouchesY_.push(clientY, e.timeStamp);
- }
-
- this.lastMoveX_ = moveX;
- this.lastMoveY_ = moveY;
- },
-
- /**
- * Filters the provided recent touches array to remove all touches except
- * the last if the move direction has changed.
- * @param {!Array<number>} recentTouches An array of tuples where the first
- * item is the x or y component of the recent touch and the second item
- * is the touch time stamp.
- * @param {number|undefined} lastMove The x or y component of the previous
- * move.
- * @param {number} recentMove The x or y component of the most recent move.
- * @private
- */
- removeTouchesInWrongDirection_(recentTouches, lastMove, recentMove) {
- if (lastMove && recentMove && recentTouches.length > 2 &&
- (lastMove > 0 ^ recentMove > 0)) {
- recentTouches.splice(0, recentTouches.length - 2);
- }
- },
-
- /**
- * Filters the provided recent touches array to remove all touches older
- * than the max tracking time or the 5th most recent touch.
- * @param {!Array<number>} recentTouches An array of tuples where the first
- * item is the x or y component of the recent touch and the second item
- * is the touch time stamp.
- * @param {number} recentTime The time of the most recent event.
- * @private
- */
- removeOldTouches_(recentTouches, recentTime) {
- while (recentTouches.length &&
- recentTime - recentTouches[1] >
- TouchHandler.MAX_TRACKING_TIME_ ||
- recentTouches.length > TouchHandler.MAX_TRACKING_TOUCHES_ * 2) {
- recentTouches.splice(0, 2);
- }
- },
-
- /**
- * Touch end handler.
- * @param {!TouchEvent} e The touchend event.
- * @private
- */
- onEnd_(e) {
- const that = this;
- assert(this.activeTouch_ !== undefined, 'Expect to already be touching');
-
- // If the touch we're tracking isn't changing here, ignore this touch end.
- const touch = this.findActiveTouch_(e.changedTouches);
- if (!touch) {
- // In most cases, our active touch will be in the 'touches' collection,
- // but we can't assert that because occasionally two touchend events can
- // occur at almost the same time with both having empty 'touches' lists.
- // I.e., 'touches' seems like it can be a bit more up to date than the
- // current event.
- return;
- }
-
- // This is touchEnd for the touch we're monitoring
- assert(!this.findActiveTouch_(e.touches), 'Touch ended also still active');
-
- // Indicate that touching has finished
- this.stopTouching_();
-
- if (this.tracking_) {
- const clientX = touch.clientX;
- const clientY = touch.clientY;
-
- if (this.dragging_) {
- this.endTime_ = e.timeStamp;
- this.endTouchX_ = clientX;
- this.endTouchY_ = clientY;
-
- this.removeOldTouches_(this.recentTouchesX_, e.timeStamp);
- this.removeOldTouches_(this.recentTouchesY_, e.timeStamp);
-
- this.dispatchEvent_(TouchHandler.EventType.DRAG_END, touch);
-
- // Note that in some situations we can get a click event here as well.
- // For now this isn't a problem, but we may want to consider having
- // some logic that hides clicks that appear to be caused by a touchEnd
- // used for dragging.
- }
-
- this.endTracking_();
- }
- this.draggingEnabled_ = false;
-
- // Note that we dispatch the touchEnd event last so that events at
- // different levels of semantics nest nicely (similar to how DOM
- // drag-and-drop events are nested inside of the mouse events that trigger
- // them).
- this.dispatchEvent_(TouchHandler.EventType.TOUCH_END, touch);
- if (!this.disableTap_) {
- this.dispatchEvent_(TouchHandler.EventType.TAP, touch);
- }
- },
-
- /**
- * Get end velocity of the drag. This method is specific to drag behavior,
- * so if touch behavior and drag behavior is split then this should go with
- * drag behavior. End velocity is defined as deltaXY / deltaTime where
- * deltaXY is the difference between endPosition and the oldest recent
- * position, and deltaTime is the difference between endTime and the oldest
- * recent time stamp.
- * @return {Object} The x and y velocity.
- */
- getEndVelocity() {
- // Note that we could move velocity to just be an end-event parameter.
- let velocityX = this.recentTouchesX_.length ?
- (this.endTouchX_ - this.recentTouchesX_[0]) /
- (this.endTime_ - this.recentTouchesX_[1]) :
- 0;
- let velocityY = this.recentTouchesY_.length ?
- (this.endTouchY_ - this.recentTouchesY_[0]) /
- (this.endTime_ - this.recentTouchesY_[1]) :
- 0;
-
- velocityX = this.correctVelocity_(velocityX);
- velocityY = this.correctVelocity_(velocityY);
-
- return {x: velocityX, y: velocityY};
- },
-
- /**
- * Correct erroneous velocities by capping the velocity if we think it's too
- * high, or setting it to a default velocity if know that the event data is
- * bad.
- * @param {number} velocity The x or y velocity component.
- * @return {number} The corrected velocity.
- * @private
- */
- correctVelocity_(velocity) {
- let absVelocity = Math.abs(velocity);
-
- // We add to recent touches for each touchstart and touchmove. If we have
- // fewer than 3 touches (6 entries), we assume that the thread was blocked
- // for the duration of the drag and we received events in quick succession
- // with the wrong time stamps.
- if (absVelocity > TouchHandler.MAXIMUM_VELOCITY_) {
- absVelocity = this.recentTouchesY_.length < 3 ?
- TouchHandler.VELOCITY_FOR_INCORRECT_EVENTS_ :
- TouchHandler.MAXIMUM_VELOCITY_;
- }
- return absVelocity * (velocity < 0 ? -1 : 1);
- },
-
- /**
- * Handler when an element has been pressed for a long time
- * @private
- */
- onLongPress_() {
- // Swallow any click that occurs on this element without an intervening
- // touch start event. This simple click-busting technique should be
- // sufficient here since a real click should have a touchstart first.
- this.swallowNextClick_ = true;
- this.disableTap_ = true;
-
- // Dispatch to the LONG_PRESS
- assert(typeof this.startTouchX_ === 'number');
- assert(typeof this.startTouchY_ === 'number');
- this.dispatchEventXY_(
- TouchHandler.EventType.LONG_PRESS, this.element_,
- /** @type {number} */ (this.startTouchX_),
- /** @type {number} */ (this.startTouchY_));
- },
-
- /**
- * Click handler - used to swallow clicks after a long-press
- * @param {!Event} e The click event.
- * @private
- */
- onClick_(e) {
- if (this.swallowNextClick_) {
- e.preventDefault();
- e.stopPropagation();
- this.swallowNextClick_ = false;
- }
- },
-
- /**
- * Dispatch a TouchHandler event to the element
- * @param {!TouchHandler.EventType} eventType The event to dispatch.
- * @param {Touch} touch The touch triggering this event.
- * @return {boolean|undefined} The value of enableDrag after dispatching
- * the event.
- * @private
- */
- dispatchEvent_(eventType, touch) {
- // Determine which element was touched. For mouse events, this is always
- // the event/touch target. But for touch events, the target is always the
- // target of the touchstart (and it's unlikely we can change this
- // since the common implementation of touch dragging relies on it). Since
- // touch is our primary scenario (which we want to emulate with mouse),
- // we'll treat both cases the same and not depend on the target.
- /** @type {Element} */
- let touchedElement;
- if (eventType === TouchHandler.EventType.TOUCH_START) {
- assertInstanceof(touch.target, Element);
- touchedElement = touch.target;
- } else {
- touchedElement = assert(this.element_.ownerDocument.elementFromPoint(
- touch.clientX, touch.clientY));
- }
-
- return this.dispatchEventXY_(
- eventType, touchedElement, touch.clientX, touch.clientY);
- },
-
- /**
- * Dispatch a TouchHandler event to the element
- * @param {!TouchHandler.EventType} eventType The event to dispatch.
- * @param {!Element} touchedElement
- * @param {number} clientX The X location for the event.
- * @param {number} clientY The Y location for the event.
- * @return {boolean|undefined} The value of enableDrag after dispatching
- * the event.
- * @private
- */
- dispatchEventXY_(eventType, touchedElement, clientX, clientY) {
- const isDrag =
- (eventType === TouchHandler.EventType.DRAG_START ||
- eventType === TouchHandler.EventType.DRAG_MOVE ||
- eventType === TouchHandler.EventType.DRAG_END);
-
- // Drag events don't bubble - we're really just dragging the element,
- // not affecting its parent at all.
- const bubbles = !isDrag;
-
- const event = new TouchHandler.Event(
- eventType, bubbles, clientX, clientY, touchedElement);
-
- // Set enableDrag when it can be overridden
- if (eventType === TouchHandler.EventType.TOUCH_START) {
- event.enableDrag = false;
- } else if (eventType === TouchHandler.EventType.DRAG_START) {
- event.enableDrag = true;
- }
-
- if (isDrag) {
- event.dragDeltaX = clientX - this.startTouchX_;
- event.dragDeltaY = clientY - this.startTouchY_;
- }
-
- this.element_.dispatchEvent(event);
- return event.enableDrag;
- },
-};
diff --git a/chromium/chrome/browser/resources/ntp4/trash.css b/chromium/chrome/browser/resources/ntp4/trash.css
deleted file mode 100644
index 43a2ab81cf2..00000000000
--- a/chromium/chrome/browser/resources/ntp4/trash.css
+++ /dev/null
@@ -1,59 +0,0 @@
-/* Copyright 2012 The Chromium Authors
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-.trash {
- -webkit-appearance: none;
- background: none;
- border: none;
- cursor: pointer;
- display: inline-block;
- outline: none;
- padding: 0;
- position: relative;
- width: 30px;
-}
-
-.trash > span {
- display: inline-block;
-}
-
-.trash > .can,
-.trash > .lid {
- background: url(./images/trash.png) 0 0 no-repeat;
- left: 8px;
- position: absolute;
- right: 8px;
- top: 2px;
-}
-
-.trash > .lid {
- height: 6px;
- transform-origin: -7% 100%;
- transition: transform 150ms;
- width: 14px;
-}
-
-html[dir='rtl'] .trash > .lid {
- transform-origin: 107% 100%;
-}
-
-.trash:-webkit-any(:focus, :hover, .open) > .lid {
- transform: rotate(-45deg);
- transition: transform 250ms;
-}
-
-html[dir='rtl'] .trash:-webkit-any(:focus, :hover, .open) > .lid {
- transform: rotate(45deg);
-}
-
-.trash > .can {
- background-position: -1px -4px;
- height: 12px;
- /* The margins match the background position offsets. */
- margin-left: 1px;
- /* The right margin is one greater due to a shadow on the trash image. */
- margin-right: 2px;
- margin-top: 4px;
- width: 11px;
-}
diff --git a/chromium/chrome/browser/resources/ntp4/trash.js b/chromium/chrome/browser/resources/ntp4/trash.js
deleted file mode 100644
index 9aaf7e52714..00000000000
--- a/chromium/chrome/browser/resources/ntp4/trash.js
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {DragWrapper} from 'chrome://resources/js/drag_wrapper.js';
-import {getCurrentlyDraggingTile, setCurrentDropEffect} from './tile_page.js';
-
-/**
- * @fileoverview Trash
- * This is the class for the trash can that appears when dragging an app.
- */
-
-
-/**
- * @constructor
- * @extends {HTMLDivElement}
- * @implements {DragWrapperDelegate}
- */
-export function Trash(trash) {
- trash.__proto__ = Trash.prototype;
- trash.initialize();
- return trash;
-}
-
-Trash.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- initialize(element) {
- this.dragWrapper_ = new DragWrapper(this, this);
- },
-
- /**
- * Determines whether we are interested in the drag data for |e|.
- * @param {Event} e The event from drag enter.
- * @return {boolean} True if we are interested in the drag data for |e|.
- */
- shouldAcceptDrag(e) {
- const tile = getCurrentlyDraggingTile();
- if (!tile) {
- return false;
- }
-
- return tile.firstChild.canBeRemoved();
- },
-
- /** @override */
- doDragOver(e) {
- getCurrentlyDraggingTile().dragClone.classList.add('hovering-on-trash');
- setCurrentDropEffect(e.dataTransfer, 'move');
- e.preventDefault();
- },
-
- /** @override */
- doDragEnter(e) {
- this.doDragOver(e);
- },
-
- /** @override */
- doDrop(e) {
- e.preventDefault();
-
- const tile = getCurrentlyDraggingTile();
- tile.firstChild.removeFromChrome();
- tile.landedOnTrash = true;
- },
-
- /** @override */
- doDragLeave(e) {
- getCurrentlyDraggingTile().dragClone.classList.remove('hovering-on-trash');
- },
-};
diff --git a/chromium/chrome/browser/resources/ntp4/ui.js b/chromium/chrome/browser/resources/ntp4/ui.js
deleted file mode 100644
index eb23df84b2a..00000000000
--- a/chromium/chrome/browser/resources/ntp4/ui.js
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// NOTE: ui.js and the autogenerated ui.m.js module version are deprecated.
-// These files and files that depend on them should only be used by legacy UIs
-// that have not yet been updated to new patterns. Use Web Components in any new
-// code.
-
- /**
- * Decorates elements as an instance of a class.
- * @param {string|!Element} source The way to find the element(s) to decorate.
- * If this is a string then {@code querySeletorAll} is used to find the
- * elements to decorate.
- * @param {!Function} constr The constructor to decorate with. The constr
- * needs to have a {@code decorate} function.
- * @closurePrimitive {asserts.matchesReturn}
- */
- export function decorate(source, constr) {
- let elements;
- if (typeof source === 'string') {
- elements = document.querySelectorAll(source);
- } else {
- elements = [source];
- }
-
- for (let i = 0, el; el = elements[i]; i++) {
- if (!(el instanceof constr)) {
- constr.decorate(el);
- }
- }
- }
-
- /**
- * Helper function for creating new element for define.
- */
- function createElementHelper(tagName, opt_bag) {
- // Allow passing in ownerDocument to create in a different document.
- let doc;
- if (opt_bag && opt_bag.ownerDocument) {
- doc = opt_bag.ownerDocument;
- } else {
- doc = document;
- }
- return doc.createElement(tagName);
- }
-
- /**
- * Creates the constructor for a UI element class.
- *
- * Usage:
- * <pre>
- * var List = cr.ui.define('list');
- * List.prototype = {
- * __proto__: HTMLUListElement.prototype,
- * decorate() {
- * ...
- * },
- * ...
- * };
- * </pre>
- *
- * @param {string|Function} tagNameOrFunction The tagName or
- * function to use for newly created elements. If this is a function it
- * needs to return a new element when called.
- * @return {function(Object=):Element} The constructor function which takes
- * an optional property bag. The function also has a static
- * {@code decorate} method added to it.
- */
- export function define(tagNameOrFunction) {
- let createFunction;
- let tagName;
- if (typeof tagNameOrFunction === 'function') {
- createFunction = tagNameOrFunction;
- tagName = '';
- } else {
- createFunction = createElementHelper;
- tagName = tagNameOrFunction;
- }
-
- /**
- * Creates a new UI element constructor.
- * @param {Object=} opt_propertyBag Optional bag of properties to set on the
- * object after created. The property {@code ownerDocument} is special
- * cased and it allows you to create the element in a different
- * document than the default.
- * @constructor
- */
- function f(opt_propertyBag) {
- const el = createFunction(tagName, opt_propertyBag);
- f.decorate(el);
- for (const propertyName in opt_propertyBag) {
- el[propertyName] = opt_propertyBag[propertyName];
- }
- return el;
- }
-
- /**
- * Decorates an element as a UI element class.
- * @param {!Element} el The element to decorate.
- */
- f.decorate = function(el) {
- el.__proto__ = f.prototype;
- if (el.decorate) {
- el.decorate();
- }
- };
-
- return f;
- }
-
- /**
- * Input elements do not grow and shrink with their content. This is a simple
- * (and not very efficient) way of handling shrinking to content with support
- * for min width and limited by the width of the parent element.
- * @param {!HTMLElement} el The element to limit the width for.
- * @param {!HTMLElement} parentEl The parent element that should limit the
- * size.
- * @param {number} min The minimum width.
- * @param {number=} opt_scale Optional scale factor to apply to the width.
- */
- export function limitInputWidth(el, parentEl, min, opt_scale) {
- // Needs a size larger than borders
- el.style.width = '10px';
- const doc = el.ownerDocument;
- const win = doc.defaultView;
- const computedStyle = win.getComputedStyle(el);
- const parentComputedStyle = win.getComputedStyle(parentEl);
- const rtl = computedStyle.direction === 'rtl';
-
- // To get the max width we get the width of the treeItem minus the position
- // of the input.
- const inputRect = el.getBoundingClientRect(); // box-sizing
- const parentRect = parentEl.getBoundingClientRect();
- const startPos = rtl ? parentRect.right - inputRect.right :
- inputRect.left - parentRect.left;
-
- // Add up border and padding of the input.
- const inner = parseInt(computedStyle.borderLeftWidth, 10) +
- parseInt(computedStyle.paddingLeft, 10) +
- parseInt(computedStyle.paddingRight, 10) +
- parseInt(computedStyle.borderRightWidth, 10);
-
- // We also need to subtract the padding of parent to prevent it to overflow.
- const parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) :
- parseInt(parentComputedStyle.paddingRight, 10);
-
- let max = parentEl.clientWidth - startPos - inner - parentPadding;
- if (opt_scale) {
- max *= opt_scale;
- }
-
- function limit() {
- if (el.scrollWidth > max) {
- el.style.width = max + 'px';
- } else {
- el.style.width = 0;
- const sw = el.scrollWidth;
- if (sw < min) {
- el.style.width = min + 'px';
- } else {
- el.style.width = sw + 'px';
- }
- }
- }
-
- el.addEventListener('input', limit);
- limit();
- }
-
- /**
- * Takes a number and spits out a value CSS will be happy with. To avoid
- * subpixel layout issues, the value is rounded to the nearest integral value.
- * @param {number} pixels The number of pixels.
- * @return {string} e.g. '16px'.
- */
- export function toCssPx(pixels) {
- if (!window.isFinite(pixels)) {
- console.error('Pixel value is not a number: ' + pixels);
- }
- return Math.round(pixels) + 'px';
- }
-
- /**
- * Users complain they occasionaly use doubleclicks instead of clicks
- * (http://crbug.com/140364). To fix it we freeze click handling for
- * the doubleclick time interval.
- * @param {MouseEvent} e Initial click event.
- */
- export function swallowDoubleClick(e) {
- const doc = e.target.ownerDocument;
- let counter = Math.min(1, e.detail);
- function swallow(e) {
- e.stopPropagation();
- e.preventDefault();
- }
- function onclick(e) {
- if (e.detail > counter) {
- counter = e.detail;
- // Swallow the click since it's a click inside the doubleclick timeout.
- swallow(e);
- } else {
- // Stop tracking clicks and let regular handling.
- doc.removeEventListener('dblclick', swallow, true);
- doc.removeEventListener('click', onclick, true);
- }
- }
- // The following 'click' event (if e.type === 'mouseup') mustn't be taken
- // into account (it mustn't stop tracking clicks). Start event listening
- // after zero timeout.
- setTimeout(function() {
- doc.addEventListener('click', onclick, true);
- doc.addEventListener('dblclick', swallow, true);
- }, 0);
- }
-
diff --git a/chromium/chrome/browser/resources/ntp4/util.js b/chromium/chrome/browser/resources/ntp4/util.js
deleted file mode 100644
index 655c7bb11de..00000000000
--- a/chromium/chrome/browser/resources/ntp4/util.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @param {Node} el A node to search for ancestors with |className|.
- * @param {string} className A class to search for.
- * @return {Element} A node with class of |className| or null if none is found.
- */
-export function findAncestorByClass(el, className) {
- while (el !== null) {
- if (el.classList && el.classList.contains(className)) {
- break;
- }
- el = el.parentNode;
- }
- return el;
-}
diff --git a/chromium/chrome/browser/resources/omnibox/omnibox_output.ts b/chromium/chrome/browser/resources/omnibox/omnibox_output.ts
index 4020851e37d..e89a3e2fb72 100644
--- a/chromium/chrome/browser/resources/omnibox/omnibox_output.ts
+++ b/chromium/chrome/browser/resources/omnibox/omnibox_output.ts
@@ -338,11 +338,10 @@ class OutputMatch extends HTMLTableRowElement {
constructor(match: AutocompleteMatch) {
super();
- let mouseMoved = false;
- this.addEventListener('mousedown', () => mouseMoved = false);
- this.addEventListener('mousemove', () => mouseMoved = true);
this.addEventListener(
- 'click', () => !mouseMoved && this.classList.toggle('expanded'));
+ 'click',
+ () => !document.getSelection()?.toString() &&
+ this.classList.toggle('expanded'));
COLUMNS.forEach(column => {
const property = column.create(match);
diff --git a/chromium/chrome/browser/resources/omnibox_popup/app.ts b/chromium/chrome/browser/resources/omnibox_popup/app.ts
index 53d45d5c401..8ec97017ad1 100644
--- a/chromium/chrome/browser/resources/omnibox_popup/app.ts
+++ b/chromium/chrome/browser/resources/omnibox_popup/app.ts
@@ -6,7 +6,7 @@ import '//resources/cr_components/omnibox/realbox_dropdown.js';
import './strings.m.js';
import {ColorChangeUpdater} from '//resources/cr_components/color_change_listener/colors_css_updater.js';
-import {AutocompleteResult, PageCallbackRouter} from '//resources/cr_components/omnibox/omnibox.mojom-webui.js';
+import {AutocompleteResult, OmniboxPopupSelection, PageCallbackRouter} from '//resources/cr_components/omnibox/omnibox.mojom-webui.js';
import {RealboxBrowserProxy} from '//resources/cr_components/omnibox/realbox_browser_proxy.js';
import {RealboxDropdownElement} from '//resources/cr_components/omnibox/realbox_dropdown.js';
import {assert} from '//resources/js/assert_ts.js';
@@ -65,7 +65,7 @@ export class OmniboxPopupAppElement extends PolymerElement {
private callbackRouter_: PageCallbackRouter;
private autocompleteResultChangedListenerId_: number|null = null;
- private selectMatchAtLineListenerId_: number|null = null;
+ private selectionChangedListenerId_: number|null = null;
constructor() {
super();
@@ -78,9 +78,9 @@ export class OmniboxPopupAppElement extends PolymerElement {
this.autocompleteResultChangedListenerId_ =
this.callbackRouter_.autocompleteResultChanged.addListener(
this.onAutocompleteResultChanged_.bind(this));
- this.selectMatchAtLineListenerId_ =
- this.callbackRouter_.selectMatchAtLine.addListener(
- this.onSelectMatchAtLine_.bind(this));
+ this.selectionChangedListenerId_ =
+ this.callbackRouter_.updateSelection.addListener(
+ this.onUpdateSelection_.bind(this));
canShowSecondarySideMediaQueryList.addEventListener(
'change', this.onCanShowSecondarySideChanged_.bind(this));
}
@@ -90,8 +90,8 @@ export class OmniboxPopupAppElement extends PolymerElement {
assert(this.autocompleteResultChangedListenerId_);
this.callbackRouter_.removeListener(
this.autocompleteResultChangedListenerId_);
- assert(this.selectMatchAtLineListenerId_);
- this.callbackRouter_.removeListener(this.selectMatchAtLineListenerId_);
+ assert(this.selectionChangedListenerId_);
+ this.callbackRouter_.removeListener(this.selectionChangedListenerId_);
canShowSecondarySideMediaQueryList.removeEventListener(
'change', this.onCanShowSecondarySideChanged_.bind(this));
}
@@ -121,8 +121,8 @@ export class OmniboxPopupAppElement extends PolymerElement {
.catch(() => {});
}
- private onSelectMatchAtLine_(line: number) {
- this.$.matches.selectIndex(line);
+ private onUpdateSelection_(selection: OmniboxPopupSelection) {
+ this.$.matches.updateSelection(selection);
}
}
diff --git a/chromium/chrome/browser/resources/omnibox_popup/omnibox_popup.html b/chromium/chrome/browser/resources/omnibox_popup/omnibox_popup.html
index 5cbcd9c1d37..799e4ac5dda 100644
--- a/chromium/chrome/browser/resources/omnibox_popup/omnibox_popup.html
+++ b/chromium/chrome/browser/resources/omnibox_popup/omnibox_popup.html
@@ -7,12 +7,13 @@
<link rel="stylesheet" href="chrome://theme/colors.css?sets=ui,chrome">
<style>
html {
- height: 100%;
+ height: auto;
overflow: hidden;
}
body {
- height: 100%;
+ height: auto;
+ overflow: hidden;
margin: 0;
}
</style>
diff --git a/chromium/chrome/browser/resources/password_manager/BUILD.gn b/chromium/chrome/browser/resources/password_manager/BUILD.gn
index 8f7a2f6f5ec..6c4b33d9b7d 100644
--- a/chromium/chrome/browser/resources/password_manager/BUILD.gn
+++ b/chromium/chrome/browser/resources/password_manager/BUILD.gn
@@ -16,6 +16,9 @@ build_webui("build") {
"images/checkup_result_banner_running.svg",
"images/checkup_result_banner_running_dark.svg",
"images/password_manager_logo.svg",
+ "images/password_sharing_family_banner.svg",
+ "images/password_sharing_secure_shield.svg",
+ "images/password_sharing_progress_bar.svg",
"password_manager.html",
]
if (is_chrome_branded) {
@@ -60,6 +63,14 @@ build_webui("build") {
"passwords_section.ts",
"prefs/pref_toggle_button.ts",
"settings_section.ts",
+ "sharing/share_password_flow.ts",
+ "sharing/share_password_dialog_header.ts",
+ "sharing/share_password_recipient.ts",
+ "sharing/share_password_family_picker_dialog.ts",
+ "sharing/share_password_loading_dialog.ts",
+ "sharing/share_password_error_dialog.ts",
+ "sharing/share_password_no_members_dialog.ts",
+ "sharing/share_password_confirmation_dialog.ts",
"side_bar.ts",
"site_favicon.ts",
"toolbar.ts",
diff --git a/chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.html b/chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.html
index a0a605471d0..c54b387b41b 100644
--- a/chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.html
+++ b/chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.html
@@ -69,10 +69,15 @@
$i18n{deletePassword}
</cr-button>
<template is="dom-if" if="[[showShareButton_(isSyncingPasswords)]]" restamp>
- <cr-button id="shareButton">
+ <cr-button id="shareButton" on-click="onShareButtonClick_">
$i18n{share}
</cr-button>
</template>
+ <template is="dom-if" if="[[showShareFlow_]]" restamp>
+ <share-password-flow password-name="[[groupName]]" icon-url="[[iconUrl]]"
+ password="[[password]]" on-share-flow-done="onShareFlowDone_">
+ </share-password-flow>
+ </template>
</div>
</div>
<template is="dom-if" if="[[showEditPasswordDialog_]]" restamp>
diff --git a/chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.ts b/chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.ts
index 5423a1081dd..ea346a271c1 100644
--- a/chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.ts
+++ b/chromium/chrome/browser/resources/password_manager/credential_details/password_details_card.ts
@@ -68,6 +68,8 @@ export class PasswordDetailsCardElement extends PasswordDetailsCardElementBase {
static get properties() {
return {
password: Object,
+ groupName: String,
+ iconUrl: String,
toastMessage_: String,
usernameCopyInteraction_: {
type: PasswordViewPageInteractions,
@@ -79,6 +81,11 @@ export class PasswordDetailsCardElement extends PasswordDetailsCardElementBase {
showEditPasswordDialog_: Boolean,
showDeletePasswordDialog_: Boolean,
+ showShareFlow_: {
+ type: Boolean,
+ value: false,
+ },
+
enableSendPasswords_: {
type: Boolean,
value() {
@@ -89,9 +96,12 @@ export class PasswordDetailsCardElement extends PasswordDetailsCardElementBase {
}
password: chrome.passwordsPrivate.PasswordUiEntry;
+ groupName: string;
+ iconUrl: string;
private toastMessage_: string;
private showEditPasswordDialog_: boolean;
private showDeletePasswordDialog_: boolean;
+ private showShareFlow_: boolean;
private enableSendPasswords_: boolean;
private isFederated_(): boolean {
@@ -173,6 +183,14 @@ export class PasswordDetailsCardElement extends PasswordDetailsCardElementBase {
this.extendAuthValidity_();
}
+ private onShareButtonClick_() {
+ this.showShareFlow_ = true;
+ }
+
+ private onShareFlowDone_() {
+ this.showShareFlow_ = false;
+ }
+
private extendAuthValidity_() {
PasswordManagerImpl.getInstance().extendAuthValidity();
}
diff --git a/chromium/chrome/browser/resources/password_manager/dialogs/add_password_dialog.ts b/chromium/chrome/browser/resources/password_manager/dialogs/add_password_dialog.ts
index d753e5d168c..a0a43b3e8f8 100644
--- a/chromium/chrome/browser/resources/password_manager/dialogs/add_password_dialog.ts
+++ b/chromium/chrome/browser/resources/password_manager/dialogs/add_password_dialog.ts
@@ -339,6 +339,11 @@ export class AddPasswordDialogElement extends AddPasswordDialogElementBase {
AddCredentialFromSettingsUserInteractions.CREDENTIAL_ADDED);
const useAccountStore = this.isAccountStoreUser &&
(this.$.storePicker.value === this.storeOptionAccountValue_);
+ if (!this.$.storePicker.hidden) {
+ chrome.metricsPrivate.recordBoolean(
+ 'PasswordManager.AddCredentialFromSettings.AccountStoreUsed2',
+ useAccountStore);
+ }
PasswordManagerImpl.getInstance()
.addPassword({
diff --git a/chromium/chrome/browser/resources/password_manager/images/password_sharing_family_banner.svg b/chromium/chrome/browser/resources/password_manager/images/password_sharing_family_banner.svg
new file mode 100644
index 00000000000..528d7657ef4
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/images/password_sharing_family_banner.svg
@@ -0,0 +1 @@
+<svg width="480" height="128" viewBox="0 0 480 128" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path fill="url(#a)" d="M0 0h480v128H0z"/><defs><pattern id="a" patternContentUnits="objectBoundingBox" width="1" height="1"><use xlink:href="#b" transform="matrix(.00047 0 0 .00178 0 -.124)"/></pattern><image id="b" width="2112" height="721" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAACEAAAALRCAYAAABRD0RUAAAAAXNSR0IArs4c6QAAAFBlWElmTU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAIQKADAAQAAAABAAAC0QAAAADic20JAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgoZXuEHAABAAElEQVR4AezdB4AcZfn48Wdm+17vl3IppEEwhE5CCR1Ct9GUovSiP5UiAQVE5CdVRMWCKO2voD8bohQJJQpCaCEhQCqkXnK93/ad//NuciEJKXe5trf7fWGyu1Pe8plL7m7mmecVoSCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQxgJWGveNriGAAAII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AAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCDSCwP8H9YZ5ERosbxEAAAAASUVORK5CYII="/></defs></svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/password_manager/images/password_sharing_progress_bar.svg b/chromium/chrome/browser/resources/password_manager/images/password_sharing_progress_bar.svg
new file mode 100644
index 00000000000..e963f7754d5
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/images/password_sharing_progress_bar.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="100" height="3" viewBox="0 0 100 3" fill="none"><mask id="a" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="100" height="3"><path fill-rule="evenodd" clip-rule="evenodd" d="M3 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm5 0a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM11.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM18 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM21.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM28 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM31.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM38 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM41.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM48 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM51.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM58 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM61.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM68 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM71.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM78 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM81.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM88 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM91.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM98 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm3.5 1.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm6.5-1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm3.5 1.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm6.5-1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm3.5 1.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm6.5-1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm3.5 1.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm6.5-1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm3.5 1.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z" fill="#D9D9D9"/></mask><g mask="url(#a)"><rect width="100" height="3" rx="1.5" fill="#185ABC"/></g></svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/password_manager/images/password_sharing_secure_shield.svg b/chromium/chrome/browser/resources/password_manager/images/password_sharing_secure_shield.svg
new file mode 100644
index 00000000000..b407abb9b7d
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/images/password_sharing_secure_shield.svg
@@ -0,0 +1 @@
+<svg width="25" height="31" viewBox="0 0 25 31" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M13.507.189a2.778 2.778 0 0 0-2.014 0l-9.722 3.78A2.778 2.778 0 0 0 0 6.56v6.433c0 8.681 5.138 14.794 11.708 17.103a2.39 2.39 0 0 0 1.584 0C19.862 27.786 25 21.673 25 12.992V6.559a2.778 2.778 0 0 0-1.771-2.59L13.507.19Zm3.16 11.414h-.695v-1.39a3.473 3.473 0 0 0-6.944 0v1.39h-.695c-.764 0-1.389.625-1.389 1.389v6.944c0 .764.625 1.389 1.39 1.389h8.333c.764 0 1.389-.625 1.389-1.389v-6.944c0-.764-.625-1.39-1.39-1.39Zm-6.25-1.39A2.08 2.08 0 0 1 12.5 8.132a2.08 2.08 0 0 1 2.083 2.083v1.389h-4.166v-1.39Zm2.083 7.64c.764 0 1.389-.625 1.389-1.39 0-.763-.625-1.388-1.389-1.388s-1.389.625-1.389 1.389.625 1.389 1.389 1.389Z" fill="#185ABC"/></svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/password_manager/passkeys_browser_proxy.ts b/chromium/chrome/browser/resources/password_manager/passkeys_browser_proxy.ts
index 39059e6c5ec..718297590b0 100644
--- a/chromium/chrome/browser/resources/password_manager/passkeys_browser_proxy.ts
+++ b/chromium/chrome/browser/resources/password_manager/passkeys_browser_proxy.ts
@@ -10,6 +10,12 @@ export interface PasskeysBrowserProxy {
* May report false positives if the last passkey was recently deleted.
*/
hasPasskeys(): Promise<boolean>;
+
+ /**
+ * Opens the passkey management surface, whether that be native to the
+ * operating system, or a Chrome settings tab.
+ */
+ managePasskeys(): void;
}
export class PasskeysBrowserProxyImpl implements PasskeysBrowserProxy {
@@ -17,6 +23,10 @@ export class PasskeysBrowserProxyImpl implements PasskeysBrowserProxy {
return sendWithPromise('passkeysHasPasskeys');
}
+ managePasskeys() {
+ return chrome.send('passkeysManagePasskeys');
+ }
+
static getInstance(): PasskeysBrowserProxy {
return passkeysProxyInstance ||
(passkeysProxyInstance = new PasskeysBrowserProxyImpl());
diff --git a/chromium/chrome/browser/resources/password_manager/password_details_section.html b/chromium/chrome/browser/resources/password_manager/password_details_section.html
index 06c3ce4d6f1..23e5077d19a 100644
--- a/chromium/chrome/browser/resources/password_manager/password_details_section.html
+++ b/chromium/chrome/browser/resources/password_manager/password_details_section.html
@@ -1,7 +1,8 @@
<style include="shared-style cr-shared-style">
#header {
align-items: center;
- display: flex;
+ display: grid;
+ grid-template-columns: auto auto 1fr;
margin-bottom: 28px;
}
@@ -11,6 +12,7 @@
}
#favicon {
+ min-width: 20px;
padding-inline-end: 12px;
--site-favicon-height: 20px;
--site-favicon-width: 20px;
@@ -31,12 +33,16 @@
<h2 id="title" class="page-title text-elide">[[selectedGroup_.name]]</h2>
</div>
<template is="dom-if" if="[[selectedGroup_.name]]">
- <template is="dom-repeat" items="[[selectedGroup_.entries]]">
+ <template is="dom-repeat" initial-count="10"
+ items="[[selectedGroup_.entries]]">
<template is="dom-if" if="[[item.isPasskey]]">
<passkey-details-card passkey="[[item]]"></passkey-details-card>
</template>
<template is="dom-if" if="[[!item.isPasskey]]">
- <password-details-card password="[[item]]"></password-details-card>
+ <password-details-card password="[[item]]"
+ group-name="[[selectedGroup_.name]]"
+ icon-url="[[selectedGroup_.iconUrl]]">
+ </password-details-card>
</template>
</template>
</template>
diff --git a/chromium/chrome/browser/resources/password_manager/password_details_section.ts b/chromium/chrome/browser/resources/password_manager/password_details_section.ts
index fa14ef96514..f836844a65e 100644
--- a/chromium/chrome/browser/resources/password_manager/password_details_section.ts
+++ b/chromium/chrome/browser/resources/password_manager/password_details_section.ts
@@ -103,10 +103,6 @@ export class PasswordDetailsSectionElement extends
Router.getInstance().currentRoute.queryParameters);
}
- private getGroupName_(): string {
- return this.selectedGroup_ ? this.selectedGroup_!.name : '';
- }
-
private async assignMatchingGroup(groupName: string) {
const groups =
await PasswordManagerImpl.getInstance().getCredentialGroups();
diff --git a/chromium/chrome/browser/resources/password_manager/password_manager.html b/chromium/chrome/browser/resources/password_manager/password_manager.html
index cbce6e2bf33..8cd6e0b8c14 100644
--- a/chromium/chrome/browser/resources/password_manager/password_manager.html
+++ b/chromium/chrome/browser/resources/password_manager/password_manager.html
@@ -1,9 +1,10 @@
<!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<html dir="$i18n{textdirection}" lang="$i18n{language}"
+ $i18n{chromeRefresh2023Attribute}>
<head>
<meta charset="utf8">
<base href="chrome://password-manager">
- <title>$i18n{title}</title>
+ <title>$i18n{passwordManagerTitle}</title>
<link rel="stylesheet" href="chrome://resources/css/md_colors.css">
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
<if expr="_google_chrome">
diff --git a/chromium/chrome/browser/resources/password_manager/password_manager.ts b/chromium/chrome/browser/resources/password_manager/password_manager.ts
index e4d727054fa..00636b8e4f0 100644
--- a/chromium/chrome/browser/resources/password_manager/password_manager.ts
+++ b/chromium/chrome/browser/resources/password_manager/password_manager.ts
@@ -35,6 +35,10 @@ export {PrefToggleButtonElement} from './prefs/pref_toggle_button.js';
export {PromoCard, PromoCardsProxy, PromoCardsProxyImpl} from './promo_cards/promo_cards_browser_proxy.js';
export {CheckupSubpage, Page, Route, RouteObserverMixin, RouteObserverMixinInterface, Router, UrlParam} from './router.js';
export {SettingsSectionElement} from './settings_section.js';
+export {SharePasswordConfirmationDialogElement} from './sharing/share_password_confirmation_dialog.js';
+export {ShareFlowState, SharePasswordFlowElement} from './sharing/share_password_flow.js';
+export {SharePasswordLoadingDialogElement} from './sharing/share_password_loading_dialog.js';
+export {SharePasswordRecipientElement} from './sharing/share_password_recipient.js';
export {PasswordManagerSideBarElement} from './side_bar.js';
export {SiteFaviconElement} from './site_favicon.js';
export {AccountInfo, SyncBrowserProxy, SyncBrowserProxyImpl, SyncInfo, TrustedVaultBannerState} from './sync_browser_proxy.js';
diff --git a/chromium/chrome/browser/resources/password_manager/password_manager_app.html b/chromium/chrome/browser/resources/password_manager/password_manager_app.html
index 491b67542c6..0fbc21dbf02 100644
--- a/chromium/chrome/browser/resources/password_manager/password_manager_app.html
+++ b/chromium/chrome/browser/resources/password_manager/password_manager_app.html
@@ -121,7 +121,8 @@
aligned with the right-hand child of the flex toolbar. -->
<div id="space-holder" hidden$="[[narrow_]]"></div>
<div>
-<cr-drawer id="drawer" heading="$i18n{title}" align="$i18n{textdirection}">
+<cr-drawer id="drawer" heading="$i18n{passwordManagerString}"
+ align="$i18n{textdirection}">
<div slot="body">
<template is="dom-if" id="drawerTemplate">
<password-manager-side-bar id="drawerSidebar"></password-manager-side-bar>
diff --git a/chromium/chrome/browser/resources/password_manager/password_manager_app.ts b/chromium/chrome/browser/resources/password_manager/password_manager_app.ts
index 548fcca879c..502e2c54601 100644
--- a/chromium/chrome/browser/resources/password_manager/password_manager_app.ts
+++ b/chromium/chrome/browser/resources/password_manager/password_manager_app.ts
@@ -172,8 +172,12 @@ export class PasswordManagerAppElement extends PasswordManagerAppElementBase {
if (modalContextOpen) {
return false;
}
- this.$.toolbar.searchField.showAndFocus();
- return true;
+ // Redirect to Password Manager search on Passwords page.
+ if (Router.getInstance().currentRoute.page === Page.PASSWORDS) {
+ this.$.toolbar.searchField.showAndFocus();
+ return true;
+ }
+ return false;
}
// Override FindShortcutMixin methods.
diff --git a/chromium/chrome/browser/resources/password_manager/password_manager_proxy.ts b/chromium/chrome/browser/resources/password_manager/password_manager_proxy.ts
index 0f5f868305f..a579fa7d6b1 100644
--- a/chromium/chrome/browser/resources/password_manager/password_manager_proxy.ts
+++ b/chromium/chrome/browser/resources/password_manager/password_manager_proxy.ts
@@ -201,6 +201,20 @@ export interface PasswordManagerProxy {
Promise<void>;
/**
+ * Fetches family members (password share recipients).
+ * @return A promise that resolves the FamilyFetchResults.
+ */
+ fetchFamilyMembers(): Promise<chrome.passwordsPrivate.FamilyFetchResults>;
+
+ /**
+ * Sends sharing invitations to the recipients.
+ * @param id The id of the password entry to be shared.
+ * @param recipients The list of selected recipients.
+ */
+ sharePassword(
+ id: number, recipients: chrome.passwordsPrivate.RecipientInfo[]): void;
+
+ /**
* Updates the given credential. Not all parameters can be updated.
* @param credential the credential to update.
* @return A promise that resolves if the credential was found and updated,
@@ -290,11 +304,6 @@ export interface PasswordManagerProxy {
listener: PasswordsFileExportProgressListener): void;
/**
- * Cancels the export in progress.
- */
- cancelExportPasswords(): void;
-
- /**
* Switches Biometric authentication before filling state after
* successful authentication.
*/
@@ -498,6 +507,15 @@ export class PasswordManagerImpl implements PasswordManagerProxy {
chrome.passwordsPrivate.undoRemoveSavedPasswordOrException();
}
+ fetchFamilyMembers() {
+ return chrome.passwordsPrivate.fetchFamilyMembers();
+ }
+
+ sharePassword(
+ id: number, recipients: chrome.passwordsPrivate.RecipientInfo[]) {
+ chrome.passwordsPrivate.sharePassword(id, recipients);
+ }
+
importPasswords(toStore: chrome.passwordsPrivate.PasswordStoreSet) {
return chrome.passwordsPrivate.importPasswords(toStore);
}
@@ -529,10 +547,6 @@ export class PasswordManagerImpl implements PasswordManagerProxy {
listener);
}
- cancelExportPasswords() {
- chrome.passwordsPrivate.cancelExportPasswords();
- }
-
switchBiometricAuthBeforeFillingState() {
chrome.passwordsPrivate.switchBiometricAuthBeforeFillingState();
}
diff --git a/chromium/chrome/browser/resources/password_manager/passwords_section.html b/chromium/chrome/browser/resources/password_manager/passwords_section.html
index f877e07ebfb..f4042cf6bc2 100644
--- a/chromium/chrome/browser/resources/password_manager/passwords_section.html
+++ b/chromium/chrome/browser/resources/password_manager/passwords_section.html
@@ -66,7 +66,8 @@
hidden$="[[hideGroupsList_(groups_, searchTerm_)]]">
<template id="passwordsList" is="dom-repeat" initial-count="50"
items="[[groups_]]" filter="[[groupFilter_(searchTerm_)]]"
- rendered-item-count="{{shownGroupsCount_::dom-change}}">
+ rendered-item-count="{{shownGroupsCount_::dom-change}}"
+ sort="[[computeSortFunction_(searchTerm_)]]">
<password-list-item item="[[item]]" first="[[!index]]"
on-password-details-shown="onPasswordDetailsShown_"
search-term="[[searchTerm_]]" role="listitem">
diff --git a/chromium/chrome/browser/resources/password_manager/passwords_section.ts b/chromium/chrome/browser/resources/password_manager/passwords_section.ts
index b0970497009..ade25bc7015 100644
--- a/chromium/chrome/browser/resources/password_manager/passwords_section.ts
+++ b/chromium/chrome/browser/resources/password_manager/passwords_section.ts
@@ -377,6 +377,28 @@ export class PasswordsSectionElement extends PasswordsSectionElementBase {
focusWithoutInk(this.activeListItem_);
});
}
+
+ private computeSortFunction_(searchTerm: string):
+ ((a: chrome.passwordsPrivate.CredentialGroup,
+ b: chrome.passwordsPrivate.CredentialGroup) => number)|null {
+ // Keep current order when not searching.
+ if (!searchTerm) {
+ return null;
+ }
+
+ // Always show group with matching name in the top, fallback to alphabetical
+ // order when matching type is the same.
+ return function(
+ a: chrome.passwordsPrivate.CredentialGroup,
+ b: chrome.passwordsPrivate.CredentialGroup) {
+ const doesNameMatchA = a.name.toLowerCase().includes(searchTerm);
+ const doesNameMatchB = b.name.toLowerCase().includes(searchTerm);
+ if (doesNameMatchA === doesNameMatchB) {
+ return a.name.localeCompare(b.name);
+ }
+ return doesNameMatchA ? -1 : 1;
+ };
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/password_manager/settings_section.html b/chromium/chrome/browser/resources/password_manager/settings_section.html
index 422b1d6a025..2ff8369adf5 100644
--- a/chromium/chrome/browser/resources/password_manager/settings_section.html
+++ b/chromium/chrome/browser/resources/password_manager/settings_section.html
@@ -113,6 +113,17 @@
sub-label="$i18n{addShortcutDescription}" role-description="button">
</cr-link-row>
</template>
+ <if expr="is_macosx">
+ <template is="dom-if" if="[[createPasskeysInICloudKeychainToggleVisible_]]"
+ restamp>
+ <pref-toggle-button id="createPasskeysInICloudKeychainRow" class="hr"
+ label="$i18n{createPasskeysInICloudKeychainLabel}"
+ sub-label="$i18n{createPasskeysInICloudKeychainSubLabel}"
+ on-click="onCreatePasskeysInICloudKeychainClick_"
+ pref="{{prefs.webauthn.create_in_icloud_keychain}}">
+ </pref-link-row>
+ </template>
+ </if>
<if expr="is_win or is_macosx">
<template is="dom-if" if="[[hasPasskeys_]]" restamp>
<cr-link-row id="managePasskeysRow" external
diff --git a/chromium/chrome/browser/resources/password_manager/settings_section.ts b/chromium/chrome/browser/resources/password_manager/settings_section.ts
index 891039f5aa9..ed84472fa28 100644
--- a/chromium/chrome/browser/resources/password_manager/settings_section.ts
+++ b/chromium/chrome/browser/resources/password_manager/settings_section.ts
@@ -77,6 +77,16 @@ export class SettingsSectionElement extends SettingsSectionElementBase {
value: false,
},
+ // <if expr="is_macosx">
+ createPasskeysInICloudKeychainToggleVisible_: {
+ type: Boolean,
+ value() {
+ return loadTimeData.getBoolean(
+ 'createPasskeysInICloudKeychainToggleVisible');
+ },
+ },
+ // </if>
+
hasPasskeys_: {
type: Boolean,
value: false,
@@ -277,12 +287,11 @@ export class SettingsSectionElement extends SettingsSectionElementBase {
}
}
+ // <if expr="is_win or is_macosx">
private onManagePasskeysClick_() {
- // In the future this may, e.g., open System Settings on macOS for iCloud
- // Keychain, or open Control Panel on Windows for Hello. Currently passkey
- // management is filled in via Chrome settings.
- OpenWindowProxyImpl.getInstance().openUrl('chrome://settings/passkeys');
+ PasskeysBrowserProxyImpl.getInstance().managePasskeys();
}
+ // </if>
private computePasswordManagerDisabled_(): boolean {
const pref = this.getPref('credentials_enable_service');
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_confirmation_dialog.html b/chromium/chrome/browser/resources/password_manager/sharing/share_password_confirmation_dialog.html
new file mode 100644
index 00000000000..037b16e9ad2
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_confirmation_dialog.html
@@ -0,0 +1,240 @@
+<style include="cr-hidden-style">
+ a[href] {
+ color: var(--cr-link-color);
+ }
+
+ cr-dialog {
+ --cr-dialog-width: 320px;
+ --cr-dialog-body-padding-horizontal: 16px;
+ }
+
+ .animation-container {
+ height: 95px;
+ position: relative;
+
+ --avatar-radius: 30px;
+ }
+
+ .avatar,
+ #shield,
+ #progress,
+ #favicon {
+ position: absolute;
+ animation-fill-mode: forwards;
+ }
+
+ .avatar {
+ border-radius: 50%;
+ height: calc(var(--avatar-radius) * 2);
+ width: calc(var(--avatar-radius) * 2);
+
+ top: 50%;
+ right: 50%;
+ transform: translate(50%, -50%);
+
+ animation-delay: 0.35s;
+ animation-duration: 4.65s;
+ animation-timing-function: ease-in;
+ }
+
+ #senderAvatar {
+ z-index: 1;
+ animation-name: slideLeft;
+ }
+
+ @keyframes slideLeft {
+ 100% {
+ right: calc(50% + var(--avatar-radius) - 4px);
+ z-index: 2;
+ }
+
+ 10%,
+ 90% {
+ right: calc(50% + 78px);
+ }
+ }
+
+ #recipientAvatar {
+ z-index: 2;
+ animation-name: slideRight;
+ }
+
+ @keyframes slideRight {
+ 100% {
+ z-index: 1;
+ right: calc(50% - var(--avatar-radius) + 4px);
+ }
+
+ 10%,
+ 90% {
+ right: calc(50% - 78px);
+ }
+ }
+
+ #shield {
+ z-index: 3;
+ /* Backgroud has to be non-transperent, and match the one from cr-dialog. */
+ background-color: var(--cr-dialog-background-color, white);
+ top: 50%;
+ right: 50%;
+ width: 25px;
+ height: 30.23px;
+
+ opacity: 0;
+ transform: translate(50%, -50%) scale(0.5);
+
+ animation-delay: 0.8s;
+ animation-duration: 4s;
+ animation-timing-function: ease-in;
+ animation-name: shieldOpacity;
+ }
+
+ @keyframes shieldOpacity {
+ 100% {
+ opacity: 0;
+ transform: translate(50%, -50%) scale(0);
+ }
+
+ 3%,
+ 95% {
+ opacity: 1;
+ transform: translate(50%, -50%) scale(1);
+ }
+ }
+
+ #favicon {
+ --site-favicon-height: 22px;
+ --site-favicon-width: 22px;
+ background: white;
+
+ z-index: 3;
+ border: 4px solid white;
+ border-radius: 7px;
+ top: calc(50% + var(--avatar-radius) - 10px);
+ right: 50%;
+
+ opacity: 0;
+ transform: translate(50%, 0) scale(0);
+
+ animation-delay: 5s;
+ animation-duration: 0.15s;
+ animation-timing-function: ease-out;
+ animation-fill-mode: forwards;
+ animation-name: faviconOpacity;
+ }
+
+ @keyframes faviconOpacity {
+ 100% {
+ opacity: 1;
+ transform: translate(50%, 0) scale(1);
+ }
+ }
+
+ #progress {
+ z-index: 0;
+ display: flex;
+ overflow: hidden;
+ top: 50%;
+ left: calc(50% - 47px);
+
+ width: 0;
+
+ animation-delay: 1s;
+ animation-duration: 3.5s;
+ animation-timing-function: linear;
+ animation-name: progressWidth;
+ }
+
+ @keyframes progressWidth {
+ 100% {
+ width: 95px;
+ opacity: 0;
+ }
+
+ 1%,
+ 99% {
+ opacity: 1;
+ }
+ }
+
+ #description {
+ margin-top: 16px;
+ }
+
+ div[slot='footer'] {
+ background: var(--google-grey-100);
+ border-top: none;
+ padding: 8px 16px;
+ }
+
+ #footerDescription {
+ color: var(--cr-secondary-text-color);
+ }
+
+ @media (prefers-color-scheme: dark) {
+ /* Backgroud has to be non-transperent, and match one from cr-dialog. */
+ #shield {
+ background-color: var(--cr-dialog-background-color,
+ var(--google-grey-900));
+ /* Note: the colors in linear-gradient() are intentionally the same to
+ * add a 4% white layer on top of the fully opaque background-color. */
+ background-image: linear-gradient(rgba(255, 255, 255, .04),
+ rgba(255, 255, 255, .04));
+ }
+
+ div[slot='footer'] {
+ background: var(--google-grey-900);
+ }
+
+ #favicon {
+ border-color: var(--google-grey-900);
+ background: var(--google-grey-900);
+ }
+ }
+</style>
+
+<cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach>
+ <div slot="title">
+ <share-password-dialog-header id="header">
+ [[getDialogTitle_(dialogStage_)]]
+ </share-password-dialog-header>
+ </div>
+ <div slot="body">
+ <div class="animation-container">
+ <img id="senderAvatar" class="avatar" src="[[avatarImage]]">
+ <!-- TODO(crbug/1445526): Show multi-avatar element for multiple
+ recipients. -->
+ <img is="cr-auto-img" id="recipientAvatar" class="avatar"
+ auto-src="[[recipients.0.profileImageUrl]]" draggable="false" alt="">
+ <div id="shield">
+ <img src="../images/password_sharing_secure_shield.svg"
+ aria-hidden="true">
+ </div>
+ <div id="progress">
+ <img src="../images/password_sharing_progress_bar.svg"
+ aria-hidden="true">
+ </div>
+ <site-favicon id="favicon" url="[[iconUrl]]" domain="[[passwordName]]"
+ aria-hidden="true">
+ </site-favicon>
+ </div>
+ <div id="description"
+ hidden$="[[!isStage_(dialogStageEnum_.SUCCESS, dialogStage_)]]"
+ inner-h-t-m-l="[[getSuccessDescription_(recipients)]]">
+ </div>
+ </div>
+ <div slot="button-container">
+ <cr-button class="cancel-button" on-click="onClickCancel_" id="cancel"
+ hidden$="[[!isStage_(dialogStageEnum_.LOADING, dialogStage_)]]">
+ $i18n{cancel}
+ </cr-button>
+ <cr-button class="action-button" on-click="onClickDone_" id="done"
+ hidden$="[[isStage_(dialogStageEnum_.LOADING, dialogStage_)]]">
+ $i18n{done}
+ </cr-button>
+ </div>
+ <div slot="footer" id="footerDescription"
+ hidden$="[[!isStage_(dialogStageEnum_.SUCCESS, dialogStage_)]]"
+ inner-h-t-m-l="[[getFooterDescription_(password)]]">
+ </div>
+</cr-dialog>
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_confirmation_dialog.ts b/chromium/chrome/browser/resources/password_manager/sharing/share_password_confirmation_dialog.ts
new file mode 100644
index 00000000000..f8d340a5635
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_confirmation_dialog.ts
@@ -0,0 +1,173 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import './share_password_dialog_header.js';
+import '../site_favicon.js';
+
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {assertNotReached} from 'chrome://resources/js/assert_ts.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {PasswordManagerImpl, PasswordManagerProxy} from '../password_manager_proxy.js';
+import {UserUtilMixin} from '../user_utils_mixin.js';
+
+import {getTemplate} from './share_password_confirmation_dialog.html.js';
+
+export interface SharePasswordConfirmationDialogElement {
+ $: {
+ header: HTMLElement,
+ cancel: HTMLElement,
+ done: HTMLElement,
+ senderAvatar: HTMLImageElement,
+ recipientAvatar: HTMLElement,
+ description: HTMLElement,
+ footerDescription: HTMLElement,
+ };
+}
+
+// Five seconds in milliseconds.
+const FIVE_SECONDS = 5000;
+
+enum ConfirmationDialogStage {
+ LOADING,
+ CANCELED,
+ SUCCESS,
+}
+
+const SharePasswordConfirmationDialogElementBase =
+ UserUtilMixin(I18nMixin(PolymerElement));
+
+export class SharePasswordConfirmationDialogElement extends
+ SharePasswordConfirmationDialogElementBase {
+ static get is() {
+ return 'share-password-confirmation-dialog';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ dialogStage_: Number,
+
+ password: Object,
+ passwordName: String,
+ iconUrl: String,
+
+ recipients: {
+ type: Array,
+ value: [],
+ },
+
+ dialogStageEnum_: {
+ type: Object,
+ value: ConfirmationDialogStage,
+ readOnly: true,
+ },
+ };
+ }
+
+ password: chrome.passwordsPrivate.PasswordUiEntry;
+ passwordName: string;
+ iconUrl: string;
+ recipients: chrome.passwordsPrivate.RecipientInfo[];
+ private dialogStage_: ConfirmationDialogStage =
+ ConfirmationDialogStage.LOADING;
+ private passwordManager_: PasswordManagerProxy =
+ PasswordManagerImpl.getInstance();
+
+ override ready() {
+ super.ready();
+
+ // The user has 5 seconds to cancel the share action while loading/sharing
+ // animation is in progress.
+ setTimeout(() => {
+ if (this.isStage_(ConfirmationDialogStage.CANCELED)) {
+ return;
+ }
+ this.passwordManager_.sharePassword(this.password.id, this.recipients);
+ this.dialogStage_ = ConfirmationDialogStage.SUCCESS;
+ }, FIVE_SECONDS);
+ }
+
+ private isStage_(stage: ConfirmationDialogStage): boolean {
+ return this.dialogStage_ === stage;
+ }
+
+
+ private getDialogTitle_(): string {
+ switch (this.dialogStage_) {
+ case ConfirmationDialogStage.LOADING:
+ return this.i18n('shareDialogLoadingTitle');
+ case ConfirmationDialogStage.CANCELED:
+ return this.i18n('shareDialogCanceledTitle');
+ case ConfirmationDialogStage.SUCCESS:
+ return this.i18n('shareDialogSuccessTitle');
+ default:
+ assertNotReached();
+ }
+ }
+
+ private getSuccessDescription_(): TrustedHTML {
+ if (this.recipients.length > 1) {
+ return this.i18nAdvanced(
+ 'sharePasswordConfirmationDescriptionMultipleRecipients', {
+ substitutions: [
+ this.passwordName,
+ this.i18n('passwordManagerLearnMoreURL'),
+ ],
+ });
+ }
+ return this.i18nAdvanced(
+ 'sharePasswordConfirmationDescriptionSingleRecipient', {
+ substitutions: [
+ this.recipients[0].displayName,
+ this.passwordName,
+ this.i18n('passwordManagerLearnMoreURL'),
+ ],
+ });
+ }
+
+ private getFooterDescription_(): TrustedHTML {
+ // Only for Android Apps that don't have affiliated website, change password
+ // url can't be generated.
+ if (!this.password.changePasswordUrl) {
+ return this.i18nAdvanced('sharePasswordConfirmationFooterAndroidApp');
+ }
+ return this.i18nAdvanced('sharePasswordConfirmationFooterWebsite', {
+ substitutions: [
+ this.password.changePasswordUrl,
+ this.passwordName,
+ ],
+ });
+ }
+
+ private onClickDone_() {
+ this.dispatchEvent(
+ new CustomEvent('close', {bubbles: true, composed: true}));
+ }
+
+ private onClickCancel_() {
+ // Ignore the click if a race with the 5 second timeout occurred.
+ if (this.isStage_(ConfirmationDialogStage.SUCCESS)) {
+ return;
+ }
+ this.dialogStage_ = ConfirmationDialogStage.CANCELED;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'share-password-confirmation-dialog':
+ SharePasswordConfirmationDialogElement;
+ }
+}
+
+customElements.define(
+ SharePasswordConfirmationDialogElement.is,
+ SharePasswordConfirmationDialogElement);
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_dialog_header.html b/chromium/chrome/browser/resources/password_manager/sharing/share_password_dialog_header.html
new file mode 100644
index 00000000000..6f49d014546
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_dialog_header.html
@@ -0,0 +1,22 @@
+<style include="shared-style">
+ :host {
+ align-items: center;
+ display: grid;
+ grid-template-columns: 1fr auto;
+ line-height: normal;
+ }
+
+ cr-icon-button {
+ --cr-icon-button-icon-size: 16px;
+ --cr-icon-button-size: 20px;
+ --cr-icon-button-margin-start: 0;
+ --cr-icon-button-margin-end: 0;
+ }
+</style>
+
+<span class="text-elide">
+ <slot></slot>
+</span>
+<cr-icon-button iron-icon="cr:help-outline" id="helpButton"
+ title="$i18n{help}" on-click="onHelpClick_">
+</cr-icon-button>
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_dialog_header.ts b/chromium/chrome/browser/resources/password_manager/sharing/share_password_dialog_header.ts
new file mode 100644
index 00000000000..58de6e164d7
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_dialog_header.ts
@@ -0,0 +1,43 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import '../shared_style.css.js';
+
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './share_password_dialog_header.html.js';
+
+export interface SharePasswordDialogHeaderElement {
+ $: {
+ helpButton: HTMLElement,
+ };
+}
+
+export class SharePasswordDialogHeaderElement extends PolymerElement {
+ static get is() {
+ return 'share-password-dialog-header';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ private onHelpClick_() {
+ // TODO(crbug/1445526): Update learn more url to be more specific.
+ OpenWindowProxyImpl.getInstance().openUrl(
+ loadTimeData.getString('passwordManagerLearnMoreURL'));
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'share-password-dialog-header': SharePasswordDialogHeaderElement;
+ }
+}
+
+customElements.define(
+ SharePasswordDialogHeaderElement.is, SharePasswordDialogHeaderElement);
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_error_dialog.html b/chromium/chrome/browser/resources/password_manager/sharing/share_password_error_dialog.html
new file mode 100644
index 00000000000..91d6c5d4e76
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_error_dialog.html
@@ -0,0 +1,18 @@
+<cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach>
+ <div slot="title">
+ <share-password-dialog-header id="header">
+ $i18n{sharePasswordErrorTitle}
+ </share-password-dialog-header>
+ </div>
+ <div slot="body" class="flex-centered" id="description">
+ $i18n{sharePasswordErrorDescription}
+ </div>
+ <div slot="button-container">
+ <cr-button class="cancel-button" on-click="onClickCancel_" id="cancel">
+ $i18n{cancel}
+ </cr-button>
+ <cr-button class="action-button" on-click="onClickTryAgain_" id="tryAgain">
+ $i18n{sharePasswordTryAgain}
+ </cr-button>
+ </div>
+</cr-dialog>
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_error_dialog.ts b/chromium/chrome/browser/resources/password_manager/sharing/share_password_error_dialog.ts
new file mode 100644
index 00000000000..5d8d82a2b7b
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_error_dialog.ts
@@ -0,0 +1,52 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import './share_password_dialog_header.js';
+
+import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './share_password_error_dialog.html.js';
+
+export interface SharePasswordErrorDialogElement {
+ $: {
+ dialog: CrDialogElement,
+ header: HTMLElement,
+ description: HTMLElement,
+ cancel: HTMLElement,
+ tryAgain: HTMLElement,
+ };
+}
+
+export class SharePasswordErrorDialogElement extends I18nMixin
+(PolymerElement) {
+ static get is() {
+ return 'share-password-error-dialog';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ private onClickCancel_() {
+ this.$.dialog.cancel();
+ }
+
+ private onClickTryAgain_() {
+ this.dispatchEvent(
+ new CustomEvent('restart', {bubbles: true, composed: true}));
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'share-password-error-dialog': SharePasswordErrorDialogElement;
+ }
+}
+
+customElements.define(
+ SharePasswordErrorDialogElement.is, SharePasswordErrorDialogElement);
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_family_picker_dialog.html b/chromium/chrome/browser/resources/password_manager/sharing/share_password_family_picker_dialog.html
new file mode 100644
index 00000000000..c72a1a60abd
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_family_picker_dialog.html
@@ -0,0 +1,71 @@
+<style include="shared-style">
+ a[href] {
+ color: var(--cr-link-color);
+ }
+
+ #avatar {
+ border-radius: 50%;
+ height: 32px;
+ margin-inline-end: 8px;
+ width: 32px;
+ }
+
+ #userAccount {
+ color: var(--cr-secondary-text-color);
+ }
+
+ #description {
+ padding-bottom: 8px;
+ }
+
+ div[slot='footer'] {
+ background: var(--google-grey-100);
+ border-top: none;
+ padding: 8px 16px;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ div[slot='footer'] {
+ background: var(--google-grey-900);
+ }
+ }
+</style>
+
+<cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach>
+ <div slot="title">
+ <share-password-dialog-header id="header">
+ [[dialogTitle]]
+ </share-password-dialog-header>
+ </div>
+ <div slot="body">
+ <div id="description"
+ inner-h-t-m-l="[[i18nAdvanced('sharePasswordFamilyPickerDescription')]]">
+ </div>
+ <template is="dom-repeat" items="[[eligibleRecipients_]]">
+ <share-password-recipient recipient="[[item]]"
+ on-change="recipientSelected_">
+ </share-password-recipient>
+ </template>
+ <template is="dom-repeat" items="[[ineligibleRecipients_]]">
+ <share-password-recipient recipient="[[item]]" disabled>
+ </share-password-recipient>
+ </template>
+ </div>
+ <div slot="button-container">
+ <cr-button class="cancel-button" on-click="onClickCancel_" id="cancel">
+ $i18n{cancel}
+ </cr-button>
+ <cr-button class="action-button" id="action" on-click="onClickShare_"
+ disabled$="[[!selectedRecipients.length]]">
+ $i18n{share}
+ </cr-button>
+ </div>
+ <div slot="footer" class="flex-centered">
+ <img id="avatar" src="[[avatarImage]]"></img>
+ <div id="footerDescription">
+ <a href="$i18n{familyGroupSiteURL}" target="_blank" id="manageLink">
+ $i18n{sharePasswordManageFamily}</a>
+ <span> • </span><span>[[accountEmail]]</span>
+ </div>
+ </div>
+</cr-dialog>
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_family_picker_dialog.ts b/chromium/chrome/browser/resources/password_manager/sharing/share_password_family_picker_dialog.ts
new file mode 100644
index 00000000000..66123f5ff2b
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_family_picker_dialog.ts
@@ -0,0 +1,110 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import './share_password_dialog_header.js';
+import './share_password_recipient.js';
+import '../shared_style.css.js';
+
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {UserUtilMixin} from '../user_utils_mixin.js';
+
+import {getTemplate} from './share_password_family_picker_dialog.html.js';
+
+export interface SharePasswordFamilyPickerDialogElement {
+ $: {
+ header: HTMLElement,
+ description: HTMLElement,
+ action: HTMLButtonElement,
+ cancel: HTMLElement,
+ avatar: HTMLImageElement,
+ manageLink: HTMLAnchorElement,
+ footerDescription: HTMLElement,
+ };
+}
+
+export class SharePasswordFamilyPickerDialogElement extends UserUtilMixin
+(I18nMixin(PolymerElement)) {
+ static get is() {
+ return 'share-password-family-picker-dialog';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ dialogTitle: String,
+ members: Array,
+
+ selectedRecipients: {
+ type: Array,
+ value: [],
+ reflectToAttribute: true,
+ notify: true,
+ },
+
+ eligibleRecipients_: {
+ type: Array,
+ computed: 'computeEligible_(members)',
+ },
+
+ ineligibleRecipients_: {
+ type: Array,
+ computed: 'computeIneligible_(members)',
+ },
+ };
+ }
+
+ dialogTitle: string;
+ members: chrome.passwordsPrivate.RecipientInfo[];
+ selectedRecipients: chrome.passwordsPrivate.RecipientInfo[];
+ private eligibleRecipients_: chrome.passwordsPrivate.RecipientInfo[];
+ private ineligibleRecipients_: chrome.passwordsPrivate.RecipientInfo[];
+
+ private onClickCancel_() {
+ this.dispatchEvent(
+ new CustomEvent('close', {bubbles: true, composed: true}));
+ }
+
+ private computeEligible_(): chrome.passwordsPrivate.RecipientInfo[] {
+ const eligibleMembers = this.members.filter(member => member.isEligible);
+ eligibleMembers.sort((a, b) => (a.displayName > b.displayName ? 1 : -1));
+ return eligibleMembers;
+ }
+
+ private computeIneligible_(): chrome.passwordsPrivate.RecipientInfo[] {
+ const inEligibleMembers = this.members.filter(member => !member.isEligible);
+ inEligibleMembers.sort((a, b) => (a.displayName > b.displayName ? 1 : -1));
+ return inEligibleMembers;
+ }
+
+ private recipientSelected_(): void {
+ this.selectedRecipients =
+ Array
+ .from(this.shadowRoot!.querySelectorAll('share-password-recipient'))
+ .filter(item => item.selected)
+ .map(item => item.recipient);
+ }
+
+ private onClickShare_() {
+ this.dispatchEvent(
+ new CustomEvent('start-share', {bubbles: true, composed: true}));
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'share-password-family-picker-dialog':
+ SharePasswordFamilyPickerDialogElement;
+ }
+}
+
+customElements.define(
+ SharePasswordFamilyPickerDialogElement.is,
+ SharePasswordFamilyPickerDialogElement);
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_flow.html b/chromium/chrome/browser/resources/password_manager/sharing/share_password_flow.html
new file mode 100644
index 00000000000..6930ccc19e8
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_flow.html
@@ -0,0 +1,38 @@
+<template is="dom-if"
+ if="[[isState_(flowStateEnum_.FETCHING, flowState)]]" restamp>
+ <share-password-loading-dialog on-close="onDialogClose_"
+ dialog-title="[[getShareDialogTitle_(passwordName)]]">
+ </share-password-loading-dialog>
+</template>
+
+<template is="dom-if"
+ if="[[isState_(flowStateEnum_.ERROR, flowState)]]" restamp>
+ <share-password-error-dialog on-cancel="onDialogClose_"
+ on-restart="startSharing_">
+ </share-password-error-dialog>
+</template>
+
+<template is="dom-if"
+ if="[[isState_(flowStateEnum_.NO_MEMBERS, flowState)]]" restamp>
+ <share-password-no-members-dialog on-close="onDialogClose_"
+ dialog-title="[[getShareDialogTitle_(passwordName)]]">
+ </share-password-no-members-dialog>
+</template>
+
+<template is="dom-if"
+ if="[[isState_(flowStateEnum_.FAMILY_PICKER, flowState)]]" restamp>
+ <share-password-family-picker-dialog on-close="onDialogClose_"
+ dialog-title="[[getShareDialogTitle_(passwordName)]]"
+ members="[[fetchResults_.familyMembers]]"
+ selected-recipients="{{recipients_}}"
+ on-start-share="onStartShare_">
+ </share-password-family-picker-dialog>
+</template>
+
+<template is="dom-if"
+ if="[[isState_(flowStateEnum_.CONFIRMATION, flowState)]]" restamp>
+ <share-password-confirmation-dialog on-close="onDialogClose_"
+ recipients="[[recipients_]]" password="[[password]]"
+ password-name="[[passwordName]]" icon-url="[[iconUrl]]">
+ </share-password-confirmation-dialog>
+</template>
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_flow.ts b/chromium/chrome/browser/resources/password_manager/sharing/share_password_flow.ts
new file mode 100644
index 00000000000..9192b4c6233
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_flow.ts
@@ -0,0 +1,126 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Element which shows and controls password sharing dialogs.
+ */
+
+import './share_password_family_picker_dialog.js';
+import './share_password_loading_dialog.js';
+import './share_password_error_dialog.js';
+import './share_password_no_members_dialog.js';
+import './share_password_confirmation_dialog.js';
+
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {assertNotReached} from 'chrome://resources/js/assert_ts.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {PasswordManagerImpl, PasswordManagerProxy} from '../password_manager_proxy.js';
+
+import {getTemplate} from './share_password_flow.html.js';
+
+export enum ShareFlowState {
+ NO_DIALOG,
+ FETCHING,
+ ERROR,
+ NO_MEMBERS,
+ FAMILY_PICKER,
+ CONFIRMATION,
+}
+
+const SharePasswordFlowElementBase = I18nMixin(PolymerElement);
+
+export class SharePasswordFlowElement extends SharePasswordFlowElementBase {
+ static get is() {
+ return 'share-password-flow';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ passwordName: String,
+ iconUrl: String,
+ password: Object,
+
+ flowState: Number,
+
+ fetchResults_: Object,
+
+ recipients_: {
+ type: Array,
+ value: [],
+ },
+
+ flowStateEnum_: {
+ type: Object,
+ value: ShareFlowState,
+ readOnly: true,
+ },
+ };
+ }
+
+ passwordName: string;
+ iconUrl: string;
+ password: chrome.passwordsPrivate.PasswordUiEntry;
+ flowState: ShareFlowState = ShareFlowState.NO_DIALOG;
+ private recipients_: chrome.passwordsPrivate.RecipientInfo[];
+ private fetchResults_: chrome.passwordsPrivate.FamilyFetchResults|null = null;
+ private passwordManager_: PasswordManagerProxy =
+ PasswordManagerImpl.getInstance();
+
+ override connectedCallback() {
+ super.connectedCallback();
+
+ this.startSharing_();
+ }
+
+ private async startSharing_() {
+ // TODO(crbug/1445526): Add timeout to avoid flickering.
+ this.flowState = ShareFlowState.FETCHING;
+
+ this.fetchResults_ = await this.passwordManager_.fetchFamilyMembers();
+ switch (this.fetchResults_.status) {
+ case chrome.passwordsPrivate.FamilyFetchStatus.UNKNOWN_ERROR:
+ this.flowState = ShareFlowState.ERROR;
+ break;
+ case chrome.passwordsPrivate.FamilyFetchStatus.NO_MEMBERS:
+ this.flowState = ShareFlowState.NO_MEMBERS;
+ break;
+ case chrome.passwordsPrivate.FamilyFetchStatus.SUCCESS:
+ this.flowState = ShareFlowState.FAMILY_PICKER;
+ break;
+ default:
+ assertNotReached();
+ }
+ }
+
+ private isState_(state: ShareFlowState): boolean {
+ return this.flowState === state;
+ }
+
+ private getShareDialogTitle_(): string {
+ return this.i18n('shareDialogTitle', this.passwordName);
+ }
+
+ private onDialogClose_() {
+ this.dispatchEvent(
+ new CustomEvent('share-flow-done', {bubbles: true, composed: true}));
+ this.flowState = ShareFlowState.NO_DIALOG;
+ }
+
+ private onStartShare_() {
+ this.flowState = ShareFlowState.CONFIRMATION;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'share-password-flow': SharePasswordFlowElement;
+ }
+}
+
+customElements.define(SharePasswordFlowElement.is, SharePasswordFlowElement);
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_loading_dialog.html b/chromium/chrome/browser/resources/password_manager/sharing/share_password_loading_dialog.html
new file mode 100644
index 00000000000..407ab1aa5ef
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_loading_dialog.html
@@ -0,0 +1,14 @@
+<style include="shared-style">
+ paper-spinner-lite {
+ display: flex;
+ margin-inline: auto;
+ margin-block: 40px 56px;
+ }
+</style>
+
+<cr-dialog close-text="$i18n{close}" show-on-attach>
+ <share-password-dialog-header slot="title">
+ [[dialogTitle]]
+ </share-password-dialog-header>
+ <paper-spinner-lite slot="body" active></paper-spinner-lite>
+</cr-dialog>
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_loading_dialog.ts b/chromium/chrome/browser/resources/password_manager/sharing/share_password_loading_dialog.ts
new file mode 100644
index 00000000000..e6ff5097b52
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_loading_dialog.ts
@@ -0,0 +1,36 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import '../shared_style.css.js';
+import './share_password_dialog_header.js';
+
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './share_password_loading_dialog.html.js';
+
+export class SharePasswordLoadingDialogElement extends PolymerElement {
+ static get is() {
+ return 'share-password-loading-dialog';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {dialogTitle: {type: String}};
+ }
+
+ dialogTitle: string;
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'share-password-loading-dialog': SharePasswordLoadingDialogElement;
+ }
+}
+
+customElements.define(
+ SharePasswordLoadingDialogElement.is, SharePasswordLoadingDialogElement);
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_no_members_dialog.html b/chromium/chrome/browser/resources/password_manager/sharing/share_password_no_members_dialog.html
new file mode 100644
index 00000000000..67efc965820
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_no_members_dialog.html
@@ -0,0 +1,33 @@
+<style>
+ #description {
+ margin-top: 16px;
+ }
+
+ a[href] {
+ color: var(--cr-link-color);
+ }
+</style>
+
+<cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach>
+ <div slot="title">
+ <share-password-dialog-header id="header">
+ [[dialogTitle]]
+ </share-password-dialog-header>
+ </div>
+ <div slot="body">
+ <picture>
+ <!-- TODO(crbug/1445526): Add dark theme version of the image. -->
+ <img srcset="./images/password_sharing_family_banner.svg"
+ role="presentation">
+ </picture>
+ <div id="description"
+ inner-h-t-m-l="[[i18nAdvanced('sharePasswordNoMembersDescription')]]">
+ </div>
+ </div>
+ <div slot="button-container">
+ <cr-button class="action-button" on-click="onClickActionButton_"
+ id="action">
+ $i18n{sharePasswordGotIt}
+ </cr-button>
+ </div>
+</cr-dialog>
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_no_members_dialog.ts b/chromium/chrome/browser/resources/password_manager/sharing/share_password_no_members_dialog.ts
new file mode 100644
index 00000000000..b617f558d11
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_no_members_dialog.ts
@@ -0,0 +1,52 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import './share_password_dialog_header.js';
+
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './share_password_no_members_dialog.html.js';
+
+export interface SharePasswordNoMembersDialogElement {
+ $: {
+ description: HTMLElement,
+ action: HTMLElement,
+ header: HTMLElement,
+ };
+}
+
+export class SharePasswordNoMembersDialogElement extends I18nMixin
+(PolymerElement) {
+ static get is() {
+ return 'share-password-no-members-dialog';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {dialogTitle: {type: String}};
+ }
+
+ dialogTitle: string;
+
+ private onClickActionButton_() {
+ this.dispatchEvent(
+ new CustomEvent('close', {bubbles: true, composed: true}));
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'share-password-no-members-dialog': SharePasswordNoMembersDialogElement;
+ }
+}
+
+customElements.define(
+ SharePasswordNoMembersDialogElement.is,
+ SharePasswordNoMembersDialogElement);
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_recipient.html b/chromium/chrome/browser/resources/password_manager/sharing/share_password_recipient.html
new file mode 100644
index 00000000000..841b5d03751
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_recipient.html
@@ -0,0 +1,135 @@
+<style include="shared-style">
+ :host {
+ margin-top: 8px;
+ padding: 8px 24px 8px 8px;
+
+ border: 0.5px solid var(--cr-separator-color);
+ border-radius: 25px;
+
+ display: grid;
+ grid-template-columns: auto min-content;
+ column-gap: 12px;
+
+ cursor: pointer;
+ }
+
+ :host([disabled]) {
+ cursor: initial;
+ }
+
+ :host([disabled]) > .content {
+ opacity: var(--cr-disabled-opacity);
+ }
+
+ :host(:not([disabled]):not([selected]):hover) {
+ background: var(--google-grey-100);
+ }
+
+ :host([selected]) {
+ border-color: var(--google-blue-300);
+ background: var(--google-blue-50);
+ }
+
+ #checkbox {
+ display: none;
+ margin: auto;
+ --cr-checkbox-ripple-size: 36px;
+ }
+
+ :host([selected]) #checkbox,
+ :host(:not([disabled]):hover) #checkbox {
+ display: block;
+ }
+
+ :host([selected]) #avatar,
+ :host(:not([disabled]):hover) #avatar {
+ display: none;
+ }
+
+ .content {
+ display: grid;
+ grid-template-columns: 32px auto;
+ column-gap: 12px;
+ }
+
+ #avatar {
+ border-radius: 50%;
+ height: 32px;
+ margin-inline-end: 8px;
+ width: 32px;
+ }
+
+ .user-data {
+ margin-block: auto;
+ }
+
+ #name {
+ font-size: 100%;
+ color: var(--cr-primary-text-color);
+ line-height: normal;
+ }
+
+ #email {
+ font-size: 85%;
+ color: var(--cr-secondary-text-color);
+ line-height: normal;
+ }
+
+ .disabled-info {
+ margin-inline-start: auto;
+ }
+
+ #notAvailable {
+ font-size: 85%;
+ margin-top: 0.15rem;
+ white-space: nowrap;
+ }
+
+ cr-tooltip-icon {
+ --cr-icon-size: 16px;
+ margin-inline-end: 4px;
+ }
+
+ .avatar-checkbox {
+ width: 32px;
+ height: 32px;
+ margin-block: auto;
+ }
+
+ cr-checkbox::part(label-container) {
+ display: none;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ :host(:not([disabled]):not([selected]):hover) {
+ background: var(--google-grey-900);
+ }
+
+ :host([selected]) {
+ /* TODO(crbug/1445526): Clarify with UX what is the correct color. */
+ background: rgb(0, 74, 119);
+ border-color: var(--google-blue-600);
+ }
+ }
+</style>
+
+<div class="content">
+ <div class="flex-centered avatar-checkbox">
+ <cr-checkbox id="checkbox" checked="{{selected}}"></cr-checkbox>
+ <img is="cr-auto-img" id="avatar" auto-src="[[recipient.profileImageUrl]]"
+ draggable="false" alt="">
+ </div>
+ <div class="user-data text-elide">
+ <div id="name" class="text-elide">[[recipient.displayName]]</div>
+ <div id="email" class="text-elide">[[recipient.email]]</div>
+ </div>
+</div>
+<template is="dom-if" if="[[disabled]]" restamp>
+ <div id="disabled-info" class="flex-centered">
+ <cr-tooltip-icon tooltip-text="$i18n{sharePasswordMemeberUnavailable}"
+ icon-class="cr:info-outline"
+ icon-aria-label="$i18n{sharePasswordMemeberUnavailable}">
+ </cr-tooltip-icon>
+ <span id="notAvailable">$i18n{sharePasswordNotAvailable}</span>
+ </div>
+</template>
diff --git a/chromium/chrome/browser/resources/password_manager/sharing/share_password_recipient.ts b/chromium/chrome/browser/resources/password_manager/sharing/share_password_recipient.ts
new file mode 100644
index 00000000000..aff115181a8
--- /dev/null
+++ b/chromium/chrome/browser/resources/password_manager/sharing/share_password_recipient.ts
@@ -0,0 +1,96 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js';
+import 'chrome://resources/cr_elements/policy/cr_tooltip_icon.js';
+import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import '../shared_style.css.js';
+
+import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './share_password_recipient.html.js';
+
+export interface SharePasswordRecipientElement {
+ $: {
+ avatar: HTMLImageElement,
+ name: HTMLElement,
+ email: HTMLElement,
+ checkbox: CrCheckboxElement,
+ };
+}
+
+export class SharePasswordRecipientElement extends PolymerElement {
+ static get is() {
+ return 'share-password-recipient';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ disabled: {
+ type: Boolean,
+ value: false,
+ },
+
+ recipient: Object,
+
+ selected: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ notify: true,
+ },
+ };
+ }
+ disabled: boolean;
+ selected: boolean;
+ recipient: chrome.passwordsPrivate.RecipientInfo;
+
+ override ready() {
+ super.ready();
+ this.addEventListener('click', this.onClick_.bind(this));
+ this.addEventListener('mouseover', this.onMouseOver_.bind(this));
+ this.addEventListener('mouseout', this.onMouseOut_.bind(this));
+ }
+
+ private onClick_(e: Event) {
+ if (this.disabled) {
+ return;
+ }
+
+ e.preventDefault();
+ this.$.checkbox.click();
+ }
+
+ private onMouseOver_(e: Event) {
+ if (this.disabled) {
+ return;
+ }
+
+ e.preventDefault();
+ this.$.checkbox.getRipple().showAndHoldDown();
+ }
+
+ private onMouseOut_(e: Event) {
+ if (this.disabled) {
+ return;
+ }
+
+ e.preventDefault();
+ this.$.checkbox.getRipple().clear();
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'share-password-recipient': SharePasswordRecipientElement;
+ }
+}
+
+customElements.define(
+ SharePasswordRecipientElement.is, SharePasswordRecipientElement);
diff --git a/chromium/chrome/browser/resources/password_manager/site_favicon.ts b/chromium/chrome/browser/resources/password_manager/site_favicon.ts
index 6606745b187..cbce0645721 100644
--- a/chromium/chrome/browser/resources/password_manager/site_favicon.ts
+++ b/chromium/chrome/browser/resources/password_manager/site_favicon.ts
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/chromium/chrome/browser/resources/password_manager/toolbar.html b/chromium/chrome/browser/resources/password_manager/toolbar.html
index 98bdeae0911..acb64bba50e 100644
--- a/chromium/chrome/browser/resources/password_manager/toolbar.html
+++ b/chromium/chrome/browser/resources/password_manager/toolbar.html
@@ -18,7 +18,7 @@
}
</style>
<cr-toolbar id="mainToolbar" on-keydown="onKeyDown_"
- page-name="$i18n{title}" clear-label="$i18n{clearSearch}"
+ page-name="$i18n{passwordManagerString}" clear-label="$i18n{clearSearch}"
search-prompt="$i18n{searchPrompt}" menu-label="$i18n{menuButtonLabel}"
autofocus on-search-changed="onSearchChanged_"
role="banner" show-menu="[[narrow]]" narrow="[[narrow]]"
diff --git a/chromium/chrome/browser/resources/pdf/BUILD.gn b/chromium/chrome/browser/resources/pdf/BUILD.gn
index cb228efb9bc..89ec1309a51 100644
--- a/chromium/chrome/browser/resources/pdf/BUILD.gn
+++ b/chromium/chrome/browser/resources/pdf/BUILD.gn
@@ -21,6 +21,8 @@ build_webui("build") {
}
web_component_files = [
+ "elements/viewer-attachment.ts",
+ "elements/viewer-attachment-bar.ts",
"elements/viewer-bookmark.ts",
"elements/viewer-document-outline.ts",
"elements/viewer-download-controls.ts",
diff --git a/chromium/chrome/browser/resources/pdf/elements/icons.html b/chromium/chrome/browser/resources/pdf/elements/icons.html
index 5f2144d0fd9..6517091c7af 100644
--- a/chromium/chrome/browser/resources/pdf/elements/icons.html
+++ b/chromium/chrome/browser/resources/pdf/elements/icons.html
@@ -6,6 +6,7 @@
See http://goo.gl/Y1OdAq for instructions on adding additional icons.
-->
<g id="add"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path></g>
+ <g id="attach-file"><path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"></path></g>
<g id="bookmark"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"></path></g>
<g id="bookmark-border"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"></path></g>
<g id="check"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path></g>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-attachment-bar.html b/chromium/chrome/browser/resources/pdf/elements/viewer-attachment-bar.html
new file mode 100644
index 00000000000..c9288127d6c
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-attachment-bar.html
@@ -0,0 +1,20 @@
+<style include="pdf-shared">
+ :host {
+ display: block;
+ padding-inline-end: 20px;
+ padding-top: 20px;
+ }
+
+ #warning {
+ align-items: flex-start;
+ padding: 5px 28px 15px;
+ position: relative;
+ }
+</style>
+<div id="warning" hidden="[[!exceedSizeLimit_]]">
+ $i18n{oversizeAttachmentWarning}
+</div>
+<template is="dom-repeat" items="[[attachments]]">
+ <viewer-attachment attachment="[[item]]" index="{{index}}">
+ </viewer-attachment>
+</template>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-attachment-bar.ts b/chromium/chrome/browser/resources/pdf/elements/viewer-attachment-bar.ts
new file mode 100644
index 00000000000..2db74492306
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-attachment-bar.ts
@@ -0,0 +1,50 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import './pdf-shared.css.js';
+import './viewer-attachment.js';
+
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {Attachment} from '../constants.js';
+
+import {getTemplate} from './viewer-attachment-bar.html.js';
+
+export class ViewerAttachmentBarElement extends PolymerElement {
+ static get is() {
+ return 'viewer-attachment-bar';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ attachments: Array,
+
+ exceedSizeLimit_: {
+ type: Boolean,
+ computed: 'computeExceedSizeLimit_(attachments)',
+ },
+ };
+ }
+
+ attachments: Attachment[];
+ private exceedSizeLimit_: boolean;
+
+ /* Indicates whether any oversized attachments exist */
+ private computeExceedSizeLimit_(): boolean {
+ return this.attachments.some(attachment => attachment.size === -1);
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'viewer-attachment-bar': ViewerAttachmentBarElement;
+ }
+}
+
+customElements.define(
+ ViewerAttachmentBarElement.is, ViewerAttachmentBarElement);
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-attachment.html b/chromium/chrome/browser/resources/pdf/elements/viewer-attachment.html
new file mode 100644
index 00000000000..daa915adbbb
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-attachment.html
@@ -0,0 +1,45 @@
+<style include="cr-hidden-style">
+ #item {
+ align-items: flex-start;
+ display: flex;
+ padding: 5px 28px;
+ position: relative;
+ transition: background-color 100ms ease-out;
+ }
+
+ #item:hover {
+ background-color: rgba(255, 255, 255, 0.25);
+ }
+
+ #title {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ :host(:not([save-allowed_])) #title {
+ opacity: var(--cr-disabled-opacity);
+ }
+
+ #download {
+ --cr-icon-button-fill-color: var(--primary-text-color);
+ --cr-icon-button-icon-size: 16px;
+ --cr-icon-button-size: 28px;
+ margin: 0;
+ position: absolute;
+ right: 0;
+ /* Vertically aligns the expand icon with the first line of #title. */
+ top: calc((100% - var(--cr-icon-button-size)) / 2);
+ }
+
+ #download:focus-visible {
+ outline: auto -webkit-focus-ring-color;
+ }
+</style>
+<div id="item">
+ <span id="title">[[attachment.name]]</span>
+ <cr-icon-button id="download" tabindex="0" hidden$="[[!saveAllowed_]]"
+ title="$i18n{tooltipDownloadAttachment}" iron-icon="cr:file-download"
+ on-click="onDownloadClick_">
+ </cr-icon-button>
+</div>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-attachment.ts b/chromium/chrome/browser/resources/pdf/elements/viewer-attachment.ts
new file mode 100644
index 00000000000..ae5b0b2a9d0
--- /dev/null
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-attachment.ts
@@ -0,0 +1,81 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/cr_elements/icons.html.js';
+
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {Attachment} from '../constants.js';
+
+import {getTemplate} from './viewer-attachment.html.js';
+
+export interface SaveAttachment {
+ index: number;
+}
+
+declare global {
+ interface HTMLElementEventMap {
+ 'save-attachment': CustomEvent<SaveAttachment>;
+ }
+}
+
+export interface ViewerAttachmentElement {
+ $: {
+ title: HTMLElement,
+ download: HTMLElement,
+ };
+}
+
+export class ViewerAttachmentElement extends PolymerElement {
+ static get is() {
+ return 'viewer-attachment';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ attachment: Object,
+
+ index: Number,
+
+ saveAllowed_: {
+ type: Boolean,
+ reflectToAttribute: true,
+ computed: 'computeSaveAllowed_(attachment.size)',
+ },
+ };
+ }
+
+ attachment: Attachment;
+ index: number;
+ private saveAllowed_: boolean;
+
+ /** Indicate whether the attachment can be downloaded. */
+ private computeSaveAllowed_(): boolean {
+ return this.attachment.size !== -1;
+ }
+
+ private onDownloadClick_() {
+ if (this.attachment.size === -1) {
+ return;
+ }
+ this.dispatchEvent(new CustomEvent(
+ 'save-attachment',
+ {detail: this.index, bubbles: true, composed: true}));
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'viewer-attachment': ViewerAttachmentElement;
+ }
+}
+
+customElements.define(ViewerAttachmentElement.is, ViewerAttachmentElement);
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.html
index d014a10289a..bdeb2addb81 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.html
@@ -63,33 +63,29 @@
margin: 0 auto;
}
</style>
-<div id="icons" hidden$="[[!bookmarks.length]]" role="tablist">
- <div class$="
- button-wrapper cr-vertical-tab [[thumbnailButtonClass_(thumbnailView_)]]">
- <cr-icon-button iron-icon="pdf:thumbnails" role="tab"
- title="$i18n{tooltipThumbnails}" aria-label="$i18n{tooltipThumbnails}"
- aria-selected$="[[getAriaSelectedThumbnails_(thumbnailView_)]]"
- on-click="onThumbnailClick_"
- tabindex$="[[getTabIndexThumbnail_(thumbnailView_)]]">
- </cr-icon-button>
- </div>
- <div class$="
- button-wrapper cr-vertical-tab [[outlineButtonClass_(thumbnailView_)]]">
- <cr-icon-button iron-icon="pdf:doc-outline" role="tab"
- title="$i18n{tooltipDocumentOutline}"
- aria-label="$i18n{tooltipDocumentOutline}"
- aria-selected$="[[getAriaSelectedOutline_(thumbnailView_)]]"
- on-click="onOutlineClick_"
- tabindex$="[[getTabIndexOutline_(thumbnailView_)]]">
- </cr-icon-button>
- </div>
+<div id="icons" hidden$="[[hideIcons_]]" role="tablist">
+ <template is="dom-repeat" items="[[tabs_]]">
+ <div class$="button-wrapper cr-vertical-tab
+ [[getTabSelectedClass_(item.id, selectedTab_)]]">
+ <cr-icon-button iron-icon="[[item.icon]]" role="tab"
+ title$="[[item.title]]"
+ aria-selected$="[[getTabAriaSelected_(item.id, selectedTab_)]]"
+ tabindex$="[[getTabIndex_(item.id, selectedTab_)]]"
+ on-click="onTabClick_">
+ </cr-icon-button>
+ </div>
+ </template>
</div>
<div id="content">
<viewer-thumbnail-bar id="thumbnail-bar" tabindex="0"
- hidden="[[!thumbnailView_]]" active-page="[[activePage]]"
+ hidden="[[hideThumbnailView_(selectedTab_)]]" active-page="[[activePage]]"
clockwise-rotations="[[clockwiseRotations]]" doc-length="[[docLength]]">
</viewer-thumbnail-bar>
- <viewer-document-outline id="outline" hidden="[[thumbnailView_]]"
- bookmarks="[[bookmarks]]">
+ <viewer-document-outline id="outline"
+ hidden="[[hideOutlineView_(selectedTab_)]]" bookmarks="[[bookmarks]]">
</viewer-document-outline>
+ <viewer-attachment-bar id="attachment-bar"
+ hidden="[[hideAttachmentView_(selectedTab_)]]"
+ attachments="[[attachments]]">
+ </viewer-attachment-bar>
</div>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.ts b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.ts
index d26c178c36d..140ebb7e350 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.ts
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.ts
@@ -5,19 +5,33 @@
import './shared-vars.css.js';
import '../pdf_viewer_shared_style.css.js';
import './icons.html.js';
+import './viewer-attachment-bar.js';
import './viewer-document-outline.js';
import './viewer-thumbnail-bar.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_style.css.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {Bookmark} from '../bookmark_type.js';
+import {Attachment} from '../constants.js';
import {record, UserAction} from '../metrics.js';
import {getTemplate} from './viewer-pdf-sidenav.html.js';
+enum TabId {
+ THUMBNAIL = 0,
+ OUTLINE = 1,
+ ATTACHMENT = 2,
+}
+
+interface Tab {
+ id: number;
+ title: string;
+ icon: string;
+}
+
export interface ViewerPdfSidenavElement {
$: {
icons: HTMLElement,
@@ -37,6 +51,11 @@ export class ViewerPdfSidenavElement extends PolymerElement {
return {
activePage: Number,
+ attachments: {
+ type: Array,
+ value: () => [],
+ },
+
bookmarks: {
type: Array,
value: () => [],
@@ -46,18 +65,31 @@ export class ViewerPdfSidenavElement extends PolymerElement {
docLength: Number,
- thumbnailView_: {
+ hideIcons_: {
type: Boolean,
- value: true,
+ computed: 'computeHideIcons_(tabs_.length)',
+ },
+
+ tabs_: {
+ type: Array,
+ computed: `computeTabs_(bookmarks.length, attachments.length)`,
+ },
+
+ selectedTab_: {
+ type: Number,
+ value: 0,
},
};
}
activePage: number;
+ attachments: Attachment[];
bookmarks: Bookmark[];
clockwiseRotations: number;
docLength: number;
- private thumbnailView_: boolean;
+ private hideIcons_: boolean;
+ private selectedTab_: number;
+ private tabs_: Tab[];
override ready() {
super.ready();
@@ -65,50 +97,107 @@ export class ViewerPdfSidenavElement extends PolymerElement {
this.$.icons.addEventListener('keydown', this.onKeydown_.bind(this));
}
- private onThumbnailClick_() {
- record(UserAction.SELECT_SIDENAV_THUMBNAILS);
- this.thumbnailView_ = true;
+ private computeTabs_(): Tab[] {
+ const tabs = [
+ {
+ id: TabId.THUMBNAIL,
+ icon: 'pdf:thumbnails',
+ title: '$i18n{tooltipThumbnails}',
+ },
+ ];
+
+ if (this.bookmarks.length > 0) {
+ tabs.push({
+ id: TabId.OUTLINE,
+ icon: 'pdf:doc-outline',
+ title: '$i18n{tooltipDocumentOutline}',
+ });
+ }
+
+ if (this.attachments.length > 0) {
+ tabs.push({
+ id: TabId.ATTACHMENT,
+ icon: 'pdf:attach-file',
+ title: '$i18n{tooltipAttachments}',
+ });
+ }
+ return tabs;
+ }
+
+ private computeHideIcons_(): boolean {
+ return this.tabs_.length === 1;
}
- private onOutlineClick_() {
- record(UserAction.SELECT_SIDENAV_OUTLINE);
- this.thumbnailView_ = false;
+ private getTabAriaSelected_(tabId: number): string {
+ return this.tabs_[this.selectedTab_].id === tabId ? 'true' : 'false';
}
- private outlineButtonClass_(): string {
- return this.thumbnailView_ ? '' : 'selected';
+ private getTabIndex_(tabId: number): string {
+ return this.tabs_[this.selectedTab_].id === tabId ? '0' : '-1';
}
- private thumbnailButtonClass_(): string {
- return this.thumbnailView_ ? 'selected' : '';
+ private getTabSelectedClass_(tabId: number): string {
+ return this.tabs_[this.selectedTab_].id === tabId ? 'selected' : '';
}
- private getAriaSelectedThumbnails_(): string {
- return this.thumbnailView_ ? 'true' : 'false';
+ private onTabClick_(e: DomRepeatEvent<Tab>) {
+ const targetTab = e.model.item;
+ switch (targetTab.id) {
+ case TabId.THUMBNAIL:
+ record(UserAction.SELECT_SIDENAV_THUMBNAILS);
+ this.selectedTab_ = 0;
+ break;
+
+ case TabId.OUTLINE:
+ record(UserAction.SELECT_SIDENAV_OUTLINE);
+ this.selectedTab_ = 1;
+ break;
+
+ case TabId.ATTACHMENT:
+ record(UserAction.SELECT_SIDENAV_ATTACHMENT);
+ this.selectedTab_ = this.tabs_.length - 1;
+ break;
+ }
}
- private getAriaSelectedOutline_(): string {
- return this.thumbnailView_ ? 'false' : 'true';
+ private hideThumbnailView_(): boolean {
+ return this.tabs_[this.selectedTab_].id !== TabId.THUMBNAIL;
}
- private getTabIndexThumbnail_(): string {
- return this.thumbnailView_ ? '0' : '-1';
+ private hideOutlineView_(): boolean {
+ return this.tabs_[this.selectedTab_].id !== TabId.OUTLINE;
}
- private getTabIndexOutline_(): string {
- return this.thumbnailView_ ? '-1' : '0';
+ private hideAttachmentView_(): boolean {
+ return this.tabs_[this.selectedTab_].id !== TabId.ATTACHMENT;
}
private onKeydown_(e: KeyboardEvent) {
- // Up and down arrows should toggle between thumbnail and outline
- // when sidenav is open and an outline exists.
- if ((e.key === 'ArrowUp' || e.key === 'ArrowDown') &&
- this.bookmarks.length > 0) {
- e.preventDefault();
- e.stopPropagation();
- this.thumbnailView_ = !this.thumbnailView_;
+ if (this.hideIcons_ || (e.key !== 'ArrowUp' && e.key !== 'ArrowDown')) {
+ return;
+ }
+
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (e.key === 'ArrowUp') {
+ if (this.selectedTab_ === 0) {
+ this.selectedTab_ = this.tabs_.length - 1;
+ } else {
+ this.selectedTab_--;
+ }
+ } else {
+ if (this.selectedTab_ === this.tabs_.length - 1) {
+ this.selectedTab_ = 0;
+ } else {
+ this.selectedTab_++;
+ }
}
}
+
+ getHideIconsForTesting(): boolean {
+ return this.hideIcons_;
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.html b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.html
index 66e59117d89..1a485ecdec7 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.html
@@ -320,7 +320,8 @@
<hr>
- <button id="present-button" class="dropdown-item" on-click="onPresentClick_">
+ <button id="present-button" class="dropdown-item" on-click="onPresentClick_"
+ disabled="[[!presentationModeAvailable_]]">
<span class="check-container" aria-hidden="true"></span>
$i18n{present}
</button>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.ts b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.ts
index 404ff79a22f..582ac7e3203 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.ts
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar.ts
@@ -42,6 +42,7 @@ export interface ViewerToolbarElement {
$: {
sidenavToggle: HTMLElement,
menu: CrActionMenuElement,
+ 'present-button': HTMLButtonElement,
'two-page-view-button': HTMLButtonElement,
};
}
@@ -65,8 +66,10 @@ export class ViewerToolbarElement extends PolymerElement {
reflectToAttribute: true,
},
// </if>
+
docTitle: String,
docLength: Number,
+ embeddedViewer: Boolean,
hasEdits: Boolean,
hasEnteredAnnotationMode: Boolean,
isFormFieldFocused: Boolean,
@@ -86,6 +89,18 @@ export class ViewerToolbarElement extends PolymerElement {
// <if expr="enable_screen_ai_service">
pdfOcrEnabled: Boolean,
// </if>
+
+ presentationModeAvailable_: {
+ type: Boolean,
+ // <if expr="enable_ink">
+ computed: 'computePresentationModeAvailable_(' +
+ 'annotationMode, embeddedViewer)',
+ // </if>
+ // <if expr="not enable_ink">
+ computed: 'computePresentationModeAvailable_(embeddedViewer)',
+ //</if>
+ },
+
printingEnabled: Boolean,
rotated: Boolean,
viewportZoom: Number,
@@ -136,6 +151,7 @@ export class ViewerToolbarElement extends PolymerElement {
docTitle: string;
docLength: number;
+ embeddedViewer: boolean;
hasEdits: boolean;
hasEnteredAnnotationMode: boolean;
isFormFieldFocused: boolean;
@@ -152,6 +168,7 @@ export class ViewerToolbarElement extends PolymerElement {
private fittingType_: FittingType = FittingType.FIT_TO_PAGE;
private fitToButtonIcon_: string;
private moreMenuOpen_: boolean = false;
+ private presentationModeAvailable_: boolean;
private loading_: boolean = true;
private viewportZoomPercent_: number;
@@ -410,6 +427,19 @@ export class ViewerToolbarElement extends PolymerElement {
this.pdfOcrAlwaysActive_ = isPdfOcrAlwaysActive;
}
// </if>
+
+ /**
+ * Updates the toolbar's presentation mode available flag depending on current
+ * conditions.
+ */
+ private computePresentationModeAvailable_() {
+ // <if expr="enable_ink">
+ return !this.annotationMode && !this.embeddedViewer;
+ // </if>
+ // <if expr="not enable_ink">
+ return !this.embeddedViewer;
+ // </if>
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/pdf/index.html b/chromium/chrome/browser/resources/pdf/index.html
index cc6197163fd..ba663b1cf6b 100644
--- a/chromium/chrome/browser/resources/pdf/index.html
+++ b/chromium/chrome/browser/resources/pdf/index.html
@@ -1,5 +1,6 @@
<!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<html dir="$i18n{textdirection}" lang="$i18n{language}"
+ $i18n{chromeRefresh2023Attribute}>
<head>
<meta charset="utf-8">
diff --git a/chromium/chrome/browser/resources/pdf/metrics.ts b/chromium/chrome/browser/resources/pdf/metrics.ts
index 88d1f8fec3e..6ceb137857f 100644
--- a/chromium/chrome/browser/resources/pdf/metrics.ts
+++ b/chromium/chrome/browser/resources/pdf/metrics.ts
@@ -212,7 +212,11 @@ export enum UserAction {
PROPERTIES_FIRST = 63,
PROPERTIES = 64,
- NUMBER_OF_ACTIONS = 65,
+ // Recorded when the attachment button in the sidenav is clicked.
+ SELECT_SIDENAV_ATTACHMENT_FIRST = 65,
+ SELECT_SIDENAV_ATTACHMENT = 66,
+
+ NUMBER_OF_ACTIONS = 67,
}
function createFirstMap(): Map<UserAction, UserAction> {
diff --git a/chromium/chrome/browser/resources/pdf/pdf_viewer.html b/chromium/chrome/browser/resources/pdf/pdf_viewer.html
index 31a7d4a6d97..cf90d7318f0 100644
--- a/chromium/chrome/browser/resources/pdf/pdf_viewer.html
+++ b/chromium/chrome/browser/resources/pdf/pdf_viewer.html
@@ -129,7 +129,8 @@
</style>
<viewer-toolbar id="toolbar" annotation-mode="[[annotationMode_]]"
- doc-title="[[title_]]" doc-length="[[docLength_]]" page-no="[[pageNo_]]"
+ doc-title="[[title_]]" doc-length="[[docLength_]]"
+ embedded-viewer="[[embedded_]]" page-no="[[pageNo_]]"
load-progress="[[loadProgress_]]" has-edits="[[hasEdits_]]"
has-entered-annotation-mode="[[hasEnteredAnnotationMode_]]"
printing-enabled="[[printingEnabled_]]"
@@ -165,10 +166,11 @@
<div id="sidenav-container" closed$="[[sidenavCollapsed_]]"
hidden$="[[!toolbarEnabled_]]">
<viewer-pdf-sidenav id="sidenav"
- active-page="[[pageNo_]]" bookmarks="[[bookmarks_]]"
- clockwise-rotations="[[clockwiseRotations_]]"
+ active-page="[[pageNo_]]" attachments="[[attachments_]]"
+ bookmarks="[[bookmarks_]]" clockwise-rotations="[[clockwiseRotations_]]"
doc-length="[[docLength_]]" on-change-page="onChangePage_"
- on-change-page-and-xy="onChangePageAndXy_" on-navigate="onNavigate_">
+ on-change-page-and-xy="onChangePageAndXy_" on-navigate="onNavigate_"
+ on-save-attachment="onSaveAttachment_">
</viewer-pdf-sidenav>
</div>
<div id="main">
diff --git a/chromium/chrome/browser/resources/pdf/pdf_viewer.ts b/chromium/chrome/browser/resources/pdf/pdf_viewer.ts
index b68423ffe09..b9d1652cfc2 100644
--- a/chromium/chrome/browser/resources/pdf/pdf_viewer.ts
+++ b/chromium/chrome/browser/resources/pdf/pdf_viewer.ts
@@ -249,6 +249,7 @@ export class PdfViewerElement extends PdfViewerBaseElement {
private docLength_: number;
private documentHasFocus_: boolean;
private documentMetadata_: DocumentMetadata;
+ private embedded_: boolean;
private fileName_: string;
private hadPassword_: boolean;
private hasEdits_: boolean;
@@ -337,6 +338,8 @@ export class PdfViewerElement extends PdfViewerBaseElement {
if (chrome.mimeHandlerPrivate && chrome.mimeHandlerPrivate.onSave) {
chrome.mimeHandlerPrivate.onSave.addListener(this.onSave_.bind(this));
}
+
+ this.embedded_ = this.browserApi!.getStreamInfo().embedded;
}
handleKeyEvent(e: KeyboardEvent) {
diff --git a/chromium/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts b/chromium/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
index 53020dae03e..f189597506d 100644
--- a/chromium/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
+++ b/chromium/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
@@ -9,8 +9,10 @@ export {AnnotationTool} from './annotation_tool.js';
// </if>
export {Bookmark} from './bookmark_type.js';
export {BrowserApi, ZoomBehavior} from './browser_api.js';
-export {FittingType, Point, Rect, SaveRequestType} from './constants.js';
+export {Attachment, FittingType, Point, Rect, SaveRequestType} from './constants.js';
export {PluginController} from './controller.js';
+export {ViewerAttachmentBarElement} from './elements/viewer-attachment-bar.js';
+export {ViewerAttachmentElement} from './elements/viewer-attachment.js';
export {ChangePageAndXyDetail, ChangePageDetail, ChangePageOrigin, ChangeZoomDetail, NavigateDetail, ViewerBookmarkElement} from './elements/viewer-bookmark.js';
export {ViewerDocumentOutlineElement} from './elements/viewer-document-outline.js';
export {ViewerDownloadControlsElement} from './elements/viewer-download-controls.js';
diff --git a/chromium/chrome/browser/resources/print_preview/BUILD.gn b/chromium/chrome/browser/resources/print_preview/BUILD.gn
index 68817c4bc08..2e4a32e0788 100644
--- a/chromium/chrome/browser/resources/print_preview/BUILD.gn
+++ b/chromium/chrome/browser/resources/print_preview/BUILD.gn
@@ -33,6 +33,7 @@ build_webui("build") {
"ui/margin_control_container.ts",
"ui/margins_settings.ts",
"ui/media_size_settings.ts",
+ "ui/media_type_settings.ts",
"ui/more_settings.ts",
"ui/number_settings_section.ts",
"ui/other_options_settings.ts",
@@ -52,6 +53,7 @@ build_webui("build") {
"ui/destination_list_item_cros.ts",
"ui/destination_select_cros.ts",
"ui/pin_settings.ts",
+ "ui/printer_setup_info_cros.ts",
"ui/provisional_destination_resolver.ts",
"ui/destination_dialog_cros.ts",
]
diff --git a/chromium/chrome/browser/resources/print_preview/COMMON_METADATA b/chromium/chrome/browser/resources/print_preview/COMMON_METADATA
new file mode 100644
index 00000000000..1d690de0e57
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/COMMON_METADATA
@@ -0,0 +1,3 @@
+monorail: {
+ component: "UI>Browser>PrintPreview"
+}
diff --git a/chromium/chrome/browser/resources/print_preview/DIR_METADATA b/chromium/chrome/browser/resources/print_preview/DIR_METADATA
index 1d690de0e57..21bc64e212d 100644
--- a/chromium/chrome/browser/resources/print_preview/DIR_METADATA
+++ b/chromium/chrome/browser/resources/print_preview/DIR_METADATA
@@ -1,3 +1 @@
-monorail: {
- component: "UI>Browser>PrintPreview"
-}
+mixins: "//chrome/browser/resources/print_preview/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/print_preview/data/cdd.ts b/chromium/chrome/browser/resources/print_preview/data/cdd.ts
index 0f048b9641b..d1645604d5a 100644
--- a/chromium/chrome/browser/resources/print_preview/data/cdd.ts
+++ b/chromium/chrome/browser/resources/print_preview/data/cdd.ts
@@ -101,12 +101,25 @@ export type SelectOption = {
export type MediaSizeOption = {
type?: string,
vendor_id?: string, height_microns: number, width_microns: number,
+ imageable_area_left_microns?: number,
+ imageable_area_bottom_microns?: number,
+ imageable_area_right_microns?: number,
+ imageable_area_top_microns?: number,
+ has_borderless_variant?: boolean,
}&SelectOption;
export type MediaSizeCapability = {
option: MediaSizeOption[],
}&CapabilityWithReset;
+export type MediaTypeOption = {
+ vendor_id: string,
+}&SelectOption;
+
+export type MediaTypeCapability = {
+ option: MediaTypeOption[],
+}&CapabilityWithReset;
+
export type DpiOption = {
vendor_id?: string, horizontal_dpi: number, vertical_dpi: number,
}&OptionWithDefault;
@@ -133,6 +146,7 @@ export interface CddCapabilities {
duplex?: DuplexCapability;
page_orientation?: PageOrientationCapability;
media_size?: MediaSizeCapability;
+ media_type?: MediaTypeCapability;
dpi?: DpiCapability;
// <if expr="is_chromeos">
pin?: PinCapability;
diff --git a/chromium/chrome/browser/resources/print_preview/data/destination_store.ts b/chromium/chrome/browser/resources/print_preview/data/destination_store.ts
index 09213df888d..845b902aaf7 100644
--- a/chromium/chrome/browser/resources/print_preview/data/destination_store.ts
+++ b/chromium/chrome/browser/resources/print_preview/data/destination_store.ts
@@ -302,8 +302,8 @@ export class DestinationStore extends EventTarget {
* will be automatically selected.
* @param pdfPrinterDisabled Whether the PDF print destination is
* disabled in print preview.
- * @param isDriveMounted Whether Google Drive is mounted. Only used
- on Chrome OS.
+ * @param saveToDriveDisabled Whether the 'Save to Google Drive' destination
+ * is disabled in print preview. Only used on Chrome OS.
* @param systemDefaultDestinationId ID of the system default
* destination.
* @param serializedDefaultDestinationSelectionRulesStr Serialized
@@ -313,10 +313,10 @@ export class DestinationStore extends EventTarget {
init(
pdfPrinterDisabled: boolean,
// <if expr="is_chromeos">
- isDriveMounted: boolean,
+ saveToDriveDisabled: boolean,
// </if>
// <if expr="not is_chromeos">
- _isDriveMounted: boolean,
+ _saveToDriveDisabled: boolean,
// </if>
systemDefaultDestinationId: string,
serializedDefaultDestinationSelectionRulesStr: string|null,
@@ -355,7 +355,7 @@ export class DestinationStore extends EventTarget {
this.pdfPrinterEnabled_ = !pdfPrinterDisabled;
this.createLocalPdfPrintDestination_();
// <if expr="is_chromeos">
- if (isDriveMounted) {
+ if (!saveToDriveDisabled) {
this.createLocalDrivePrintDestination_();
}
// </if>
diff --git a/chromium/chrome/browser/resources/print_preview/data/document_info.ts b/chromium/chrome/browser/resources/print_preview/data/document_info.ts
index d901de2aaf0..c4732c41192 100644
--- a/chromium/chrome/browser/resources/print_preview/data/document_info.ts
+++ b/chromium/chrome/browser/resources/print_preview/data/document_info.ts
@@ -6,7 +6,7 @@ import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {Coordinate2d} from './coordinate2d.js';
-import {CustomMarginsOrientation, Margins} from './margins.js';
+import {Margins} from './margins.js';
import {PrintableArea} from './printable_area.js';
import {Size} from './size.js';
@@ -161,14 +161,20 @@ export class PrintPreviewDocumentInfoElement extends
const size =
new Size(pageLayout.printableAreaWidth, pageLayout.printableAreaHeight);
- const margins = new Margins(
- Math.round(pageLayout.marginTop), Math.round(pageLayout.marginRight),
- Math.round(pageLayout.marginBottom), Math.round(pageLayout.marginLeft));
-
- const o = CustomMarginsOrientation;
const pageSize = new Size(
- pageLayout.contentWidth + margins.get(o.LEFT) + margins.get(o.RIGHT),
- pageLayout.contentHeight + margins.get(o.TOP) + margins.get(o.BOTTOM));
+ Math.round(
+ pageLayout.contentWidth + pageLayout.marginLeft +
+ pageLayout.marginRight),
+ Math.round(
+ pageLayout.contentHeight + pageLayout.marginTop +
+ pageLayout.marginBottom));
+
+ // Note that `Margins` stores rounded margin values, which is not
+ // appropriate for use with `pageSize` above, as that could cause rounding
+ // errors.
+ const margins = new Margins(
+ pageLayout.marginTop, pageLayout.marginRight, pageLayout.marginBottom,
+ pageLayout.marginLeft);
if (this.isInitialized_) {
this.printableArea = new PrintableArea(origin, size);
diff --git a/chromium/chrome/browser/resources/print_preview/data/margins.ts b/chromium/chrome/browser/resources/print_preview/data/margins.ts
index 0ae69919993..7e0eb442fca 100644
--- a/chromium/chrome/browser/resources/print_preview/data/margins.ts
+++ b/chromium/chrome/browser/resources/print_preview/data/margins.ts
@@ -40,7 +40,9 @@ type MarginsObject = {
export class Margins {
/**
- * Backing store for the margin values in points.
+ * Backing store for the margin values in points. The numbers are stored as
+ * integer values, because that is what the C++ `printing::PageMargins` class
+ * expects.
*/
private value_: MarginsObject = {top: 0, bottom: 0, left: 0, right: 0};
@@ -48,21 +50,12 @@ export class Margins {
* Creates a Margins object that holds four margin values in points.
*/
constructor(top: number, right: number, bottom: number, left: number) {
- this.value_ = {top, right, bottom, left};
- }
-
- /**
- * Parses a margins object from the given serialized state.
- * @param state Serialized representation of the margins created by
- * the {@code serialize} method.
- * @return New margins instance.
- */
- static parse(state: MarginsObject): Margins {
- return new Margins(
- state[CustomMarginsOrientation.TOP] || 0,
- state[CustomMarginsOrientation.RIGHT] || 0,
- state[CustomMarginsOrientation.BOTTOM] || 0,
- state[CustomMarginsOrientation.LEFT] || 0);
+ this.value_ = {
+ top: Math.round(top),
+ right: Math.round(right),
+ bottom: Math.round(bottom),
+ left: Math.round(left),
+ };
}
/**
diff --git a/chromium/chrome/browser/resources/print_preview/data/model.ts b/chromium/chrome/browser/resources/print_preview/data/model.ts
index 209288e349d..7ab1fa1867d 100644
--- a/chromium/chrome/browser/resources/print_preview/data/model.ts
+++ b/chromium/chrome/browser/resources/print_preview/data/model.ts
@@ -3,9 +3,7 @@
// found in the LICENSE file.
import {assert} from 'chrome://resources/js/assert_ts.js';
-// <if expr="is_chromeos">
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-// </if>
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -13,7 +11,7 @@ import {BackgroundGraphicsModeRestriction, Policies} from '../native_layer.js';
// <if expr="is_chromeos">
import {ColorModeRestriction, DuplexModeRestriction, PinModeRestriction} from '../native_layer.js';
// </if>
-import {CapabilityWithReset, Cdd, CddCapabilities, ColorOption, DpiOption, DuplexOption, MediaSizeOption} from './cdd.js';
+import {CapabilityWithReset, Cdd, CddCapabilities, ColorOption, DpiOption, DuplexOption, MediaSizeOption, MediaTypeOption} from './cdd.js';
import {Destination, DestinationOrigin, GooglePromotedDestinationId, PrinterType, RecentDestination} from './destination.js';
import {DocumentSettings} from './document_info.js';
import {CustomMarginsOrientation, Margins, MarginsSetting, MarginsType} from './margins.js';
@@ -48,6 +46,8 @@ export interface Settings {
color: Setting;
customMargins: Setting;
mediaSize: Setting;
+ borderless: Setting;
+ mediaType: Setting;
margins: Setting;
dpi: Setting;
scaling: Setting;
@@ -74,6 +74,8 @@ export interface SerializedSettings {
recentDestinations?: RecentDestination[];
dpi?: DpiOption;
mediaSize?: MediaSizeOption;
+ borderless?: boolean;
+ mediaType?: MediaTypeOption;
marginsType?: MarginsType;
customMargins?: MarginsSetting;
isColorEnabled?: boolean;
@@ -140,6 +142,7 @@ export interface MediaSizeValue {
imageable_area_bottom_microns?: number;
imageable_area_right_microns?: number;
imageable_area_top_microns?: number;
+ has_borderless_variant?: boolean;
}
export interface Ticket {
@@ -162,6 +165,8 @@ export interface Ticket {
scalingType: ScalingType;
shouldPrintBackgrounds: boolean;
shouldPrintSelectionOnly: boolean;
+ borderless?: boolean;
+ mediaType?: string;
advancedSettings?: object;
capabilities?: string;
marginsCustom?: MarginsSetting;
@@ -222,6 +227,7 @@ export function whenReady(): Promise<void> {
*/
const STICKY_SETTING_NAMES: string[] = [
'recentDestinations',
+ 'borderless',
'collate',
'color',
'cssBackground',
@@ -233,6 +239,7 @@ const STICKY_SETTING_NAMES: string[] = [
'layout',
'margins',
'mediaSize',
+ 'mediaType',
'scaling',
'scalingType',
'scalingTypePdf',
@@ -257,11 +264,13 @@ const MINIMUM_HEIGHT_MICRONS: number = 25400;
* @param defaultMode Duplex default mode set by policy.
*/
function getDuplexDefaultValue(
- allowedMode: DuplexModeRestriction,
+ allowedMode: DuplexModeRestriction|undefined,
defaultMode: DuplexModeRestriction): DuplexModeRestriction {
if (allowedMode !== DuplexModeRestriction.DUPLEX) {
- return allowedMode === DuplexModeRestriction.UNSET ? defaultMode :
- allowedMode;
+ return (allowedMode === undefined ||
+ allowedMode === DuplexModeRestriction.UNSET) ?
+ defaultMode :
+ allowedMode;
}
// If allowedMode === DUPLEX, then we need to use defaultMode as the
@@ -371,6 +380,26 @@ export class PrintPreviewModelElement extends PolymerElement {
key: 'mediaSize',
updatesPreview: true,
},
+ borderless: {
+ value: false,
+ unavailableValue: false,
+ valid: true,
+ available: false,
+ setByPolicy: false,
+ setFromUi: false,
+ key: 'borderless',
+ updatesPreview: true,
+ },
+ mediaType: {
+ value: '',
+ unavailableValue: '',
+ valid: true,
+ available: false,
+ setByPolicy: false,
+ setFromUi: false,
+ key: 'mediaType',
+ updatesPreview: false,
+ },
margins: {
value: MarginsType.DEFAULT,
unavailableValue: MarginsType.DEFAULT,
@@ -826,6 +855,13 @@ export class PrintPreviewModelElement extends PolymerElement {
'mediaSize.available',
!!caps && !!caps.media_size && !knownSizeToSaveAsPdf);
this.setSettingPath_(
+ 'borderless.available', this.isBorderlessAvailable_(caps));
+ this.setSettingPath_(
+ 'mediaType.available',
+ loadTimeData.getBoolean('isBorderlessPrintingEnabled') && !!caps &&
+ !!caps.media_type && !!caps.media_type.option &&
+ caps.media_type.option.length > 1);
+ this.setSettingPath_(
'dpi.available',
!this.documentSettings.isFromArc && !!caps && !!caps.dpi &&
!!caps.dpi.option && caps.dpi.option.length > 1);
@@ -959,6 +995,16 @@ export class PrintPreviewModelElement extends PolymerElement {
return hasLandscapeOption && hasAutoOrPortraitOption;
}
+ /**
+ * @return Whether the borderless setting should be available.
+ */
+ private isBorderlessAvailable_(caps: CddCapabilities|null): boolean {
+ return loadTimeData.getBoolean('isBorderlessPrintingEnabled') && !!caps &&
+ !!caps.media_size?.option?.find(o => {
+ return o.has_borderless_variant;
+ });
+ }
+
private updateSettingsValues_(caps: CddCapabilities|null) {
if (this.settings.mediaSize.available) {
const defaultOption =
@@ -978,6 +1024,35 @@ export class PrintPreviewModelElement extends PolymerElement {
this.setSetting('mediaSize', matchingOption || defaultOption, true);
}
+ if (this.settings.borderless.available) {
+ this.setSetting(
+ 'borderless',
+ this.settings.borderless.setFromUi &&
+ this.getSettingValue('borderless'),
+ true);
+ }
+
+ if (this.settings.mediaType.available) {
+ const defaultOption =
+ caps!.media_type!.option.find(o => !!o.is_default) ||
+ caps!.media_type!.option[0];
+ let matchingOption = null;
+ if (this.settings.mediaType.setFromUi) {
+ const currentMediaType = this.getSettingValue('mediaType');
+ matchingOption = caps!.media_type!.option.find(o => {
+ return o.vendor_id === currentMediaType.vendor_id;
+ });
+ }
+ this.setSetting('mediaType', matchingOption || defaultOption, true);
+ } else if (
+ caps && caps.media_type && caps.media_type.option &&
+ caps.media_type.option.length > 0) {
+ const unavailableValue =
+ caps!.media_type!.option.find(o => !!o.is_default) ||
+ caps!.media_type!.option[0];
+ this.setSettingPath_('mediaType.unavailableValue', unavailableValue);
+ }
+
if (this.settings.dpi.available) {
const defaultOption =
caps!.dpi!.option.find(o => !!o.is_default) || caps!.dpi!.option[0];
@@ -1463,6 +1538,14 @@ export class PrintPreviewModelElement extends PolymerElement {
}
}
+ if (this.settings.mediaType.available) {
+ assert(loadTimeData.getBoolean('isBorderlessPrintingEnabled'));
+ const cddDefault = this.getResetValue_(caps['media_type']!);
+ if (cddDefault) {
+ this.set('settings.mediaType.value', cddDefault);
+ }
+ }
+
if (this.settings.color.available) {
const cddDefault = this.getResetValue_(caps['color']!) as ColorOption;
if (cddDefault) {
@@ -1586,6 +1669,10 @@ export class PrintPreviewModelElement extends PolymerElement {
'scalingType';
const ticket: PrintTicket = {
mediaSize: this.getSettingValue('mediaSize') as MediaSizeValue,
+ borderless: loadTimeData.getBoolean('isBorderlessPrintingEnabled') &&
+ this.getSettingValue('mediaSize')?.has_borderless_variant &&
+ this.getSettingValue('borderless'),
+ mediaType: this.getSettingValue('mediaType')?.vendor_id,
pageCount: this.getSettingValue('pages').length,
landscape: this.getSettingValue('layout'),
color: destination.getNativeColorModel(
diff --git a/chromium/chrome/browser/resources/print_preview/data/printer_status_cros.ts b/chromium/chrome/browser/resources/print_preview/data/printer_status_cros.ts
index c15b7d6e12d..5567fbefe37 100644
--- a/chromium/chrome/browser/resources/print_preview/data/printer_status_cros.ts
+++ b/chromium/chrome/browser/resources/print_preview/data/printer_status_cros.ts
@@ -1,7 +1,8 @@
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import {assertNotReached} from 'chrome://resources/js/assert_ts.js';
+import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
/**
* These values must be kept in sync with the Reason enum in
@@ -149,6 +150,11 @@ export function computePrinterState(
export function getPrinterStatusIcon(
printerStatusReason: PrinterStatusReason|null, isEnterprisePrinter: boolean,
prefersDarkColorScheme: boolean): string {
+ if (loadTimeData.getBoolean('isPrintPreviewSetupAssistanceEnabled')) {
+ return getPrinterStatusIconImproved(
+ printerStatusReason, isEnterprisePrinter, prefersDarkColorScheme);
+ }
+
const printerTypePrefix = isEnterprisePrinter ?
'print-preview:business-printer-status-' :
'print-preview:printer-status-';
@@ -164,3 +170,70 @@ export function getPrinterStatusIcon(
assertNotReached();
}
}
+
+// Based on printer status icon colors in
+// chrome/browser/resources/print_preview/ui/icons.html.
+type StatusColor = 'green'|'grey'|'orange'|'red';
+
+// Mapping based on http://go/printer-settings-revamp-2023-dd "Determining
+// Printer Status" section.
+const PRINTER_STATUS_REASON_COLOR_MAP =
+ new Map<PrinterStatusReason, StatusColor>([
+ [PrinterStatusReason.UNKNOWN_REASON, 'green'],
+ [PrinterStatusReason.DEVICE_ERROR, 'orange'],
+ [PrinterStatusReason.DOOR_OPEN, 'orange'],
+ [PrinterStatusReason.LOW_ON_INK, 'orange'],
+ [PrinterStatusReason.LOW_ON_PAPER, 'orange'],
+ [PrinterStatusReason.NO_ERROR, 'green'],
+ [PrinterStatusReason.OUT_OF_INK, 'orange'],
+ [PrinterStatusReason.OUT_OF_PAPER, 'orange'],
+ [PrinterStatusReason.OUTPUT_ALMOST_FULL, 'orange'],
+ [PrinterStatusReason.OUTPUT_FULL, 'orange'],
+ [PrinterStatusReason.PAPER_JAM, 'orange'],
+ [PrinterStatusReason.PAUSED, 'orange'],
+ [PrinterStatusReason.PRINTER_QUEUE_FULL, 'orange'],
+ [PrinterStatusReason.PRINTER_UNREACHABLE, 'red'],
+ [PrinterStatusReason.STOPPED, 'orange'],
+ [PrinterStatusReason.TRAY_MISSING, 'orange'],
+ ]);
+
+/**
+ * Returns the print-preview icon matching the printer's PrinterStatusReason,
+ * enterprise status, and color scheme when
+ * 'isPrintPreviewSetupAssistanceEnabled' flag is enabled.
+ */
+// TODO(b/289091283): Rename function to `getPrinterStatusIcon` and remove
+// previous implementation when flag is removed.
+function getPrinterStatusIconImproved(
+ printerStatusReason: PrinterStatusReason|null, isEnterprisePrinter: boolean,
+ prefersDarkColorScheme: boolean): string {
+ const printerTypePrefix = isEnterprisePrinter ?
+ 'print-preview:business-printer-status-' :
+ 'print-preview:printer-status-';
+ const darkModeSuffix = prefersDarkColorScheme ? '-dark' : '';
+ const iconColor = printerStatusReason === null ?
+ 'grey' :
+ PRINTER_STATUS_REASON_COLOR_MAP.get(printerStatusReason);
+ assert(iconColor);
+ return `${printerTypePrefix}${iconColor}${darkModeSuffix}`;
+}
+
+/**
+ * Returns class name matching icon color for the printer's
+ * PrinterStatusReason.
+ */
+export function getStatusTextColorClass(
+ printerStatusReason: PrinterStatusReason|null): string {
+ // TODO(b/289091283): Remove condition when flag is removed.
+ if (!loadTimeData.getBoolean('isPrintPreviewSetupAssistanceEnabled')) {
+ return '';
+ }
+
+ if (printerStatusReason === null) {
+ return '';
+ }
+
+ const color = PRINTER_STATUS_REASON_COLOR_MAP.get(printerStatusReason);
+ assert(color);
+ return `status-${color}`;
+}
diff --git a/chromium/chrome/browser/resources/print_preview/metrics.ts b/chromium/chrome/browser/resources/print_preview/metrics.ts
index a031547e319..aa4053053ac 100644
--- a/chromium/chrome/browser/resources/print_preview/metrics.ts
+++ b/chromium/chrome/browser/resources/print_preview/metrics.ts
@@ -44,6 +44,25 @@ export enum PrintSettingsUiBucket {
PRINT_SETTINGS_UI_MAX_BUCKET = 6
}
+// <if expr="is_chromeos">
+/**
+ * Launch printer settings usage metric buckets.
+ */
+export enum PrintPreviewLaunchSourceBucket {
+ // "Manage printers" button in preview-area when it fails to fetch
+ // capabilities.
+ PREVIEW_AREA_CONNECTION_ERROR = 0,
+ // "Manage printers" button in destination-dialog-cros when there are no
+ // destinations.
+ DESTINATION_DIALOG_CROS_NO_PRINTERS = 1,
+ // "Manage printers" button in destination-dialog-cros when there are
+ // destinations.
+ DESTINATION_DIALOG_CROS_HAS_PRINTERS = 2,
+ // Max value.
+ PRINT_PREVIEW_LAUNCH_SOURCE_MAX_BUCKET = 3,
+}
+// </if>
+
/* A context for recording a value in a specific UMA histogram. */
export class MetricsContext {
private histogram_: string;
@@ -78,4 +97,16 @@ export class MetricsContext {
'PrintPreview.PrintSettingsUi',
PrintSettingsUiBucket.PRINT_SETTINGS_UI_MAX_BUCKET);
}
+
+ // <if expr="is_chromeos">
+ /**
+ * Get `MetricsContext` for `PrintPreview.PrinterSettingsLaunchSource`
+ * histogram.
+ */
+ static getLaunchPrinterSettingsMetricsContextCros(): MetricsContext {
+ return new MetricsContext(
+ 'PrintPreview.PrinterSettingsLaunchSource',
+ PrintPreviewLaunchSourceBucket.PRINT_PREVIEW_LAUNCH_SOURCE_MAX_BUCKET);
+ }
+ // </if>
}
diff --git a/chromium/chrome/browser/resources/print_preview/native_layer.ts b/chromium/chrome/browser/resources/print_preview/native_layer.ts
index d040bddeb3d..3f4172fa862 100644
--- a/chromium/chrome/browser/resources/print_preview/native_layer.ts
+++ b/chromium/chrome/browser/resources/print_preview/native_layer.ts
@@ -162,7 +162,7 @@ export interface NativeLayer {
* @return Promise that will resolve when the print request is
* finished or rejected.
*/
- print(printTicket: string): Promise<string|undefined>;
+ doPrint(printTicket: string): Promise<string|undefined>;
/** Requests that the current pending print request be cancelled. */
cancelPendingPrintRequest(): void;
@@ -227,8 +227,8 @@ export class NativeLayerImpl implements NativeLayer {
chrome.send('managePrinters');
}
- print(printTicket: string) {
- return sendWithPromise('print', printTicket);
+ doPrint(printTicket: string) {
+ return sendWithPromise('doPrint', printTicket);
}
cancelPendingPrintRequest() {
diff --git a/chromium/chrome/browser/resources/print_preview/native_layer_cros.ts b/chromium/chrome/browser/resources/print_preview/native_layer_cros.ts
index 21f35ba7d3c..2664b132d4a 100644
--- a/chromium/chrome/browser/resources/print_preview/native_layer_cros.ts
+++ b/chromium/chrome/browser/resources/print_preview/native_layer_cros.ts
@@ -77,6 +77,12 @@ export interface NativeLayerCros {
* the result from opening Print Preview.
*/
recordPrintAttemptOutcome(printAttemptOutcome: PrintAttemptOutcome): void;
+
+ /**
+ * Returns whether or not the manage printers button should be displayed for
+ * the given print preview initiator.
+ */
+ getShowManagePrinters(): Promise<boolean>;
}
export class NativeLayerCrosImpl implements NativeLayerCros {
@@ -115,6 +121,10 @@ export class NativeLayerCrosImpl implements NativeLayerCros {
chrome.send('recordPrintAttemptOutcome', [printAttemptOutcome]);
}
+ getShowManagePrinters() {
+ return sendWithPromise('getShowManagePrinters');
+ }
+
static getInstance(): NativeLayerCros {
return instance || (instance = new NativeLayerCrosImpl());
}
diff --git a/chromium/chrome/browser/resources/print_preview/print_preview.html b/chromium/chrome/browser/resources/print_preview/print_preview.html
index 9cab2158f13..43e414a3d04 100644
--- a/chromium/chrome/browser/resources/print_preview/print_preview.html
+++ b/chromium/chrome/browser/resources/print_preview/print_preview.html
@@ -56,6 +56,9 @@
<print-preview-app></print-preview-app>
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
<link rel="stylesheet" href="chrome://resources/css/md_colors.css">
+<if expr="is_chromeos">
+ <link rel="stylesheet" href="chrome://theme/typography.css">
+</if>
<script type="module" src="print_preview.js"></script>
</body>
</html>
diff --git a/chromium/chrome/browser/resources/print_preview/print_preview.ts b/chromium/chrome/browser/resources/print_preview/print_preview.ts
index d8fca806ed8..c2ad6b208b4 100644
--- a/chromium/chrome/browser/resources/print_preview/print_preview.ts
+++ b/chromium/chrome/browser/resources/print_preview/print_preview.ts
@@ -7,7 +7,7 @@ export {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_c
export {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
export {PluralStringProxyImpl as PrintPreviewPluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js';
export {IronMeta} from 'chrome://resources/polymer/v3_0/iron-meta/iron-meta.js';
-export {Cdd, ColorOption, DpiOption, DuplexOption, MediaSizeCapability, MediaSizeOption, PageOrientationOption, SelectOption, VendorCapabilityValueType} from './data/cdd.js';
+export {Cdd, ColorOption, DpiOption, DuplexOption, MediaSizeCapability, MediaSizeOption, MediaTypeCapability, MediaTypeOption, PageOrientationOption, SelectOption, VendorCapabilityValueType} from './data/cdd.js';
export {ColorMode, createDestinationKey, Destination, DestinationOrigin, GooglePromotedDestinationId, makeRecentDestination, PDF_DESTINATION_KEY, PrinterType, RecentDestination} from './data/destination.js';
// <if expr="is_chromeos">
export {SAVE_TO_DRIVE_CROS_DESTINATION_KEY} from './data/destination.js';
@@ -71,6 +71,7 @@ export {PrintPreviewMarginControlElement} from './ui/margin_control.js';
export {PrintPreviewMarginControlContainerElement} from './ui/margin_control_container.js';
export {PrintPreviewMarginsSettingsElement} from './ui/margins_settings.js';
export {PrintPreviewMediaSizeSettingsElement} from './ui/media_size_settings.js';
+export {PrintPreviewMediaTypeSettingsElement} from './ui/media_type_settings.js';
export {PrintPreviewNumberSettingsSectionElement} from './ui/number_settings_section.js';
export {PrintPreviewOtherOptionsSettingsElement} from './ui/other_options_settings.js';
export {PrintPreviewPagesPerSheetSettingsElement} from './ui/pages_per_sheet_settings.js';
@@ -81,6 +82,9 @@ export {PrintPreviewPinSettingsElement} from './ui/pin_settings.js';
export {PluginProxy, PluginProxyImpl, ViewportChangedCallback} from './ui/plugin_proxy.js';
export {PreviewAreaState, PreviewTicket, PrintPreviewPreviewAreaElement} from './ui/preview_area.js';
export {PrintPreviewSearchBoxElement} from './ui/print_preview_search_box.js';
+// <if expr="is_chromeos">
+export {PrinterSetupInfoMessageType, PrinterSetupInfoMetricsSource, PrintPreviewPrinterSetupInfoCrosElement} from './ui/printer_setup_info_cros.js';
+// </if>
export {PrintPreviewScalingSettingsElement} from './ui/scaling_settings.js';
export {SelectMixin, SelectMixinInterface} from './ui/select_mixin.js';
export {SettingsMixinInterface} from './ui/settings_mixin.js';
diff --git a/chromium/chrome/browser/resources/print_preview/ui/app.ts b/chromium/chrome/browser/resources/print_preview/ui/app.ts
index 164493ec294..252e6910f51 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/app.ts
+++ b/chromium/chrome/browser/resources/print_preview/ui/app.ts
@@ -420,7 +420,7 @@ export class PrintPreviewAppElement extends PrintPreviewAppElementBase {
// </if>
const whenPrintDone =
- this.nativeLayer_!.print(this.$.model.createPrintTicket(
+ this.nativeLayer_!.doPrint(this.$.model.createPrintTicket(
this.destination_, this.openPdfInPreview_,
this.showSystemDialogBeforePrint_));
const onError = this.destination_.type === PrinterType.PDF_PRINTER ?
diff --git a/chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.html b/chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.html
index 63483b0ccb6..43ac1c9bbf2 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.html
+++ b/chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.html
@@ -1,4 +1,8 @@
<style include="destination-dialog-style">
+ :host(:not([show-manage-printers-button])) div[slot=button-container] {
+ justify-content: flex-end;
+ }
+
.form-row {
align-items: center;
column-gap: 18px;
@@ -33,21 +37,29 @@
</div>
</div>
<print-preview-search-box id="searchBox"
+ hidden$="[[isShowingPrinterSetupAssistance]]"
label="$i18n{searchBoxPlaceholder}" search-query="{{searchQuery_}}"
autofocus>
</print-preview-search-box>
<print-preview-destination-list id="printList"
destinations="[[destinations_]]"
+ hidden$="[[isShowingPrinterSetupAssistance]]"
loading-destinations="[[loadingAnyDestinations_]]"
search-query="[[searchQuery_]]"
on-destination-selected="onDestinationSelected_">
</print-preview-destination-list>
+ <print-preview-printer-setup-info-cros
+ hidden$="[[!isShowingPrinterSetupAssistance]]"
+ message-type="[[noPrinters_]]"
+ metrics-source="[[destinationDialogCrosSource_]]">
+ </print-preview-printer-setup-info-cros>
<print-preview-provisional-destination-resolver id="provisionalResolver"
destination-store="[[destinationStore]]">
</print-preview-provisional-destination-resolver>
</div>
<div slot="button-container">
- <cr-button on-click="onManageButtonClick_">
+ <cr-button on-click="onManageButtonClick_" id="managePrinters"
+ hidden$="[[!showManagePrintersButton]]">
$i18n{manage}
<iron-icon icon="cr:open-in-new" id="manageIcon"></iron-icon>
</cr-button>
diff --git a/chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.ts b/chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.ts
index a20c786fe32..1d6b9127a08 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.ts
+++ b/chromium/chrome/browser/resources/print_preview/ui/destination_dialog_cros.ts
@@ -15,26 +15,31 @@ import './destination_list.js';
import './print_preview_search_box.js';
import './print_preview_shared.css.js';
import './print_preview_vars.css.js';
+import './printer_setup_info_cros.js';
import './provisional_destination_resolver.js';
import '../strings.m.js';
import './throbber.css.js';
import './destination_list_item_cros.js';
import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {assert} from 'chrome://resources/js/assert_ts.js';
-import {EventTracker} from 'chrome://resources/js/event_tracker.js';
import {ListPropertyUpdateMixin} from 'chrome://resources/cr_elements/list_property_update_mixin.js';
import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {EventTracker} from 'chrome://resources/js/event_tracker.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {Destination, GooglePromotedDestinationId} from '../data/destination.js';
import {DestinationStore, DestinationStoreEventType} from '../data/destination_store.js';
import {PrintServerStore, PrintServerStoreEventType} from '../data/print_server_store.js';
+import {MetricsContext, PrintPreviewLaunchSourceBucket} from '../metrics.js';
import {NativeLayerImpl} from '../native_layer.js';
+import {NativeLayerCrosImpl} from '../native_layer_cros.js';
import {getTemplate} from './destination_dialog_cros.html.js';
import {PrintPreviewDestinationListItemElement} from './destination_list_item_cros.js';
import {PrintPreviewSearchBoxElement} from './print_preview_search_box.js';
+import {PrinterSetupInfoMessageType, PrinterSetupInfoMetricsSource} from './printer_setup_info_cros.js';
import {PrintPreviewProvisionalDestinationResolverElement} from './provisional_destination_resolver.js';
interface PrintServersChangedEventDetail {
@@ -113,6 +118,44 @@ export class PrintPreviewDestinationDialogCrosElement extends
computed: 'computeLoadingDestinations_(' +
'loadingDestinations_, loadingServerPrinters_)',
},
+
+ isPrintPreviewSetupAssistanceEnabled_: {
+ type: Boolean,
+ value: () => {
+ return loadTimeData.getBoolean(
+ 'isPrintPreviewSetupAssistanceEnabled');
+ },
+ readOnly: true,
+ },
+
+ isShowingPrinterSetupAssistance: {
+ type: Boolean,
+ computed:
+ 'computeIsShowingPrinterSetupAssistance(destinations_.length, ' +
+ 'isPrintPreviewSetupAssistanceEnabled_)',
+ reflectToAttribute: true,
+ },
+
+ showManagePrintersButton: {
+ type: Boolean,
+ computed: 'computeShowManagePrintersButton(' +
+ 'showManagePrinters, isShowingPrinterSetupAssistance)',
+ reflectToAttribute: true,
+ },
+
+ showManagePrinters: Boolean,
+
+ noPrinters_: {
+ type: Number,
+ value: PrinterSetupInfoMessageType.NO_PRINTERS,
+ readOnly: true,
+ },
+
+ destinationDialogCrosSource_: {
+ type: Number,
+ value: PrinterSetupInfoMetricsSource.DESTINATION_DIALOG_CROS,
+ readOnly: true,
+ },
};
}
@@ -125,11 +168,16 @@ export class PrintPreviewDestinationDialogCrosElement extends
private printServerNames_: string[];
private loadingServerPrinters_: boolean;
private loadingAnyDestinations_: boolean;
+ private isPrintPreviewSetupAssistanceEnabled_: boolean;
+ private metricsContext_: MetricsContext;
+ private isShowingPrinterSetupAssistance: boolean;
+ private showManagePrintersButton: boolean;
private tracker_: EventTracker = new EventTracker();
private destinationInConfiguring_: Destination|null = null;
private initialized_: boolean = false;
private printServerStore_: PrintServerStore|null = null;
+ private showManagePrinters: boolean = false;
override disconnectedCallback() {
super.disconnectedCallback();
@@ -159,6 +207,12 @@ export class PrintPreviewDestinationDialogCrosElement extends
if (this.destinationStore) {
this.printServerStore_.setDestinationStore(this.destinationStore);
}
+ this.metricsContext_ =
+ MetricsContext.getLaunchPrinterSettingsMetricsContextCros();
+ NativeLayerCrosImpl.getInstance().getShowManagePrinters().then(
+ (show: boolean) => {
+ this.showManagePrinters = show;
+ });
}
private onKeydown_(e: KeyboardEvent) {
@@ -325,6 +379,31 @@ export class PrintPreviewDestinationDialogCrosElement extends
private onManageButtonClick_() {
NativeLayerImpl.getInstance().managePrinters();
+ if (this.isPrintPreviewSetupAssistanceEnabled_) {
+ this.metricsContext_.record(
+ PrintPreviewLaunchSourceBucket.DESTINATION_DIALOG_CROS_HAS_PRINTERS);
+ }
+ }
+
+ /**
+ * Sets `isShowingPrinterSetupAssistance` state based on flag and
+ * destinations.
+ * If flag is turned off, then do not show.
+ * If flag is turned on and there is only a PDF printer, then show.
+ * Save to drive is already excluded by `getDestinationList_()`.
+ */
+ private computeIsShowingPrinterSetupAssistance(): boolean {
+ if (!this.isPrintPreviewSetupAssistanceEnabled_) {
+ return false;
+ }
+
+ return !this.destinations_.some(
+ (destination: Destination): boolean =>
+ destination.id !== GooglePromotedDestinationId.SAVE_AS_PDF);
+ }
+
+ private computeShowManagePrintersButton(): boolean {
+ return this.showManagePrinters && !this.isShowingPrinterSetupAssistance;
}
}
diff --git a/chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.html b/chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.html
index d3d10ee66ea..3f374e0e42c 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.html
+++ b/chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.html
@@ -4,8 +4,13 @@
font-style: italic;
}
- :host([is-destination-cros-local_]) .connection-status {
- color: var(--google-red-600);
+ :host([is-destination-cros-local_]) .connection-status,
+ :host([is-destination-cros-local_]) .connection-status.status-red {
+ color: var(--error-status-alert);
+ }
+
+ :host([is-destination-cros-local_]) .connection-status.status-orange {
+ color: var(--error-status-warning);
}
</style>
<iron-media-query query="(prefers-color-scheme: dark)"
@@ -16,7 +21,8 @@
<span class="search-hint searchable" hidden="[[!searchHint_]]">
[[searchHint_]]
</span>
-<span class="connection-status" hidden="[[!statusText_]]">
+<span class$="[[computeStatusClass_(destination.printerStatusReason)]]"
+ hidden="[[!statusText_]]">
[[statusText_]]
</span>
<span class="extension-controlled-indicator"
diff --git a/chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.ts b/chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.ts
index 27c7ef5203f..ca6c8afe155 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.ts
+++ b/chromium/chrome/browser/resources/print_preview/ui/destination_list_item_cros.ts
@@ -10,14 +10,14 @@ import './destination_list_item_style.css.js';
import './icons.html.js';
import '../strings.m.js';
-import {assert} from 'chrome://resources/js/assert_ts.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {removeHighlights} from 'chrome://resources/js/search_highlight_utils.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {Destination, DestinationOrigin} from '../data/destination.js';
-import {ERROR_STRING_KEY_MAP, getPrinterStatusIcon, PrinterStatusReason} from '../data/printer_status_cros.js';
+import {ERROR_STRING_KEY_MAP, getPrinterStatusIcon, getStatusTextColorClass, PrinterStatusReason} from '../data/printer_status_cros.js';
import {getTemplate} from './destination_list_item_cros.html.js';
import {updateHighlights} from './highlight_utils.js';
@@ -208,6 +208,16 @@ export class PrintPreviewDestinationListItemElement extends
return this.destination.icon;
}
+ private computeStatusClass_(): string {
+ const statusClass = 'connection-status';
+ if (!this.destination || this.destination.printerStatusReason === null) {
+ return statusClass;
+ }
+
+ return `${statusClass} ${
+ getStatusTextColorClass(this.destination.printerStatusReason)}`;
+ }
+
/**
* True when the destination is a CrOS local printer.
*/
diff --git a/chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.html b/chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.html
index 44cb7a31b12..6136599b169 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.html
+++ b/chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.html
@@ -8,6 +8,14 @@
white-space: nowrap;
}
+ :host([is-current-destination-cros-local_]) #statusText.status-orange {
+ color: var(--error-status-warning);
+ }
+
+ :host([is-current-destination-cros-local_]) #statusText.status-red {
+ color: var(--error-status-alert);
+ }
+
@media (prefers-color-scheme: dark) {
:host([is-current-destination-cros-local_]) #statusText {
color: var(--google-red-300);
@@ -51,7 +59,9 @@
hidden$="[[hideDestinationAdditionalInfo_(statusText_)]]">
<div slot="title"></div>
<div slot="controls">
- <div id="statusText" class="destination-status" title="[[statusText_]]"
+ <div id="statusText"
+ class$="[[computeStatusClass_(destination.printerStatusReason)]]"
+ title="[[statusText_]]"
aria-hidden="[[isCurrentDestinationCrosLocal_]]"
inner-h-t-m-l="[[statusText_]]">
</div>
diff --git a/chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.ts b/chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.ts
index bd7e21396a2..ee89e22229d 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.ts
+++ b/chromium/chrome/browser/resources/print_preview/ui/destination_select_cros.ts
@@ -19,7 +19,7 @@ import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {Destination, DestinationOrigin, GooglePromotedDestinationId, PDF_DESTINATION_KEY} from '../data/destination.js';
-import {ERROR_STRING_KEY_MAP, getPrinterStatusIcon, PrinterStatusReason} from '../data/printer_status_cros.js';
+import {ERROR_STRING_KEY_MAP, getPrinterStatusIcon, getStatusTextColorClass, PrinterStatusReason} from '../data/printer_status_cros.js';
import {PrintPreviewDestinationDropdownCrosElement} from './destination_dropdown_cros.js';
import {getTemplate} from './destination_select_cros.html.js';
@@ -267,6 +267,22 @@ export class PrintPreviewDestinationSelectCrosElement extends
this.destination.origin === DestinationOrigin.CROS;
}
+ private computeStatusClass_(): string {
+ const statusClass = 'destination-status';
+ if (!this.destination) {
+ return statusClass;
+ }
+
+ const printerStatusReason = this.destination.printerStatusReason;
+ if (printerStatusReason === null ||
+ printerStatusReason === PrinterStatusReason.NO_ERROR ||
+ printerStatusReason === PrinterStatusReason.UNKNOWN_REASON) {
+ return statusClass;
+ }
+
+ return `${statusClass} ${getStatusTextColorClass(printerStatusReason)}`;
+ }
+
/**
* Return the options currently visible to the user for testing purposes.
*/
diff --git a/chromium/chrome/browser/resources/print_preview/ui/destination_settings.ts b/chromium/chrome/browser/resources/print_preview/ui/destination_settings.ts
index 11cacaea976..83a85707aa7 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/destination_settings.ts
+++ b/chromium/chrome/browser/resources/print_preview/ui/destination_settings.ts
@@ -234,27 +234,27 @@ export class PrintPreviewDestinationSettingsElement extends
/**
* @param defaultPrinter The system default printer ID.
* @param pdfPrinterDisabled Whether the PDF printer is disabled.
- * @param isDriveMounted Whether Google Drive is mounted. Only used
- on Chrome OS.
+ * @param saveToDriveDisabled Whether the 'Save to Google Drive' destination
+ * is disabled in print preview. Only used on Chrome OS.
* @param serializedDefaultDestinationRulesStr String with rules
* for selecting a default destination.
*/
init(
defaultPrinter: string, pdfPrinterDisabled: boolean,
- isDriveMounted: boolean,
+ saveToDriveDisabled: boolean,
serializedDefaultDestinationRulesStr: string|null) {
this.pdfPrinterDisabled_ = pdfPrinterDisabled;
let recentDestinations =
this.getSettingValue('recentDestinations') as RecentDestination[];
// <if expr="is_chromeos">
this.driveDestinationKey_ =
- isDriveMounted ? SAVE_TO_DRIVE_CROS_DESTINATION_KEY : '';
+ saveToDriveDisabled ? '' : SAVE_TO_DRIVE_CROS_DESTINATION_KEY;
// </if>
recentDestinations = recentDestinations.slice(
0, this.getRecentDestinationsDisplayCount_(recentDestinations));
this.destinationStore_!.init(
- this.pdfPrinterDisabled_, isDriveMounted, defaultPrinter,
+ this.pdfPrinterDisabled_, saveToDriveDisabled, defaultPrinter,
serializedDefaultDestinationRulesStr, recentDestinations);
}
diff --git a/chromium/chrome/browser/resources/print_preview/ui/icons.html b/chromium/chrome/browser/resources/print_preview/ui/icons.html
index 4fe2cce0caa..d53cc926c84 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/icons.html
+++ b/chromium/chrome/browser/resources/print_preview/ui/icons.html
@@ -27,6 +27,10 @@
<path d="M19,8 C20.66,8 22,9.34 22,11 L22,11 L22.0008411,12.1834702 C20.9260374,10.5660653 19.0875152,9.5 17,9.5 C14.2041481,9.5 11.8549346,11.412286 11.1889599,14.0002575 L8,14 L8,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L6,21 L6,17 L2,17 L2,11 C2,9.34 3.34,8 5,8 L5,8 Z M18,3 L18,7 L6,7 L6,3 L18,3 Z"></path>
<circle fill="#dadce0" cx="17" cy="15.5" r="3.5"></circle>
</g>
+ <g id="printer-status-orange">
+ <path d="M19,8 C20.66,8 22,9.34 22,11 L22,11 L22.0008411,12.1834702 C20.9260374,10.5660653 19.0875152,9.5 17,9.5 C14.2041481,9.5 11.8549346,11.412286 11.1889599,14.0002575 L8,14 L8,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L6,21 L6,17 L2,17 L2,11 C2,9.34 3.34,8 5,8 L5,8 Z M18,3 L18,7 L6,7 L6,3 L18,3 Z"></path>
+ <circle fill="#e37400" cx="17" cy="15.5" r="3.5"></circle>
+ </g>
<g id="printer-status-red">
<path d="M19,8 C20.66,8 22,9.34 22,11 L22,11 L22.0008411,12.1834702 C20.9260374,10.5660653 19.0875152,9.5 17,9.5 C14.2041481,9.5 11.8549346,11.412286 11.1889599,14.0002575 L8,14 L8,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L6,21 L6,17 L2,17 L2,11 C2,9.34 3.34,8 5,8 L5,8 Z M18,3 L18,7 L6,7 L6,3 L18,3 Z"></path>
<circle fill="#d93025" cx="17" cy="15.5" r="3.5"></circle>
@@ -39,6 +43,10 @@
<path d="M12,3 L12,7 L22,7 L22.0008411,12.1834702 C21.4889261,11.4131214 20.8037622,10.7678435 20.000963,10.3032504 L20,9 L12,9 L12,11 L13.0312427,11.0000183 C11.7856236,12.0994345 11,13.7079712 11,15.5 C11,16.7266262 11.3680857,17.8672813 11.9998276,18.8175358 L12,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L2,21 L2,3 L12,3 Z M6,17 L4,17 L4,19 L6,19 L6,17 Z M10,17 L8,17 L8,19 L10,19 L10,17 Z M6,13 L4,13 L4,15 L6,15 L6,13 Z M10,13 L8,13 L8,15 L10,15 L10,13 Z M6,9 L4,9 L4,11 L6,11 L6,9 Z M10,9 L8,9 L8,11 L10,11 L10,9 Z M6,5 L4,5 L4,7 L6,7 L6,5 Z M10,5 L8,5 L8,7 L10,7 L10,5 Z"></path>
<circle fill="#dadce0" cx="17" cy="15.5" r="3.5"></circle>
</g>
+ <g id="business-printer-status-orange">
+ <path d="M12,3 L12,7 L22,7 L22.0008411,12.1834702 C21.4889261,11.4131214 20.8037622,10.7678435 20.000963,10.3032504 L20,9 L12,9 L12,11 L13.0312427,11.0000183 C11.7856236,12.0994345 11,13.7079712 11,15.5 C11,16.7266262 11.3680857,17.8672813 11.9998276,18.8175358 L12,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L2,21 L2,3 L12,3 Z M6,17 L4,17 L4,19 L6,19 L6,17 Z M10,17 L8,17 L8,19 L10,19 L10,17 Z M6,13 L4,13 L4,15 L6,15 L6,13 Z M10,13 L8,13 L8,15 L10,15 L10,13 Z M6,9 L4,9 L4,11 L6,11 L6,9 Z M10,9 L8,9 L8,11 L10,11 L10,9 Z M6,5 L4,5 L4,7 L6,7 L6,5 Z M10,5 L8,5 L8,7 L10,7 L10,5 Z"></path>
+ <circle fill="#e37400" cx="17" cy="15.5" r="3.5"></circle>
+ </g>
<g id="business-printer-status-red">
<path d="M12,3 L12,7 L22,7 L22.0008411,12.1834702 C21.4889261,11.4131214 20.8037622,10.7678435 20.000963,10.3032504 L20,9 L12,9 L12,11 L13.0312427,11.0000183 C11.7856236,12.0994345 11,13.7079712 11,15.5 C11,16.7266262 11.3680857,17.8672813 11.9998276,18.8175358 L12,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L2,21 L2,3 L12,3 Z M6,17 L4,17 L4,19 L6,19 L6,17 Z M10,17 L8,17 L8,19 L10,19 L10,17 Z M6,13 L4,13 L4,15 L6,15 L6,13 Z M10,13 L8,13 L8,15 L10,15 L10,13 Z M6,9 L4,9 L4,11 L6,11 L6,9 Z M10,9 L8,9 L8,11 L10,11 L10,9 Z M6,5 L4,5 L4,7 L6,7 L6,5 Z M10,5 L8,5 L8,7 L10,7 L10,5 Z"></path>
<circle fill="#d93025" cx="17" cy="15.5" r="3.5"></circle>
@@ -53,6 +61,10 @@
<path d="M19,8 C20.66,8 22,9.34 22,11 L22,11 L22.0008411,12.1834702 C20.9260374,10.5660653 19.0875152,9.5 17,9.5 C14.2041481,9.5 11.8549346,11.412286 11.1889599,14.0002575 L8,14 L8,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L6,21 L6,17 L2,17 L2,11 C2,9.34 3.34,8 5,8 L5,8 Z M18,3 L18,7 L6,7 L6,3 L18,3 Z"></path>
<circle fill="#80868b" cx="17" cy="15.5" r="3.5"></circle>
</g>
+ <g id="printer-status-orange-dark">
+ <path d="M19,8 C20.66,8 22,9.34 22,11 L22,11 L22.0008411,12.1834702 C20.9260374,10.5660653 19.0875152,9.5 17,9.5 C14.2041481,9.5 11.8549346,11.412286 11.1889599,14.0002575 L8,14 L8,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L6,21 L6,17 L2,17 L2,11 C2,9.34 3.34,8 5,8 L5,8 Z M18,3 L18,7 L6,7 L6,3 L18,3 Z"></path>
+ <circle fill="#fdd663" cx="17" cy="15.5" r="3.5"></circle>
+ </g>
<g id="printer-status-red-dark">
<path d="M19,8 C20.66,8 22,9.34 22,11 L22,11 L22.0008411,12.1834702 C20.9260374,10.5660653 19.0875152,9.5 17,9.5 C14.2041481,9.5 11.8549346,11.412286 11.1889599,14.0002575 L8,14 L8,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L6,21 L6,17 L2,17 L2,11 C2,9.34 3.34,8 5,8 L5,8 Z M18,3 L18,7 L6,7 L6,3 L18,3 Z"></path>
<circle fill="#f28b82" cx="17" cy="15.5" r="3.5"></circle>
@@ -65,10 +77,23 @@
<path d="M12,3 L12,7 L22,7 L22.0008411,12.1834702 C21.4889261,11.4131214 20.8037622,10.7678435 20.000963,10.3032504 L20,9 L12,9 L12,11 L13.0312427,11.0000183 C11.7856236,12.0994345 11,13.7079712 11,15.5 C11,16.7266262 11.3680857,17.8672813 11.9998276,18.8175358 L12,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L2,21 L2,3 L12,3 Z M6,17 L4,17 L4,19 L6,19 L6,17 Z M10,17 L8,17 L8,19 L10,19 L10,17 Z M6,13 L4,13 L4,15 L6,15 L6,13 Z M10,13 L8,13 L8,15 L10,15 L10,13 Z M6,9 L4,9 L4,11 L6,11 L6,9 Z M10,9 L8,9 L8,11 L10,11 L10,9 Z M6,5 L4,5 L4,7 L6,7 L6,5 Z M10,5 L8,5 L8,7 L10,7 L10,5 Z"></path>
<circle fill="#80868b" cx="17" cy="15.5" r="3.5"></circle>
</g>
+ <g id="business-printer-status-orange-dark">
+ <path d="M12,3 L12,7 L22,7 L22.0008411,12.1834702 C21.4889261,11.4131214 20.8037622,10.7678435 20.000963,10.3032504 L20,9 L12,9 L12,11 L13.0312427,11.0000183 C11.7856236,12.0994345 11,13.7079712 11,15.5 C11,16.7266262 11.3680857,17.8672813 11.9998276,18.8175358 L12,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L2,21 L2,3 L12,3 Z M6,17 L4,17 L4,19 L6,19 L6,17 Z M10,17 L8,17 L8,19 L10,19 L10,17 Z M6,13 L4,13 L4,15 L6,15 L6,13 Z M10,13 L8,13 L8,15 L10,15 L10,13 Z M6,9 L4,9 L4,11 L6,11 L6,9 Z M10,9 L8,9 L8,11 L10,11 L10,9 Z M6,5 L4,5 L4,7 L6,7 L6,5 Z M10,5 L8,5 L8,7 L10,7 L10,5 Z"></path>
+ <circle fill="#fdd663" cx="17" cy="15.5" r="3.5"></circle>
+ </g>
<g id="business-printer-status-red-dark">
<path d="M12,3 L12,7 L22,7 L22.0008411,12.1834702 C21.4889261,11.4131214 20.8037622,10.7678435 20.000963,10.3032504 L20,9 L12,9 L12,11 L13.0312427,11.0000183 C11.7856236,12.0994345 11,13.7079712 11,15.5 C11,16.7266262 11.3680857,17.8672813 11.9998276,18.8175358 L12,19 L12.1267078,19.0009178 C12.7530956,19.8713157 13.6069102,20.5670952 14.6011413,21.0012461 L2,21 L2,3 L12,3 Z M6,17 L4,17 L4,19 L6,19 L6,17 Z M10,17 L8,17 L8,19 L10,19 L10,17 Z M6,13 L4,13 L4,15 L6,15 L6,13 Z M10,13 L8,13 L8,15 L10,15 L10,13 Z M6,9 L4,9 L4,11 L6,11 L6,9 Z M10,9 L8,9 L8,11 L10,11 L10,9 Z M6,5 L4,5 L4,7 L6,7 L6,5 Z M10,5 L8,5 L8,7 L10,7 L10,5 Z"></path>
<circle fill="#f28b82" cx="17" cy="15.5" r="3.5"></circle>
</g>
+ <svg id="no-printer-available" xmlns="http://www.w3.org/2000/svg" width="322" height="237" viewBox="0 0 322 237" fill="none">
+ <path d="M236.777 90.584L209.208 68.9842C205.778 66.3608 203.517 62.4502 202.924 58.1118C202.331 53.7735 203.453 49.3625 206.044 45.8485V45.8485C207.327 44.1076 208.932 42.6395 210.768 41.528C212.604 40.4166 214.635 39.6835 216.744 39.3707C218.853 39.0579 221 39.1715 223.062 39.705C225.123 40.2386 227.059 41.1816 228.758 42.4802L256.319 64.0787C259.751 66.7009 262.013 70.6112 262.608 74.9499C263.203 79.2886 262.082 83.7005 259.491 87.2157V87.2157C256.899 90.7296 253.05 93.0584 248.791 93.69C244.531 94.3216 240.21 93.2044 236.777 90.584V90.584Z" fill="var(--cros-sys-illo-color3)" />
+ <path d="M85.1662 84.2372L72.8191 98.9518C71.2617 100.808 71.5038 103.575 73.3599 105.132L87.9005 117.333C89.7565 118.891 92.5237 118.649 94.0811 116.793L106.428 102.078C107.986 100.222 107.743 97.4549 105.887 95.8975L91.3468 83.6965C89.4908 82.1391 86.7236 82.3811 85.1662 84.2372Z" fill="var(--cros-sys-illo-color5)" />
+ <path d="M78.7032 188.115C84.1071 186.925 89.4438 185.445 94.689 183.681C101.986 181.501 109.83 182.242 116.498 185.74C120.141 187.833 123.896 189.732 127.749 191.43C141.472 196.798 157.382 189.517 160.959 174.989C164.101 162.228 155.512 148.399 142.122 146.423C135.936 145.481 129.647 146.652 123.428 146.255C116.808 145.902 110.548 143.297 105.721 138.888C101.595 135.075 97.8122 130.889 93.1593 127.592C88.65 124.363 83.3803 122.294 77.8232 121.57C69.0434 120.448 60.1179 122.691 52.8967 127.833C45.6755 132.975 40.7124 140.622 39.0361 149.189C37.3598 157.757 39.0988 166.587 43.8928 173.85C48.6867 181.114 56.1678 186.253 64.7856 188.203C69.3679 189.128 74.109 189.098 78.7032 188.115V188.115Z" fill="var(--cros-sys-illo-color1-2)" />
+ <path d="M257.605 175.866C257.354 176.426 256.987 176.925 256.527 177.331C256.068 177.737 255.527 178.041 254.941 178.222C254.354 178.403 253.734 178.457 253.122 178.381C252.511 178.305 251.921 178.101 251.391 177.781C242.089 172.217 235.131 163.435 231.848 153.117C228.564 142.798 229.186 131.667 233.594 121.854C238.002 112.04 245.887 104.233 255.74 99.9269C265.593 95.6204 276.722 95.1167 286.998 98.5122C287.585 98.7057 288.126 99.0202 288.585 99.4355C289.045 99.8507 289.413 100.357 289.665 100.922C289.918 101.487 290.049 102.098 290.051 102.714C290.052 103.331 289.924 103.94 289.675 104.501L257.605 175.866Z" stroke="var(--cros-sys-illo-secondary)" stroke-width="3" stroke-miterlimit="10" />
+ <path d="M204.704 187.052C215.197 187.052 223.704 178.545 223.704 168.052C223.704 157.558 215.197 149.052 204.704 149.052C194.21 149.052 185.704 157.558 185.704 168.052C185.704 178.545 194.21 187.052 204.704 187.052Z" stroke="var(--cros-sys-illo-color4)" stroke-width="3" stroke-miterlimit="10" />
+ <circle cx="159" cy="96.0518" r="46" fill="var(--cros-sys-illo-secondary)" />
+ <path d="M152.325 106.303L153.411 103.123C153.938 101.577 154.568 100.313 155.298 99.3294C156.088 98.317 157.075 97.4462 158.261 96.717C159.507 95.9588 161.032 95.1731 162.839 94.36C164.112 93.8086 165.26 93.2637 166.282 92.7252C167.304 92.1867 168.165 91.5437 168.864 90.7962C169.623 90.0196 170.198 89.0572 170.59 87.909C171.299 85.8334 171.195 83.8994 170.279 82.1069C169.406 80.3296 167.733 79.0186 165.26 78.1739C163.671 77.6309 162.211 77.5021 160.88 77.7875C159.55 78.0728 158.38 78.6102 157.371 79.3997C156.361 80.1892 155.507 81.0314 154.806 81.9265L150.386 77.9757C151.339 76.7726 152.625 75.6089 154.241 74.4844C155.858 73.36 157.753 72.5771 159.926 72.1356C162.098 71.6941 164.487 71.9183 167.093 72.8083C169.787 73.7283 171.939 75.0798 173.549 76.8626C175.219 78.6163 176.281 80.6312 176.737 82.9072C177.251 85.1542 177.101 87.47 176.287 89.8548C175.653 91.7096 174.69 93.2299 173.398 94.4157C172.149 95.6167 170.836 96.5736 169.458 97.2866C168.096 97.9554 166.918 98.5149 165.926 98.965C164.712 99.4874 163.66 100.04 162.771 100.624C161.881 101.208 161.131 101.889 160.52 102.666C159.909 103.444 159.415 104.385 159.038 105.489L158.088 108.271L152.325 106.303ZM150.33 121.673C149.137 121.266 148.257 120.521 147.69 119.44C147.181 118.329 147.131 117.178 147.538 115.985C147.93 114.837 148.66 114.001 149.726 113.478C150.852 112.925 152.011 112.853 153.203 113.26C154.351 113.652 155.179 114.404 155.688 115.514C156.255 116.596 156.343 117.71 155.951 118.859C155.544 120.051 154.777 120.923 153.652 121.476C152.585 121.999 151.478 122.065 150.33 121.673Z" fill="var(--cros-sys-illo-base)" />
+ </svg>
</if>
<!--
diff --git a/chromium/chrome/browser/resources/print_preview/ui/media_size_settings.html b/chromium/chrome/browser/resources/print_preview/ui/media_size_settings.html
index bcf5dec989c..9183bb3d8d0 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/media_size_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/ui/media_size_settings.html
@@ -12,3 +12,16 @@
</print-preview-settings-select>
</div>
</print-preview-settings-section>
+
+<iron-collapse opened="[[settings.borderless.available]]">
+ <print-preview-settings-section>
+ <div slot="title"></div>
+ <div slot="controls" class="checkbox">
+ <cr-checkbox id="borderless" aria-labelledby="borderless-label"
+ disabled$="[[disabled]]"
+ on-change="onBorderlessCheckboxChange_">
+ <span id="borderless-label">$i18n{borderlessLabel}</span>
+ </cr-checkbox>
+ </div>
+ </print-preview-settings-section>
+</iron-collapse>
diff --git a/chromium/chrome/browser/resources/print_preview/ui/media_size_settings.ts b/chromium/chrome/browser/resources/print_preview/ui/media_size_settings.ts
index 40ecf4b0389..307ad60a402 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/media_size_settings.ts
+++ b/chromium/chrome/browser/resources/print_preview/ui/media_size_settings.ts
@@ -6,6 +6,8 @@ import './print_preview_shared.css.js';
import './settings_section.js';
import './settings_select.js';
+import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {MediaSizeCapability} from '../data/cdd.js';
@@ -13,6 +15,12 @@ import {MediaSizeCapability} from '../data/cdd.js';
import {getTemplate} from './media_size_settings.html.js';
import {SettingsMixin} from './settings_mixin.js';
+export interface PrintPreviewMediaSizeSettingsElement {
+ $: {
+ borderless: CrCheckboxElement,
+ };
+}
+
const PrintPreviewMediaSizeSettingsElementBase = SettingsMixin(PolymerElement);
export class PrintPreviewMediaSizeSettingsElement extends
@@ -39,6 +47,8 @@ export class PrintPreviewMediaSizeSettingsElement extends
static get observers() {
return [
'onMediaSizeSettingChange_(settings.mediaSize.*, capability.option)',
+ 'updateBorderlessAvailabilityForSize_(settings.mediaSize.*)',
+ 'onBorderlessSettingChange_(settings.borderless.*)',
];
}
@@ -59,6 +69,45 @@ export class PrintPreviewMediaSizeSettingsElement extends
this.capability.option[0];
this.setSetting('mediaSize', defaultOption);
}
+
+ private updateBorderlessAvailabilityForSize_() {
+ if (!loadTimeData.getBoolean('isBorderlessPrintingEnabled')) {
+ return;
+ }
+ const size = this.getSettingValue('mediaSize');
+ if (size?.has_borderless_variant) {
+ this.$.borderless.disabled = false;
+ this.$.borderless.checked = this.getSettingValue('borderless');
+ } else {
+ // If a size only supports borderless and has no bordered variant,
+ // has_borderless_variant will be false. In this case, the checkbox
+ // will be disabled (it wouldn't have any effect), but will display
+ // as checked to indicate that the print will be borderless. This is
+ // a corner case, but printers are allowed to do it, so it's best to
+ // handle it as well as possible. If a size only supports bordered and
+ // not borderless, disable the checkbox and leave it unchecked.
+ this.$.borderless.disabled = true;
+ this.$.borderless.checked =
+ (size?.imageable_area_left_microns === 0 &&
+ size?.imageable_area_bottom_microns === 0 &&
+ size?.imageable_area_right_microns === size.width_microns &&
+ size?.imageable_area_top_microns === size.height_microns);
+ }
+ }
+
+ private onBorderlessSettingChange_() {
+ if (!loadTimeData.getBoolean('isBorderlessPrintingEnabled')) {
+ return;
+ }
+ this.$.borderless.checked = this.getSettingValue('borderless');
+ }
+
+ private onBorderlessCheckboxChange_() {
+ if (!loadTimeData.getBoolean('isBorderlessPrintingEnabled')) {
+ return;
+ }
+ this.setSetting('borderless', this.$.borderless.checked);
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/print_preview/ui/media_type_settings.html b/chromium/chrome/browser/resources/print_preview/ui/media_type_settings.html
new file mode 100644
index 00000000000..ab663f264f2
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/ui/media_type_settings.html
@@ -0,0 +1,14 @@
+<style include="print-preview-shared">
+ :host print-preview-settings-select {
+ margin: 0 calc(var(--print-preview-sidebar-margin) - 2px);
+ }
+</style>
+<print-preview-settings-section>
+ <span id="media-type-label" slot="title">$i18n{mediaTypeLabel}</span>
+ <div slot="controls">
+ <print-preview-settings-select aria-label="$i18n{mediaTypeLabel}"
+ capability="[[capability]]" setting-name="mediaType"
+ settings="{{settings}}" disabled="[[disabled]]">
+ </print-preview-settings-select>
+ </div>
+</print-preview-settings-section>
diff --git a/chromium/chrome/browser/resources/print_preview/ui/media_type_settings.ts b/chromium/chrome/browser/resources/print_preview/ui/media_type_settings.ts
new file mode 100644
index 00000000000..80db22fa768
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/ui/media_type_settings.ts
@@ -0,0 +1,78 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import './print_preview_shared.css.js';
+import './settings_section.js';
+import '../strings.m.js';
+import './settings_select.js';
+
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {MediaTypeCapability, MediaTypeOption, SelectOption} from '../data/cdd.js';
+
+import {getTemplate} from './media_type_settings.html.js';
+import {SettingsMixin} from './settings_mixin.js';
+
+type LabelledMediaTypeOption = MediaTypeOption&SelectOption;
+export interface LabelledMediaTypeCapability {
+ option: LabelledMediaTypeOption[];
+}
+
+const PrintPreviewMediaTypeSettingsElementBase = SettingsMixin(PolymerElement);
+
+export class PrintPreviewMediaTypeSettingsElement extends
+ PrintPreviewMediaTypeSettingsElementBase {
+ static get is() {
+ return 'print-preview-media-type-settings';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ capability: Object,
+
+ disabled: Boolean,
+ };
+ }
+
+ static get observers() {
+ return [
+ 'onMediaTypeSettingChange_(settings.mediaType.*, capability.option)',
+ ];
+ }
+
+ capability: MediaTypeCapability;
+ disabled: boolean;
+
+ private onMediaTypeSettingChange_() : void {
+ if (!this.capability) {
+ return;
+ }
+ const valueToSet = JSON.stringify(this.getSettingValue('mediaType'));
+ for (const option of this.capability.option) {
+ if (JSON.stringify(option) === valueToSet) {
+ this.shadowRoot!.querySelector('print-preview-settings-select')!
+ .selectValue(valueToSet);
+ return;
+ }
+ }
+
+ const defaultOption = this.capability.option.find(o => !!o.is_default) ||
+ this.capability.option[0];
+ this.setSetting('mediaType', defaultOption);
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'print-preview-media-type-settings': PrintPreviewMediaTypeSettingsElement;
+ }
+}
+
+customElements.define(
+ PrintPreviewMediaTypeSettingsElement.is,
+ PrintPreviewMediaTypeSettingsElement);
diff --git a/chromium/chrome/browser/resources/print_preview/ui/preview_area.html b/chromium/chrome/browser/resources/print_preview/ui/preview_area.html
index b6f185e5cd8..cc26c24169d 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/preview_area.html
+++ b/chromium/chrome/browser/resources/print_preview/ui/preview_area.html
@@ -9,6 +9,24 @@
100% { top: 0; }
}
+/* <if expr="is_chromeos"> */
+ :host([show-cros-printer-setup-info_]) .preview-area-message {
+ background: rgb(255, 255, 255);
+ border-radius: 16px;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ justify-content: center;
+ margin: 16px;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ :host([show-cros-printer-setup-info_]) .preview-area-message {
+ background: rgb(40, 41, 44);
+ }
+ }
+/* </if> */
+
span.jumping-dots > span {
animation: dancing-dots-jump 1800ms infinite;
padding: 1px;
@@ -85,13 +103,23 @@
aria-hidden$="[[getAriaHidden_(previewState)]]">
<div class="preview-area-message">
<div>
- <span inner-h-t-m-l="[[currentMessage_(previewState)]]"></span>
+ <span
+ hidden$="[[showCrosPrinterSetupInfo_]]"
+ inner-h-t-m-l="[[currentMessage_(previewState)]]">
+ </span>
<span class$="preview-area-loading-message-jumping-dots
[[getJumpingDots_(previewState)]]"
hidden$="[[!isPreviewLoading_(previewState)]]">
<span>.</span><span>.</span><span>.</span>
</span>
</div>
+ <if expr="is_chromeos">
+ <print-preview-printer-setup-info-cros
+ hidden$="[[!showCrosPrinterSetupInfo_]]"
+ message-type="[[printerOffline_]]"
+ metrics-source="[[previewAreaSource_]]">
+ </print-preview-printer-setup-info-cros>
+ </if>
</div>
</div>
<div class="preview-area-plugin-wrapper"></div>
diff --git a/chromium/chrome/browser/resources/print_preview/ui/preview_area.ts b/chromium/chrome/browser/resources/print_preview/ui/preview_area.ts
index af35ba29983..960822fe1e6 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/preview_area.ts
+++ b/chromium/chrome/browser/resources/print_preview/ui/preview_area.ts
@@ -4,12 +4,18 @@
import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+// <if expr="is_chromeos">
+import './printer_setup_info_cros.js';
+// </if>
import './print_preview_vars.css.js';
import '../strings.m.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
-import {hasKeyModifiers} from 'chrome://resources/js/util_ts.js';
import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+// <if expr="is_chromeos">
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+// </if>
+import {hasKeyModifiers} from 'chrome://resources/js/util_ts.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {DarkModeMixin} from '../dark_mode_mixin.js';
@@ -27,6 +33,9 @@ import {areRangesEqual} from '../print_preview_utils.js';
import {MARGIN_KEY_MAP, PrintPreviewMarginControlContainerElement} from './margin_control_container.js';
import {PluginProxy, PluginProxyImpl} from './plugin_proxy.js';
import {getTemplate} from './preview_area.html.js';
+// <if expr="is_chromeos">
+import {PrinterSetupInfoMessageType, PrinterSetupInfoMetricsSource} from './printer_setup_info_cros.js';
+// </if>
import {SettingsMixin} from './settings_mixin.js';
export type PreviewTicket = Ticket&{
@@ -102,6 +111,35 @@ export class PrintPreviewPreviewAreaElement extends
notify: true,
computed: 'computePreviewLoaded_(documentReady_, pluginLoadComplete_)',
},
+
+ // <if expr="is_chromeos">
+ isPrintPreviewSetupAssistanceEnabled_: {
+ type: Boolean,
+ value: () => {
+ return loadTimeData.getBoolean(
+ 'isPrintPreviewSetupAssistanceEnabled');
+ },
+ readOnly: true,
+ },
+
+ printerOffline_: {
+ type: Number,
+ value: PrinterSetupInfoMessageType.PRINTER_OFFLINE,
+ readOnly: true,
+ },
+
+ previewAreaSource_: {
+ type: Number,
+ value: PrinterSetupInfoMetricsSource.PREVIEW_AREA,
+ readOnly: true,
+ },
+ // </if>
+
+ showCrosPrinterSetupInfo_: {
+ type: Boolean,
+ computed: 'computeShowCrosPrinterSetupInfo(state, error)',
+ reflectToAttribute: true,
+ },
};
}
@@ -124,7 +162,11 @@ export class PrintPreviewPreviewAreaElement extends
private pluginLoadComplete_: boolean;
private documentReady_: boolean;
private previewLoaded_: boolean;
+ // <if expr="is_chromeos">
+ private isPrintPreviewSetupAssistanceEnabled_: boolean;
+ // </if>
+ private showCrosPrinterSetupInfo_: boolean = false;
private nativeLayer_: NativeLayer|null = null;
private lastTicket_: PreviewTicket|null = null;
private inFlightRequestId_: number = -1;
@@ -757,6 +799,21 @@ export class PrintPreviewPreviewAreaElement extends
return window.trustedTypes!.emptyHTML;
}
}
+
+ /**
+ * Determines if setup info element should be shown instead of the preview
+ * area message. For ChromeOS, setup assistance is shown if the flag is
+ * enabled and the `INVALID_PRINTER` error has occurred. All other platforms
+ * `computeShowCrosPrinterSetupInfo` will return false.
+ */
+ private computeShowCrosPrinterSetupInfo(): boolean {
+ // <if expr="is_chromeos">
+ if (this.isPrintPreviewSetupAssistanceEnabled_) {
+ return this.state === State.ERROR && this.error === Error.INVALID_PRINTER;
+ }
+ // </if>
+ return false;
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/print_preview/ui/print_preview_vars.css b/chromium/chrome/browser/resources/print_preview/ui/print_preview_vars.css
index 7e82b84560c..3b5cc4b6165 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/print_preview_vars.css
+++ b/chromium/chrome/browser/resources/print_preview/ui/print_preview_vars.css
@@ -28,6 +28,13 @@ html {
--iron-icon-width: var(--cr-icon-size);
--search-icon-size: 32px;
--throbber-size: 16px;
+
+<if expr="is_chromeos">
+ /* TODO(b/290239226): Remove hard-coded and google colors after print-preview
+ is configured to use dynamic colors. */
+ --error-status-alert: var(--google-red-600);
+ --error-status-warning: #e37400;
+</if>
}
@media (prefers-color-scheme: dark) {
@@ -35,5 +42,12 @@ html {
--preview-area-background-color: var(--google-grey-700);
--print-preview-settings-border: var(--cr-separator-line);
--iron-icon-fill-color: var(--google-grey-500);
+
+<if expr="is_chromeos">
+ /* TODO(b/290239226): Remove hard-coded and google colors after
+ print-preview is configured to use dynamic colors. */
+ --error-status-alert: var(--google-red-300);
+ --error-status-warning: #fdd663;
+</if>
}
}
diff --git a/chromium/chrome/browser/resources/print_preview/ui/printer_setup_info_cros.html b/chromium/chrome/browser/resources/print_preview/ui/printer_setup_info_cros.html
new file mode 100644
index 00000000000..648b931eebd
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/ui/printer_setup_info_cros.html
@@ -0,0 +1,77 @@
+<style include="print-preview-shared">
+ .container {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ justify-content: center;
+ margin-block: 16px 0;
+ margin-inline: auto;
+ text-align: center;
+ width: 368px;
+ }
+
+ .message-container {
+ /* TODO(b/290239226): Replace with on-surface-variant after print-preview is
+ configured to use dynamic styling. */
+ color: var(--google-grey-700);
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .message-detail {
+ font: var(--cros-body-1-font);
+ margin-block: 0;
+ }
+
+ .message-heading {
+ font: var(--cros-display-7-font);
+ margin-block: 0;
+ }
+
+ cr-button {
+ font: var(--cros-button-2-font);
+ }
+
+ /* TODO(b/290239226): Remove hard-coded SVG colors after print-preview is
+ configured to use dynamic colors. */
+ iron-icon {
+ --cros-sys-illo-base: #FFFFFF;
+ --cros-sys-illo-color1-2: #DBE1FF;
+ --cros-sys-illo-color3: #EE9829;
+ --cros-sys-illo-color4: #FF5449;
+ --cros-sys-illo-color5: #C6A0BF;
+ --cros-sys-illo-secondary: #E2E1EC;
+ --iron-icon-height: 200px;
+ --iron-icon-width: 268px;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ .message-container {
+ /* TODO(b/290239226): Remove after print-preview is configured to use
+ dynamic styling. */
+ color: var(--google-grey-400);
+ }
+
+ iron-icon {
+ --cros-sys-illo-base: #000000;
+ --cros-sys-illo-color1-2: #414659;
+ --cros-sys-illo-color3: #FFB866;
+ --cros-sys-illo-color4: #FF5449;
+ --cros-sys-illo-color5: #745470;
+ --cros-sys-illo-secondary: #5D5E67;
+ }
+ }
+</style>
+<div class="container">
+ <iron-icon icon="print-preview:no-printer-available"></iron-icon>
+ <div class="message-container">
+ <h2 class="message-heading">[[getMessageHeading(messageType)]]</h2>
+ <p class="message-detail">[[getMessageDetail(messageType)]]</p>
+ </div>
+ <cr-button class="action-button" on-click="onManagePrintersClicked"
+ hidden$="[[!showManagePrintersButton]]">
+ $i18n{managePrintersLabel}
+ </cr-button>
+</div>
diff --git a/chromium/chrome/browser/resources/print_preview/ui/printer_setup_info_cros.ts b/chromium/chrome/browser/resources/print_preview/ui/printer_setup_info_cros.ts
new file mode 100644
index 00000000000..cc539ce1825
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/ui/printer_setup_info_cros.ts
@@ -0,0 +1,148 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+import './print_preview_shared.css.js';
+
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {MetricsContext, PrintPreviewLaunchSourceBucket} from '../metrics.js';
+import {NativeLayer, NativeLayerImpl} from '../native_layer.js';
+import {NativeLayerCrosImpl} from '../native_layer_cros.js';
+
+import {getTemplate} from './printer_setup_info_cros.html.js';
+
+/**
+ * @fileoverview PrintPreviewPrinterSetupInfoCrosElement
+ * This element provides contextual instructions to help users navigate
+ * to printer settings based on the state of printers available in
+ * print-preview. Element will use NativeLayer to open the correct printer
+ * settings interface.
+ */
+
+const PrintPreviewPrinterSetupInfoCrosElementBase = I18nMixin(PolymerElement);
+
+export enum PrinterSetupInfoMetricsSource {
+ PREVIEW_AREA,
+ DESTINATION_DIALOG_CROS,
+}
+
+export enum PrinterSetupInfoMessageType {
+ NO_PRINTERS,
+ PRINTER_OFFLINE,
+}
+
+interface PrinterSetupInfoMessageData {
+ detailKey: string;
+ headingKey: string;
+}
+
+const MESSAGE_TYPE_LOCALIZED_STRINGS_MAP =
+ new Map<PrinterSetupInfoMessageType, PrinterSetupInfoMessageData>([
+ [
+ PrinterSetupInfoMessageType.NO_PRINTERS,
+ {
+ headingKey: 'printerSetupInfoMessageHeadingNoPrintersText',
+ detailKey: 'printerSetupInfoMessageDetailNoPrintersText',
+ },
+ ],
+ [
+ PrinterSetupInfoMessageType.PRINTER_OFFLINE,
+ {
+ headingKey: 'printerSetupInfoMessageHeadingPrinterOfflineText',
+ detailKey: 'printerSetupInfoMessageDetailPrinterOfflineText',
+ },
+ ],
+ ]);
+
+export class PrintPreviewPrinterSetupInfoCrosElement extends
+ PrintPreviewPrinterSetupInfoCrosElementBase {
+ static get is() {
+ return 'print-preview-printer-setup-info-cros' as const;
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ messageType: {
+ type: Number,
+ value: PrinterSetupInfoMessageType.NO_PRINTERS,
+ },
+
+ metricsSource: Number,
+
+ showManagePrintersButton: Boolean,
+ };
+ }
+
+ messageType: PrinterSetupInfoMessageType;
+ private metricsSource: PrinterSetupInfoMetricsSource;
+ private nativeLayer: NativeLayer;
+ private metricsContext: MetricsContext;
+ private showManagePrintersButton: boolean = false;
+
+ override connectedCallback() {
+ super.connectedCallback();
+ this.nativeLayer = NativeLayerImpl.getInstance();
+ this.metricsContext =
+ MetricsContext.getLaunchPrinterSettingsMetricsContextCros();
+ NativeLayerCrosImpl.getInstance().getShowManagePrinters().then(
+ (show: boolean) => {
+ this.showManagePrintersButton = show;
+ });
+ }
+
+ private getMessageDetail(): string {
+ const messageData =
+ MESSAGE_TYPE_LOCALIZED_STRINGS_MAP.get(this.messageType);
+ assert(messageData);
+ return this.i18n(messageData!.detailKey);
+ }
+
+ private getMessageHeading(): string {
+ const messageData =
+ MESSAGE_TYPE_LOCALIZED_STRINGS_MAP.get(this.messageType);
+ assert(messageData);
+ return this.i18n(messageData!.headingKey);
+ }
+
+ private onManagePrintersClicked(): void {
+ this.nativeLayer.managePrinters();
+ switch (this.metricsSource) {
+ case PrinterSetupInfoMetricsSource.PREVIEW_AREA:
+ this.metricsContext.record(
+ PrintPreviewLaunchSourceBucket.PREVIEW_AREA_CONNECTION_ERROR);
+ break;
+ case PrinterSetupInfoMetricsSource.DESTINATION_DIALOG_CROS:
+ // `<print-preview-printer-setup-info-cros>` is only displayed when
+ // there are no printers.
+ this.metricsContext.record(
+ PrintPreviewLaunchSourceBucket.DESTINATION_DIALOG_CROS_NO_PRINTERS);
+ break;
+ default:
+ assertNotReached();
+ }
+ }
+
+ setMetricsSourceForTesting(source: PrinterSetupInfoMetricsSource): void {
+ this.metricsSource = source;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ [PrintPreviewPrinterSetupInfoCrosElement.is]:
+ PrintPreviewPrinterSetupInfoCrosElement;
+ }
+}
+
+customElements.define(
+ PrintPreviewPrinterSetupInfoCrosElement.is,
+ PrintPreviewPrinterSetupInfoCrosElement);
diff --git a/chromium/chrome/browser/resources/print_preview/ui/sidebar.html b/chromium/chrome/browser/resources/print_preview/ui/sidebar.html
index 04aab87985a..d75b86931e6 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/sidebar.html
+++ b/chromium/chrome/browser/resources/print_preview/ui/sidebar.html
@@ -85,6 +85,11 @@
hidden$="[[!settings.mediaSize.available]]"
class="settings-section">
</print-preview-media-size-settings>
+ <print-preview-media-type-settings settings="[[settings]]" state="[[state]]"
+ capability="[[destination.capabilities.printer.media_type]]"
+ disabled="[[controlsDisabled_]]"
+ hidden$="[[!settings.mediaType.available]]" class="settings-section">
+ </print-preview-media-type-settings>
<print-preview-pages-per-sheet-settings settings="[[settings]]"
disabled="[[controlsDisabled_]]"
hidden$="[[!settings.pagesPerSheet.available]]"
diff --git a/chromium/chrome/browser/resources/print_preview/ui/sidebar.ts b/chromium/chrome/browser/resources/print_preview/ui/sidebar.ts
index ef555cb0a8e..609767bf7f0 100644
--- a/chromium/chrome/browser/resources/print_preview/ui/sidebar.ts
+++ b/chromium/chrome/browser/resources/print_preview/ui/sidebar.ts
@@ -15,6 +15,7 @@ import './duplex_settings.js';
import './header.js';
import './layout_settings.js';
import './media_size_settings.js';
+import './media_type_settings.js';
import './margins_settings.js';
import './more_settings.js';
import './other_options_settings.js';
@@ -174,11 +175,14 @@ export class PrintPreviewSidebarElement extends PrintPreviewSidebarElementBase {
pdfPrinterDisabled: boolean, isDriveMounted: boolean) {
this.isInAppKioskMode_ = appKioskMode;
pdfPrinterDisabled = this.isInAppKioskMode_ || pdfPrinterDisabled;
- // If PDF printing is disabled, then Save to Drive also needs to be disabled
- // on Chrome OS.
- isDriveMounted = !pdfPrinterDisabled && isDriveMounted;
+
+ // 'Save to Google Drive' is almost the same as PDF printing. The only
+ // difference is the default location shown in the file picker when user
+ // clicks 'Save'. Therefore, we should disable the 'Save to Google Drive'
+ // destination if the user should be blocked from using PDF printing.
+ const saveToDriveDisabled = pdfPrinterDisabled || !isDriveMounted;
this.$.destinationSettings.init(
- defaultPrinter, pdfPrinterDisabled, isDriveMounted,
+ defaultPrinter, pdfPrinterDisabled, saveToDriveDisabled,
serializedDestinationSelectionRulesStr);
}
diff --git a/chromium/chrome/browser/resources/privacy_sandbox/DIR_METADATA b/chromium/chrome/browser/resources/privacy_sandbox/DIR_METADATA
index 7d283fa6a30..a01852cff95 100644
--- a/chromium/chrome/browser/resources/privacy_sandbox/DIR_METADATA
+++ b/chromium/chrome/browser/resources/privacy_sandbox/DIR_METADATA
@@ -2,4 +2,4 @@ monorail: {
component: "UI>Settings>Privacy"
}
-team_email: "chrome-friendly-settings@google.com"
+team_email: "chrome-privacy-controls@google.com"
diff --git a/chromium/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_mixin.ts b/chromium/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_mixin.ts
index d3a8828a812..d7a5673c34a 100644
--- a/chromium/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_mixin.ts
+++ b/chromium/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_mixin.ts
@@ -158,7 +158,12 @@ export const PrivacySandboxDialogMixin = dedupingMixin(
};
const observer = new IntersectionObserver(entries => {
assert(entries.length === 1);
- this.wasScrolledToBottom = entries[0].intersectionRatio === 1;
+ // We cannot check for intersectionRatio strictly equal to 1
+ // because its value is sometimes reported with ~0.99 values
+ // (see crbug.com/1020466): this can lead to a state where
+ // the more button is always visible, with unclickable action
+ // buttons covered by an overlay (b/299120185).
+ this.wasScrolledToBottom = entries[0].intersectionRatio >= 0.99;
// After the whole text content was visible at least once, stop
// observing.
diff --git a/chromium/chrome/browser/resources/safe_browsing/DIR_METADATA b/chromium/chrome/browser/resources/safe_browsing/DIR_METADATA
deleted file mode 100644
index 6015b06be93..00000000000
--- a/chromium/chrome/browser/resources/safe_browsing/DIR_METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-monorail: {
- component: "Services>Safebrowsing"
-}
diff --git a/chromium/chrome/browser/resources/safe_browsing/OWNERS b/chromium/chrome/browser/resources/safe_browsing/OWNERS
deleted file mode 100644
index 5225674f443..00000000000
--- a/chromium/chrome/browser/resources/safe_browsing/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/safe_browsing/OWNERS
diff --git a/chromium/chrome/browser/resources/safe_browsing/README.md b/chromium/chrome/browser/resources/safe_browsing/README.md
deleted file mode 100644
index 08952f67aad..00000000000
--- a/chromium/chrome/browser/resources/safe_browsing/README.md
+++ /dev/null
@@ -1,228 +0,0 @@
-# Behavior of Download File Types in Chrome
-
-This describes how to adjust file-type download behavior in
-Chrome including interactions with Safe Browsing. The metadata described
-here, and stored in `download_file_types.asciipb`, will be both baked into
-Chrome released and pushable to Chrome between releases (via
-`FileTypePolicies` class). http://crbug.com/596555
-
-Rendered version of this file: https://chromium.googlesource.com/chromium/src/+/main/chrome/browser/resources/safe_browsing/README.md
-
-
-## Procedure for adding/modifying file type(s)
- * **Edit** `download_file_types.asciipb` and `enums.xml`.
- * Get it reviewed, **submit.**
- * **Push** it to all users via component update:
- * Wait 1-3 day for this to run on Canary to verify it doesn't crash Chrome.
- * In a synced checkout, run the following to generate protos for all
- platforms and push them to GCS. Replace the arg with your build directory:
- * % `components/safe_browsing/content/resources/push_file_type_proto.py -d
- out-gn/Debug`
- * It will ask you to double check its actions before proceeding. It will
- fail if you're not a member of
- `chrome-file-type-policies-pushers@google.com`, since that's required for
- access to the GCS bucket.
- * The Component Updater system will notice those files and push them to
- users within ~6 hours. If not, contact `waffles@.`
-
-## Procedure for rollback
-While Omaha allows rollback through the release manager, the Chrome client will
-reject updates with lower version numbers. (This is important for running new
-versions on Canary/Dev channel). Rolling back a bad version is best achieved by:
- * **Reverting** the changes on the Chromium source tree.
- * **Submitting** a new CL incrementing the version number.
- * **Push** the newest version, as above.
-
-## Procedure for incremental rollout
- * Open the Omaha Release manager at:
- https://omaharelease.corp.google.com/product/1436/cohorts
- * **Disable** the automatic push by changing the _Push Scheduler_ from
- `LATEST_TO_AUTO` to `NONE`. Then commit the changes by clicking _Commit
- Automation Changes_.
- * **Upload** the new version of the file types.
- * In a synced checkout, run the following to generate protos for all
- platforms and push them to GCS. Replace the arg with your build directory:
- * % `components/safe_browsing/content/resources/push_file_type_proto.py -d
- out-gn/Debug`
- * Create a new cohort.
- * Under _Cohorts_, click _Manage_, then _Create Subcohort_ of Auto.
- * Select a name and percentage for the new cohort.
- * Return to the product page.
- * **Push** to the new cohort.
- * Select _Edit Schedule_ for the new cohort, and select the newest file
- group and a time to roll out.
- * After the incremental rollout is complete, remember to set the _Push
- Scheduler_ back to `LATEST_TO_AUTO`
-
-Note: Everything after disabling automation could be scripted through the
-[ReleaseServiceManager](http://google3/java/com/google/installer/releasemanager/proto/releasemanager_stubby.proto).
-An example script using this API is
-[here](http://google3/googleclient/installer/tools/release/keystone/create_release_testing_cohorts.py).
-If we regularly need incremental rollouts, it may be worth creating our own
-scripts to do so reliably.
-
-## Procedure for incremental rollout with A/B testing
- * Open the Omaha Release manager at:
- https://omaharelease.corp.google.com/product/1436/cohorts
- * **Disable** the automatic push by changing the _Push Scheduler_ from
- `LATEST_TO_AUTO` to `NONE`. Then commit the changes by clicking _Commit
- Automation Changes_.
- * **Edit** the `download_file_types_experiment.asciipb` file. Make sure the
- version in this file is greater than the version in
- `download_file_types.asciipb`. Do not edit `download_file_types.asciipb`.
- Otherwise, the experiment version will be automatically released with Chrome
- binary.
- * **Upload** the new version of the file types.
- * In a synced checkout, run the following to generate experiment protos for all
- platforms and push them to GCS. Replace the arg with your build directory:
- * % `components/safe_browsing/content/resources/push_file_type_proto.py -d
- out-gn/Debug --experiment`
- * Create a new cohort.
- * Name the new cohort with the new experiment version (e.g. `v44`).
- * Select the file group as `auto_$EXPERIMENT_VERSION_ID`.
- * Add an attribute with name `tag` and value `$EXPERIMENT_VERSION_ID`
- (e.g. `44`).
- * Keep the `auto` cohort unchanged. The auto cohort should be matching to
- any tag value.
- * **Rollout** Create a finch study to rollout the new version.
- * The finch study should set the `tag` value to `$EXPERIMENT_VERSION_ID`
- that is sent to Omaha.
- * Recommended schedule: Canary_Dev 50% -> Beta 50% -> Stable 1% -> Launched.
- * **Cleanup** After the incremental rollout is complete, perform the following
- cleanup:
- * Update `download_file_types.asciipb` with changes in
- `download_file_types_experiment.asciipb`. Make sure the `version_id` is
- also updated to be equal to the `version_id` in the experiment config.
- Chrome will then get the new version by default.
- * Update the `auto` cohort to match to the new file group. Delete the
- experiment cohort.
- * set the _Push Scheduler_ back to `LATEST_TO_AUTO`.
-
-## Guidelines for a DownloadFileType entry:
-See `download_file_types.proto` for all fields.
-
- * `extension`: (required) Value must be unique within the config. It should be
- lowercase ASCII and not contain a dot. If there _is_ a duplicate,
- first one wins. Only the `default_file_type` should leave this unset.
-
- * `uma_value`: (required) must be unique and match one in the
- `SBClientDownloadExtensions` enum in `enums.xml`.
-
- * `is_archive`: `True` if this filetype is a container for other files.
- Leave it unset for `false`.
-
- * `ping_setting`: (required). This controls what sort of ping is sent
- to Safe Browsing and if a verdict is checked before the user can
- access the file.
-
- * `SAMPLED_PING`: Don't send a full Safe Browsing ping, but
- send a no-PII "light-ping" for a random sample of SBER users.
- This should be used for known safe types. The verdict won't be used.
-
- * `NO_PING`: Don’t send any pings. This file is allowlisted. All
- NOT_DANGEROUS files should normally use this.
- * `FULL_PING`: Send full pings and use the verdict. All dangerous
- file should use this.
-
- * `platform_settings`: (repeated) Zero or more settings to differentiate
- behavior by platform. Keep them sorted by platform. At build time,
- this list will be filtered to contain exactly one setting by choosing
- as follows before writing out the binary proto.
-
- 1. If there's an entry matching the built platform,
- that will be preferred. Otherwise,
-
- 2. If there's a "PLATFORM_TYPE_ANY" (i.e. `platform` is not set),
- that will be used. Otherwise,
-
- 3. The `default_file_type`'s settings will be filled in.
-
- **Warning**: When specifying a new `platform_settings` for a file type, be
- sure to specify values for all necessary settings. The `platform_settings`
- will override all of the `default_file_type`'s settings, and this may result
- in a change in behavior for `platform_settings` left unspecified. For
- example, see [crbug.com/946558](https://crbug.com/946558#c5).
-
- * `platform_settings.danger_level`: (required) Controls how files should be
- handled by the UI in the absence of a better signal from the Safe Browsing
- ping. This applies to all file types where `ping_setting` is either
- `SAMPLED_PING` or `NO_PING`, and downloads where the Safe Browsing ping
- either fails, is disabled, or returns an `UNKNOWN` verdict. Exceptions are
- noted below.
-
- The warning controlled here is a generic "This file may harm your computer."
- If the Safe Browsing verdict is `UNCOMMON`, `POTENTIALLY_UNWANTED`,
- `DANGEROUS_HOST`, or `DANGEROUS`, Chrome will show that more severe warning
- regardless of this setting.
-
- This policy also affects also how subresources are handled for *"Save As
- ..."* downloads of complete web pages. If any subresource ends up with a
- file type that is considered `DANGEROUS` or `ALLOW_ON_USER_GESTURE`, then
- the filename will be changed to end in `.download`. This is done to prevent
- the file from being opened accidentally.
-
- * `NOT_DANGEROUS`: Safe to download and open, even if the download
- was accidental. No additional warnings are necessary.
- * `DANGEROUS`: Always warn the user that this file may harm their
- computer. We let them continue or discard the file. If Safe
- Browsing returns a `SAFE` verdict, we still warn the user.
- * `ALLOW_ON_USER_GESTURE`: Potentially dangerous, but is likely harmless if
- the user is familiar with host and if the download was intentional. Chrome
- doesn't warn the user if both of the following conditions are true:
-
- * There is a user gesture associated with the network request that
- initiated the download.
- * There is a recorded visit to the referring origin that's older than
- the most recent midnight. This is taken to imply that the user has a
- history of visiting the site.
-
- In addition, Chrome skips the warning if the download was explicit (i.e.
- the user selected "Save link as ..." from the context menu), or if the
- navigation that resulted in the download was initiated using the Omnibox.
-
- If the `SafeBrowsingForTrustedSourcesEnabled` policy is set and the download
- originates from a Trusted source, no warnings will be shown even for types
- with a `danger_level` of `DANGEROUS` or `ALLOW_ON_USER_GESTURE`.
-
- * `platform_settings.auto_open_hint`: (required).
- * `ALLOW_AUTO_OPEN`: File type can be opened automatically if the user
- selected that option from the download tray on a previous download
- of this type.
- * `DISALLOW_AUTO_OPEN`: Never let the file automatically open.
- Files that should be disallowed from auto-opening include those that
- execute arbitrary or harmful code with user privileges, or change
- configuration of the system to cause harmful behavior immediately
- or at some time in the future. We *do* allow auto-open for files
- that upon opening sufficiently warn the user about the fact that it
- was downloaded from the internet and can do damage. **Note**:
- Some file types (e.g.: .local and .manifest) aren't dangerous
- to open. However, their presence on the file system may cause
- potentially dangerous changes in behavior for other programs. We
- allow automatically opening these file types, but always warn when
- they are downloaded.
- * `platform_settings.max_file_size_to_analyze`: (optional).
- Size in bytes of the largest file that the analyzer is willing to inspect;
- for instance, a zip file larger than the threshold will not be unpacked
- to allow scanning of the files within.
-
- * TODO(nparker): Support this: `platform_settings.unpacker`:
- optional. Specifies which archive unpacker internal to Chrome
- should be used. If potentially dangerous file types are found,
- Chrome will send a full-ping for the entire file. Otherwise, it'll
- follow the ping settings. Can be one of UNPACKER_ZIP or UNPACKER_DMG.
-
-## Guidelines for the top level DownloadFileTypeConfig entry:
- * `version_id`: Must be increased (+1) every time the file is checked in.
- Will be logged to UMA.
-
- * `sampled_ping_probability`: For what fraction of extended-reporting
- users' downloads with unknown extensions (or
- ping_setting=SAMPLED_PING) should we send light-pings? [0.0 .. 1.0]
-
- * `file_types`: The big list of all known file types. Keep them
- sorted by extension.
-
- * `default_file_type`: Settings used if a downloaded file is not in
- the above list. `extension` is ignored, but other settings are used.
- The ping_setting should be FULL_PING for all platforms, so that
- unknown file types generate pings.
diff --git a/chromium/chrome/browser/resources/waffle/BUILD.gn b/chromium/chrome/browser/resources/search_engine_choice/BUILD.gn
index f701c4a9c61..4e008a6f1be 100644
--- a/chromium/chrome/browser/resources/waffle/BUILD.gn
+++ b/chromium/chrome/browser/resources/search_engine_choice/BUILD.gn
@@ -5,21 +5,19 @@
import("//components/signin/features.gni")
import("//ui/webui/resources/tools/build_webui.gni")
-assert(enable_waffle_desktop)
+assert(enable_search_engine_choice)
build_webui("build") {
- grd_prefix = "waffle"
+ grd_prefix = "search_engine_choice"
- static_files = [ "waffle.html" ]
+ static_files = [ "search_engine_choice.html" ]
web_component_files = [ "app.ts" ]
non_web_component_files = [ "browser_proxy.ts" ]
- mojo_files_deps =
- [ "//chrome/browser/ui/webui/waffle:mojo_bindings_ts__generator" ]
- mojo_files =
- [ "$root_gen_dir/chrome/browser/ui/webui/waffle/waffle.mojom-webui.ts" ]
+ mojo_files_deps = [ "//chrome/browser/ui/webui/search_engine_choice:mojo_bindings_ts__generator" ]
+ mojo_files = [ "$root_gen_dir/chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom-webui.ts" ]
ts_deps = [
"//third_party/polymer/v3_0:library",
@@ -27,4 +25,6 @@ build_webui("build") {
"//ui/webui/resources/js:build_ts",
"//ui/webui/resources/mojo:build_ts",
]
+
+ ts_composite = true
}
diff --git a/chromium/chrome/browser/resources/search_engine_choice/DIR_METADATA b/chromium/chrome/browser/resources/search_engine_choice/DIR_METADATA
new file mode 100644
index 00000000000..c8383f40dfb
--- /dev/null
+++ b/chromium/chrome/browser/resources/search_engine_choice/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chrome/browser/search_engine_choice/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/search_engine_choice/OWNERS b/chromium/chrome/browser/resources/search_engine_choice/OWNERS
new file mode 100644
index 00000000000..4b1c0994254
--- /dev/null
+++ b/chromium/chrome/browser/resources/search_engine_choice/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/search_engine_choice/OWNERS
diff --git a/chromium/chrome/browser/resources/search_engine_choice/app.html b/chromium/chrome/browser/resources/search_engine_choice/app.html
new file mode 100644
index 00000000000..ec03de2d939
--- /dev/null
+++ b/chromium/chrome/browser/resources/search_engine_choice/app.html
@@ -0,0 +1,270 @@
+<style include="cr-icons tangible-sync-style-shared">
+ :host {
+ --iron-icon-fill-color: var(--google-blue-700);
+ color: var(--cr-primary-text-color);
+ }
+
+ .tangible-sync-style-left-banner {
+ content: url(images/left_illustration.svg);
+ }
+
+ .tangible-sync-style-right-banner {
+ content: url(images/right_illustration.svg);
+ }
+
+ #submitButton {
+ height: unset;
+ margin: 40px;
+ /* The max-width is needed to stop the button from overlapping with
+ the choice list in case of large text. */
+ max-width: 115px;
+ pointer-events: all;
+ }
+
+ .content-container {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ margin: auto;
+ max-width: 522px;
+ text-align: center;
+ }
+
+ .tangible-sync-style .title {
+ margin-bottom: 12px;
+ }
+
+ .tangible-sync-style .subtitle {
+ line-height: 30px;
+ margin: 12px 0 40px;
+ }
+
+ .omnibox {
+ --omnibox-height: 48px;
+ align-items: center;
+ border: 1px dashed var(--google-grey-300);
+ border-radius: calc(var(--omnibox-height) / 2);
+ display: flex;
+ flex-direction: row;
+ height: var(--omnibox-height);
+ width: 300px;
+ }
+
+ cr-radio-group {
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ width: 500px;
+ }
+
+ .choice {
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+ gap: 16px;
+ }
+
+ .choice-icon {
+ --choice-icon-height: 24px;
+ background-color: green;
+ border-radius: calc(var(--choice-icon-height) / 2);
+ height: var(--choice-icon-height);
+ width: var(--choice-icon-height);
+ }
+
+ .choice-title {
+ font-family: Roboto;
+ font-size: 0.875rem;
+ font-weight: 400;
+ line-height: 20px;
+ }
+
+ cr-radio-button {
+ --cr-radio-button-size: 20px;
+ background-color: var(--google-grey-100);
+ border-radius: 2px;
+ padding: 16px 24px;
+ }
+
+ cr-radio-button:first-of-type {
+ border-top-left-radius: 24px;
+ border-top-right-radius: 24px;
+ }
+
+ cr-radio-button:last-of-type {
+ border-bottom-left-radius: 24px;
+ border-bottom-right-radius: 24px;
+ }
+
+ .product-logo {
+ height: 48px;
+ margin-top: 132px;
+ width: 48px;
+ }
+
+ iron-icon {
+ margin: 0 12px 0 15px;
+ }
+
+ .hint-placeholder {
+ background: var(--google-grey-100);
+ border-radius: 12px;
+ height: 12px;
+ width: 100px;
+ }
+
+ .info-link {
+ color: var(--google-blue-600);
+ }
+
+ cr-dialog {
+ --cr-dialog-body-padding-horizontal: 24px;
+ --cr-dialog-button-container-padding-horizontal: 24px;
+ --cr-dialog-button-container-padding-bottom: 24px;
+ --cr-dialog-button-container-padding-top: 20px;
+ --cr-dialog-title-font-size: 1.375rem;
+ --cr-dialog-title-slot-padding-bottom: 24px;
+ --cr-dialog-title-slot-padding-end: 24px;
+ --cr-dialog-title-slot-padding-start: 24px;
+ --cr-dialog-title-slot-padding-top: 24px;
+ }
+
+ cr-dialog h1 {
+ font-size: 1.375rem;
+ font-weight: 400;
+ line-height: 28px;
+ margin: 0;
+ }
+
+ cr-dialog p {
+ font-family: Roboto;
+ font-size: 0.875rem;
+ font-weight: 400;
+ line-height: 20px;
+ margin: 0 0 24px;
+ }
+
+ .button-container {
+ --button-container-background-color: white;
+ bottom: 0;
+ display: flex;
+ justify-content: flex-end;
+ pointer-events: none;
+ position: fixed;
+ width: 100vw;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ :host {
+ --iron-icon-fill-color: var(--google-blue-300);
+ }
+
+ .hint-placeholder {
+ background-color: var(--google-grey-800);
+ }
+
+ .tangible-sync-style-left-banner {
+ content: url(images/left_illustration_dark.svg);
+ }
+
+ .tangible-sync-style-right-banner {
+ content: url(images/right_illustration_dark.svg);
+ }
+
+ .info-link {
+ color: var(--google-blue-300);
+ }
+
+ cr-radio-button {
+ --cr-radio-button-unchecked-color: var(--google-grey-100);
+ background-color: var(--google-grey-800);
+ }
+
+ .button-container {
+ --button-container-background-color: var(--md-background-color);
+ }
+ }
+
+ /* We need to override the values from tangible-sync-style. The dialog
+ initially sizes itself to height=300 and width=1077 then resizes its
+ height based on the content height.
+ The media query is being called at the first resize (1077 x 300), changing
+ the content size and then making the dialog size itself based on that
+ content. We will handle this case when we implement the small size dialog
+ UI. */
+ @media screen and ((max-width: 780px) or (max-height: 600px)) {
+ .tangible-sync-style .title {
+ font-size: 2.25rem;
+ line-height: 44px;
+ }
+
+ .tangible-sync-style .subtitle {
+ font-size: 1.25rem;
+ line-height: 30px;
+ }
+}
+
+@media screen and (max-width: 920px) {
+ .button-container {
+ background-color: var(--button-container-background-color);
+ border-top: var(--cr-separator-line);
+ pointer-events: unset;
+ }
+
+ #submitButton {
+ margin: 28px;
+ max-width: unset;
+ pointer-events: unset;
+ }
+}
+</style>
+
+<img class="tangible-sync-style-left-banner" alt="">
+<img class="tangible-sync-style-right-banner" alt="">
+<div class="content-container tangible-sync-style">
+ <img class="product-logo" src="images/product-logo.svg"
+ alt="$i18n{productLogoAltText}">
+ <h1 class="title">$i18n{title}</h1>
+ <div class="omnibox">
+ <iron-icon icon="cr:search"></iron-icon>
+ <div class="hint-placeholder"></div>
+ </div>
+ <p class="subtitle">
+ $i18n{subtitle}
+ <a class="info-link" href="" on-click="onLinkClicked_">
+ $i18n{subtitleInfoLink}
+ </a>
+ </p>
+ <cr-radio-group class="choice-list" selected="{{selectedChoice_}}">
+ <template is="dom-repeat" items="[[choiceList_]]">
+ <cr-radio-button class="label-first" name="[[item.prepopulate_id]]">
+ <div class="choice">
+ <div class="choice-icon"></div>
+ <div class="choice-title">[[item.name]]</div>
+ </div>
+ </cr-radio-button>
+ </template>
+ </cr-radio-group>
+</div>
+<div class="button-container">
+ <cr-button class="action-button" id="submitButton" on-click="onSubmitClicked_"
+ disabled="[[isSubmitDisabled_]]">
+ $i18n{buttonText}
+ </cr-button>
+</div>
+
+<cr-dialog id="infoDialog">
+ <div slot="title">
+ <h1>$i18n{infoDialogTitle}</h1>
+ </div>
+ <div slot="body">
+ <p>$i18n{infoDialogFirstParagraph}</p>
+ <p>$i18nRaw{infoDialogSecondParagraph}</p>
+ <p>$i18n{infoDialogThirdParagraph}</p>
+ </div>
+ <div slot="button-container">
+ <cr-button class="action-button" on-click="onInfoDialogButtonClicked_">
+ $i18n{infoDialogButtonText}
+ </cr-button>
+ </div>
+</cr-dialog>
diff --git a/chromium/chrome/browser/resources/search_engine_choice/app.ts b/chromium/chrome/browser/resources/search_engine_choice/app.ts
new file mode 100644
index 00000000000..1cb1a5a3f7b
--- /dev/null
+++ b/chromium/chrome/browser/resources/search_engine_choice/app.ts
@@ -0,0 +1,109 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://search-engine-choice/tangible_sync_style_shared.css.js';
+import 'chrome://resources/cr_components/localized_link/localized_link.js';
+import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js';
+import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js';
+import 'chrome://resources/cr_elements/cr_icons.css.js';
+import 'chrome://resources/cr_elements/icons.html.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+import './strings.m.js';
+
+import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './app.html.js';
+import {SearchEngineChoice, SearchEngineChoiceBrowserProxy} from './browser_proxy.js';
+
+export interface SearchEngineChoiceAppElement {
+ $: {
+ infoDialog: CrDialogElement,
+ submitButton: CrButtonElement,
+ };
+}
+
+export class SearchEngineChoiceAppElement extends PolymerElement {
+ static get is() {
+ return 'search-engine-choice-app';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ /**
+ * We pass the choice list as JSON because it doesn't change
+ * dynamically, so it would be better to have it available as loadtime
+ * data.
+ */
+ choiceList_: {
+ type: Array,
+ value() {
+ return JSON.parse(loadTimeData.getString('choiceList'));
+ },
+ },
+
+ // The choice will always be > 0 when selected for prepopulated engines
+ // and == 0 for a custom search engine.
+ selectedChoice_: {
+ type: Number,
+ value: -1,
+ },
+
+ isSubmitDisabled_: {
+ type: Boolean,
+ computed: 'isSubmitButtonDisabled_(selectedChoice_)',
+ },
+ };
+ }
+
+ private choiceList_: SearchEngineChoice[];
+ private selectedChoice_: number;
+
+ override connectedCallback() {
+ super.connectedCallback();
+
+ afterNextRender(this, () => {
+ // Prefer using `document.body.offsetHeight` instead of
+ // `document.body.scrollHeight` as it returns the correct height of the
+ // page even when the page zoom in Chrome is different than 100%.
+ SearchEngineChoiceBrowserProxy.getInstance().handler.displayDialog(
+ document.body.offsetHeight);
+ });
+ }
+
+ private onLinkClicked_() {
+ this.$.infoDialog.showModal();
+ }
+
+ private isSubmitButtonDisabled_() {
+ return this.selectedChoice_ === -1;
+ }
+
+ private onSubmitClicked_() {
+ SearchEngineChoiceBrowserProxy.getInstance()
+ .handler.handleSearchEngineChoiceSelected(this.selectedChoice_);
+ }
+
+ private onInfoDialogButtonClicked_() {
+ this.$.infoDialog.close();
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'search-engine-choice-app': SearchEngineChoiceAppElement;
+ }
+}
+
+customElements.define(
+ SearchEngineChoiceAppElement.is, SearchEngineChoiceAppElement);
diff --git a/chromium/chrome/browser/resources/search_engine_choice/browser_proxy.ts b/chromium/chrome/browser/resources/search_engine_choice/browser_proxy.ts
new file mode 100644
index 00000000000..6fdbc76bc2b
--- /dev/null
+++ b/chromium/chrome/browser/resources/search_engine_choice/browser_proxy.ts
@@ -0,0 +1,42 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A helper object used by the chrome://search-engine-choice page
+ * to interact with the browser.
+ */
+
+import {PageHandlerFactory, PageHandlerInterface, PageHandlerRemote} from './search_engine_choice.mojom-webui.js';
+
+export interface SearchEngineChoice {
+ prepopulate_id: number;
+ name: string;
+}
+
+export class SearchEngineChoiceBrowserProxy {
+ handler: PageHandlerInterface;
+
+ constructor(handler: PageHandlerRemote) {
+ this.handler = handler;
+ }
+
+ static getInstance(): SearchEngineChoiceBrowserProxy {
+ if (instance) {
+ return instance;
+ }
+
+ const handler = new PageHandlerRemote();
+ const factory = PageHandlerFactory.getRemote();
+ factory.createPageHandler(
+ (handler as PageHandlerRemote).$.bindNewPipeAndPassReceiver());
+
+ return instance = new SearchEngineChoiceBrowserProxy(handler);
+ }
+
+ static setInstance(obj: SearchEngineChoiceBrowserProxy) {
+ instance = obj;
+ }
+}
+
+let instance: SearchEngineChoiceBrowserProxy|null = null;
diff --git a/chromium/chrome/browser/resources/search_engine_choice/search_engine_choice.html b/chromium/chrome/browser/resources/search_engine_choice/search_engine_choice.html
new file mode 100644
index 00000000000..11b6198a681
--- /dev/null
+++ b/chromium/chrome/browser/resources/search_engine_choice/search_engine_choice.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html dir="$i18n{textdirection}" lang="$i18n{language}"
+ $i18n{chromeRefresh2023Attribute}>
+
+<head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
+ <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+ <style>
+ body {
+ margin: 0;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ body {
+ --md-background-color: rgb(41, 42, 45);
+ background-color: var(--md-background-color);
+ }
+ }
+ </style>
+</head>
+
+<body>
+ <search-engine-choice-app></search-engine-choice-app>
+ <script type="module" src="app.js"></script>
+</body>
+
+</html>
diff --git a/chromium/chrome/browser/resources/settings/BUILD.gn b/chromium/chrome/browser/resources/settings/BUILD.gn
index d86fe848ade..6b484992fb3 100644
--- a/chromium/chrome/browser/resources/settings/BUILD.gn
+++ b/chromium/chrome/browser/resources/settings/BUILD.gn
@@ -44,20 +44,22 @@ build_webui("build") {
"images/privacy_guide/horizon_graphic.svg",
"images/privacy_guide/msbb_graphic_dark_v2.svg",
"images/privacy_guide/msbb_graphic_v2.svg",
+ "images/privacy_guide/preload_graphic.svg",
+ "images/privacy_guide/preload_graphic_dark.svg",
"images/privacy_guide/privacy_sandbox_graphic_dark.svg",
"images/privacy_guide/privacy_sandbox_graphic.svg",
"images/privacy_guide/promo_banner_dark.svg",
"images/privacy_guide/promo_banner.svg",
"images/privacy_guide/safe_browsing_graphic_dark_v2.svg",
"images/privacy_guide/safe_browsing_graphic_v2.svg",
+ "images/privacy_guide/search_suggestions_graphic.svg",
+ "images/privacy_guide/search_suggestions_graphic_dark.svg",
"images/privacy_guide/waa_graphic_dark.svg",
"images/privacy_guide/waa_graphic.svg",
"images/privacy_guide/welcome_banner_dark.svg",
"images/privacy_guide/welcome_banner.svg",
"images/privacy_sandbox_banner_v3_dark.svg",
"images/privacy_sandbox_banner_v3.svg",
- "images/privacy_sandbox_floc_banner_dark.svg",
- "images/privacy_sandbox_floc_banner.svg",
"images/safe_browsing_banner_dark.svg",
"images/safe_browsing_banner.svg",
"images/sync_banner_dark.svg",
@@ -77,29 +79,12 @@ build_webui("build") {
"autofill_page/address_remove_confirmation_dialog.ts",
"autofill_page/autofill_page.ts",
"autofill_page/autofill_section.ts",
- "autofill_page/avatar_icon.ts",
"autofill_page/credit_card_edit_dialog.ts",
"autofill_page/credit_card_list_entry.ts",
"autofill_page/iban_edit_dialog.ts",
"autofill_page/iban_list_entry.ts",
- "autofill_page/password_check_edit_disclaimer_dialog.ts",
- "autofill_page/password_check_list_item.ts",
- "autofill_page/password_check.ts",
- "autofill_page/password_edit_dialog.ts",
- "autofill_page/password_list_item.ts",
- "autofill_page/password_preview_item.ts",
- "autofill_page/password_move_multiple_passwords_to_account_dialog.ts",
- "autofill_page/password_remove_confirmation_dialog.ts",
- "autofill_page/password_remove_dialog.ts",
- "autofill_page/password_view.ts",
- "autofill_page/passwords_device_section.ts",
- "autofill_page/passwords_export_dialog.ts",
- "autofill_page/passwords_import_dialog.ts",
- "autofill_page/passwords_list_handler.ts",
- "autofill_page/passwords_section.ts",
"autofill_page/payments_list.ts",
"autofill_page/payments_section.ts",
- "autofill_page/upi_id_list_entry.ts",
"autofill_page/virtual_card_unenroll_dialog.ts",
"basic_page/basic_page.ts",
"clear_browsing_data_dialog/clear_browsing_data_dialog.ts",
@@ -143,7 +128,9 @@ build_webui("build") {
"privacy_page/privacy_guide/privacy_guide_msbb_fragment.ts",
"privacy_page/privacy_guide/privacy_guide_page.ts",
"privacy_page/privacy_guide/privacy_guide_promo.ts",
+ "privacy_page/privacy_guide/privacy_guide_preload_fragment.ts",
"privacy_page/privacy_guide/privacy_guide_safe_browsing_fragment.ts",
+ "privacy_page/privacy_guide/privacy_guide_search_suggestions_fragment.ts",
"privacy_page/privacy_guide/privacy_guide_welcome_fragment.ts",
"privacy_page/privacy_guide/step_indicator.ts",
"privacy_page/privacy_page.ts",
@@ -176,7 +163,11 @@ build_webui("build") {
"safety_check_page/safety_check_safe_browsing_child.ts",
"safety_check_page/safety_check_unused_site_permissions.ts",
"safety_check_page/safety_check_updates_child.ts",
+ "safety_hub/safety_hub_card.ts",
+ "safety_hub/safety_hub_entry_point.ts",
+ "safety_hub/safety_hub_module.ts",
"safety_hub/safety_hub_page.ts",
+ "safety_hub/unused_site_permissions_module.ts",
"search_engines_page/omnibox_extension_entry.ts",
"search_engines_page/search_engine_edit_dialog.ts",
"search_engines_page/search_engine_entry.ts",
@@ -216,6 +207,9 @@ build_webui("build") {
"site_settings/site_entry.ts",
"site_settings/site_list_entry.ts",
"site_settings/site_list.ts",
+ "site_settings/storage_access_static_site_list_entry.ts",
+ "site_settings/storage_access_site_list_entry.ts",
+ "site_settings/storage_access_site_list.ts",
"site_settings/zoom_levels.ts",
]
@@ -274,14 +268,8 @@ build_webui("build") {
"appearance_page/appearance_browser_proxy.ts",
"autofill_page/address_edit_dialog_components.ts",
"autofill_page/autofill_manager_proxy.ts",
- "autofill_page/merge_passwords_store_copies_mixin.ts",
- "autofill_page/user_util_mixin.ts",
- "autofill_page/password_check_mixin.ts",
"autofill_page/password_manager_proxy.ts",
- "autofill_page/password_removal_mixin.ts",
- "autofill_page/password_requestor_mixin.ts",
"autofill_page/payments_manager_proxy.ts",
- "autofill_page/show_password_mixin.ts",
"base_mixin.ts",
"clear_browsing_data_dialog/clear_browsing_data_browser_proxy.ts",
"controls/settings_idle_load.ts",
@@ -296,6 +284,7 @@ build_webui("build") {
"on_startup_page/on_startup_browser_proxy.ts",
"on_startup_page/startup_urls_page_browser_proxy.ts",
"page_visibility.ts",
+ "performance_page/constants.ts",
"performance_page/discard_timer_options.ts",
"performance_page/performance_browser_proxy.ts",
"performance_page/performance_metrics_proxy.ts",
@@ -325,10 +314,6 @@ build_webui("build") {
"tooltip_mixin.ts",
]
- if (is_chromeos) {
- non_web_component_files += [ "autofill_page/blocking_request_manager.ts" ]
- }
-
if (is_chromeos_ash) {
non_web_component_files += [
"people_page/account_manager_browser_proxy.ts",
@@ -358,6 +343,11 @@ build_webui("build") {
non_web_component_files += [ "autofill_page/passkeys_browser_proxy.ts" ]
}
+ if (is_mac) {
+ non_web_component_files +=
+ [ "a11y_page/mac_system_settings_browser_proxy.ts" ]
+ }
+
# -----------------non_web_component_files end -------------------------------
icons_html_files = [
@@ -397,6 +387,7 @@ build_webui("build") {
ts_deps = [
"//chrome/browser/resources/settings_shared:build_ts",
"//third_party/polymer/v3_0:library",
+ "//ui/webui/resources/cr_components/customize_color_scheme_mode:build_ts",
"//ui/webui/resources/cr_components/help_bubble:build_ts",
"//ui/webui/resources/cr_components/localized_link:build_ts",
"//ui/webui/resources/cr_components/managed_dialog:build_ts",
@@ -407,8 +398,10 @@ build_webui("build") {
]
if (!is_chromeos_ash) {
- ts_deps +=
- [ "//ui/webui/resources/cr_components/customize_themes:build_ts" ]
+ ts_deps += [
+ "//ui/webui/resources/cr_components/customize_themes:build_ts",
+ "//ui/webui/resources/cr_components/theme_color_picker:build_ts",
+ ]
} else {
ts_deps += [ "//ash/webui/common/resources:build_ts" ]
}
diff --git a/chromium/chrome/browser/resources/settings_shared/controls/settings_slider.html b/chromium/chrome/browser/resources/settings_shared/controls/settings_slider.html
index 12df2dafa85..67192fb1fed 100644
--- a/chromium/chrome/browser/resources/settings_shared/controls/settings_slider.html
+++ b/chromium/chrome/browser/resources/settings_shared/controls/settings_slider.html
@@ -64,7 +64,8 @@
min="[[min]]" on-dragging-changed="onSliderChanged_"
on-updating-from-key="onSliderChanged_"
aria-roledescription$="[[getRoleDescription_()]]"
- aria-label$="[[labelAria]]">
+ aria-label$="[[labelAria]]"
+ aria-disabled="[[ariaDisabled]]">
</cr-slider>
<!-- aria-hidden because role description on #slider contains min/max. -->
<div id="labels" disabled$="[[disableSlider_]]" aria-hidden="true">
diff --git a/chromium/chrome/browser/resources/settings_shared/controls/settings_slider.ts b/chromium/chrome/browser/resources/settings_shared/controls/settings_slider.ts
index 0d9c1a1f781..3397b18f5d0 100644
--- a/chromium/chrome/browser/resources/settings_shared/controls/settings_slider.ts
+++ b/chromium/chrome/browser/resources/settings_shared/controls/settings_slider.ts
@@ -74,6 +74,9 @@ export class SettingsSliderElement extends SettingsSliderElementBase {
disabled: Boolean,
+ // The value of ariaDisabled should only be "true" or "false".
+ ariaDisabled: String,
+
showMarkers: Boolean,
disableSlider_: {
@@ -111,6 +114,8 @@ export class SettingsSliderElement extends SettingsSliderElementBase {
updateValueInstantly: boolean;
private loaded_: boolean;
+ override ariaDisabled: string;
+
override connectedCallback() {
super.connectedCallback();
diff --git a/chromium/chrome/browser/resources/settings_shared/people_page/sync_browser_proxy.ts b/chromium/chrome/browser/resources/settings_shared/people_page/sync_browser_proxy.ts
index e217d9f5aa5..a1f19c49109 100644
--- a/chromium/chrome/browser/resources/settings_shared/people_page/sync_browser_proxy.ts
+++ b/chromium/chrome/browser/resources/settings_shared/people_page/sync_browser_proxy.ts
@@ -62,32 +62,48 @@ export enum StatusAction {
* PeopleHandler::PushSyncPrefs() for more details.
*/
export interface SyncPrefs {
+ appsManaged: boolean;
appsRegistered: boolean;
appsSynced: boolean;
+ autofillManaged: boolean;
autofillRegistered: boolean;
autofillSynced: boolean;
+ bookmarksManaged: boolean;
bookmarksRegistered: boolean;
bookmarksSynced: boolean;
customPassphraseAllowed: boolean;
encryptAllData: boolean;
+ extensionsManaged: boolean;
extensionsRegistered: boolean;
extensionsSynced: boolean;
passphraseRequired: boolean;
+ passwordsManaged: boolean;
passwordsRegistered: boolean;
passwordsSynced: boolean;
- paymentsIntegrationEnabled: boolean;
+ paymentsManaged: boolean;
+ paymentsRegistered: boolean;
+ paymentsSynced: boolean;
+ preferencesManaged: boolean;
preferencesRegistered: boolean;
preferencesSynced: boolean;
+ readingListManaged: boolean;
readingListRegistered: boolean;
readingListSynced: boolean;
+ savedTabGroupsManaged: boolean;
+ savedTabGroupsRegistered: boolean;
+ savedTabGroupsSynced: boolean;
syncAllDataTypes: boolean;
+ tabsManaged: boolean;
tabsRegistered: boolean;
tabsSynced: boolean;
+ themesManaged: boolean;
themesRegistered: boolean;
themesSynced: boolean;
trustedVaultKeysRequired: boolean;
+ typedUrlsManaged: boolean;
typedUrlsRegistered: boolean;
typedUrlsSynced: boolean;
+ wifiConfigurationsManaged: boolean;
wifiConfigurationsRegistered: boolean;
wifiConfigurationsSynced: boolean;
explicitPassphraseTime?: string;
@@ -104,7 +120,7 @@ export const syncPrefsIndividualDataTypes: string[] = [
'extensionsSynced',
'readingListSynced',
'passwordsSynced',
- 'paymentsIntegrationEnabled',
+ 'paymentsSynced',
'preferencesSynced',
'savedTabGroupsSynced',
'tabsSynced',
diff --git a/chromium/chrome/browser/resources/settings_shared/privacy_page/secure_dns.html b/chromium/chrome/browser/resources/settings_shared/privacy_page/secure_dns.html
index edcd415163b..b6fa9aed9bc 100644
--- a/chromium/chrome/browser/resources/settings_shared/privacy_page/secure_dns.html
+++ b/chromium/chrome/browser/resources/settings_shared/privacy_page/secure_dns.html
@@ -43,6 +43,12 @@
padding: 8px /* md-select left padding */;
}
+ /* Style the link appearing within the privacyPolicy's localized
+ content. */
+ #privacyPolicy a {
+ color: var(--cr-link-color);
+ }
+
#secureDnsInput {
margin-top: 6px;
}
diff --git a/chromium/chrome/browser/resources/side_panel/bookmarks/bookmarks_list.ts b/chromium/chrome/browser/resources/side_panel/bookmarks/bookmarks_list.ts
index ea83c09f557..6c8254e42c5 100644
--- a/chromium/chrome/browser/resources/side_panel/bookmarks/bookmarks_list.ts
+++ b/chromium/chrome/browser/resources/side_panel/bookmarks/bookmarks_list.ts
@@ -137,6 +137,9 @@ export class BookmarksListElement extends PolymerElement {
callbackRouter.priceTrackedForBookmark.addListener(
(product: BookmarkProductInfo) =>
this.onBookmarkPriceTracked(product)),
+ callbackRouter.priceUntrackedForBookmark.addListener(
+ (product: BookmarkProductInfo) =>
+ this.onBookmarkPriceUntracked(product)),
);
}
@@ -441,6 +444,11 @@ export class BookmarksListElement extends PolymerElement {
chrome.metricsPrivate.recordUserAction(
'Commerce.PriceTracking.SidePanel.TrackedProductsShown');
}
+
+ private onBookmarkPriceUntracked(product: BookmarkProductInfo) {
+ this.productInfos_ = this.productInfos_.filter(
+ item => item.bookmarkId !== product.bookmarkId);
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/side_panel/bookmarks/icons.html b/chromium/chrome/browser/resources/side_panel/bookmarks/icons.html
index da518d4e538..29f6d43cd40 100644
--- a/chromium/chrome/browser/resources/side_panel/bookmarks/icons.html
+++ b/chromium/chrome/browser/resources/side_panel/bookmarks/icons.html
@@ -1,33 +1,39 @@
-<iron-iconset-svg name="bookmarks" size="16">
+<iron-iconset-svg name="bookmarks" size="24">
<svg>
<defs>
- <g id="add-tab">
+ <g id="add-tab" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 1.333A6.67 6.67 0 0 0 1.333 8 6.67 6.67 0 0 0 8 14.667 6.67 6.67 0 0 0 14.666 8 6.67 6.67 0 0 0 8 1.333Zm-.667 3.334v2.666H4.666v1.334h2.667v2.666h1.333V8.667h2.667V7.333H8.666V4.667H7.333ZM2.666 8A5.34 5.34 0 0 0 8 13.333 5.34 5.34 0 0 0 13.333 8 5.34 5.34 0 0 0 8 2.667 5.34 5.34 0 0 0 2.666 8Z"></path>
</g>
- <g id="bookmarks-bar" viewBox="0 0 24 24">
+ <g id="bookmarks-bar">
<path d="M2 2v20h20V2H2Zm2 2h16v3H4V4Zm0 16V9h16v11H4Z"></path>
</g>
- <g id="check" viewBox="0 0 24 24">
+ <g id="check">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path>
</g>
- <g id="create-new-folder" viewBox="0 0 24 24">
+ <g id="create-new-folder">
<path fill-rule="evenodd" d="M22 8v10c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2l.01-12c0-1.1.89-2 1.99-2h6l2 2h8c1.1 0 2 .9 2 2Zm-10 4h2v-2h2v2h2v2h-2v2h-2v-2h-2v-2ZM4 8h16v10H4V8Z"></path>
</g>
- <g id="delete" viewBox="0 0 24 24">
+ <g id="delete">
<path d="M15 3v1h5v2h-1v13c0 1.1-.9 2-2 2H7c-1.1 0-2-.9-2-2V6H4V4h5V3h6ZM7 19h10V6H7v13ZM9 8h2v9H9V8Zm6 0h-2v9h2V8Z"></path>
</g>
- <g id="edit" viewBox="0 0 24 24">
+ <g id="edit">
<path d="m20.41 4.94-1.35-1.35c-.78-.78-2.05-.78-2.83 0L13.4 6.41 3 16.82V21h4.18l10.46-10.46 2.77-2.77c.79-.78.79-2.05 0-2.83zm-14 14.12L5 19v-1.36l9.82-9.82 1.41 1.41-9.82 9.83z"></path>
</g>
- <g id="format-list" viewBox="0 0 24 24">
+ <g id="format-list">
<path d="M3 6c0-.83.67-1.5 1.5-1.5S6 5.17 6 6s-.67 1.5-1.5 1.5S3 6.83 3 6Zm0 6c0-.83.67-1.5 1.5-1.5S6 11.17 6 12s-.67 1.5-1.5 1.5S3 12.83 3 12Zm1.5 4.5c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5S6 18.82 6 18s-.67-1.5-1.5-1.5ZM21 19H8v-2h13v2ZM8 13h13v-2H8v2Zm0-6V5h13v2H8Z"></path>
</g>
- <g id="move" viewBox="0 0 24 24">
+ <g id="move">
<path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2Zm0 12H4V8h16v10Zm-8.01-9-1.41 1.41L12.16 12H8v2h4.16l-1.59 1.59L11.99 17 16 13.01 11.99 9Z"></path>
</g>
- <g id="price-tracking">
+ <g id="price-tracking" viewBox="0 0 16 16">
<path d="M12 11.333v-4c0-2.046-1.087-3.76-3-4.213v-.453c0-.554-.447-1-1-1-.554 0-1 .446-1 1v.453c-1.907.453-3 2.16-3 4.213v4H2.666v1.334h10.667v-1.334H12Zm-1.334 0H5.333v-4c0-1.653 1.007-3 2.667-3s2.666 1.347 2.666 3v4ZM8 14.667c.733 0 1.333-.6 1.333-1.334H6.666c0 .734.6 1.334 1.334 1.334Zm6.666-7.334h-1.333c0-1.826-.82-3.46-2.107-4.56l.94-.94c1.527 1.347 2.5 3.307 2.5 5.5Zm-9.893-4.56-.94-.94c-1.527 1.347-2.5 3.307-2.5 5.5h1.333c0-1.826.82-3.46 2.107-4.56Z"></path>
</g>
+ <g id="compact-view">
+ <path d="M9 18h11v-2.675H9V18ZM4 8.675h3V6H4v2.675Zm0 4.675h3v-2.675H4v2.675ZM4 18h3v-2.675H4V18Zm5-4.65h11v-2.675H9v2.675Zm0-4.675h11V6H9v2.675ZM4 20a1.99 1.99 0 0 1-1.425-.575A1.99 1.99 0 0 1 2 18V6c0-.55.192-1.017.575-1.4.4-.4.875-.6 1.425-.6h16c.55 0 1.017.2 1.4.6.4.383.6.85.6 1.4v12c0 .55-.2 1.025-.6 1.425-.383.383-.85.575-1.4.575H4Z"></path>
+ </g>
+ <g id="visual-view">
+ <path d="M4 17c-.283 0-.525-.092-.725-.275A1.035 1.035 0 0 1 3 16c0-.283.092-.517.275-.7.2-.2.442-.3.725-.3s.517.1.7.3c.2.183.3.417.3.7s-.1.525-.3.725A.948.948 0 0 1 4 17Zm0-4c-.283 0-.525-.092-.725-.275A1.035 1.035 0 0 1 3 12c0-.283.092-.517.275-.7.2-.2.442-.3.725-.3s.517.1.7.3c.2.183.3.417.3.7s-.1.525-.3.725A.948.948 0 0 1 4 13Zm0-4c-.283 0-.525-.092-.725-.275A1.035 1.035 0 0 1 3 8c0-.283.092-.517.275-.7.2-.2.442-.3.725-.3s.517.1.7.3c.2.183.3.417.3.7s-.1.525-.3.725A.948.948 0 0 1 4 9Zm3 8v-2h14v2H7Zm0-4v-2h14v2H7Zm0-4V7h14v2H7Z"></path>
+ </g>
</defs>
</svg>
</iron-iconset-svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
index 122dcae9c7d..98a0e2880b9 100644
--- a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
+++ b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
@@ -57,13 +57,16 @@
count="[[bookmark.children.length]]"
title="[[bookmark.title]]"
description="[[description]]"
+ description-meta="[[descriptionMeta]]"
button-aria-label="[[rowAriaLabel]]"
button-aria-description="[[rowAriaDescription]]"
on-click="onRowClicked_"
+ on-auxclick="onRowClicked_"
on-contextmenu="onContextMenu_"
force-hover="[[forceHover]]">
<template is="dom-if" if="[[hasCheckbox]]" restamp>
<cr-checkbox id="checkbox" slot="prefix" hidden="[[!hasCheckbox]]"
+ checked="[[checkboxChecked]]"
on-checked-changed="onCheckboxChange_"
disabled="[[checkboxDisabled]]">
$i18n{checkboxA11yLabel}
diff --git a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.ts b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.ts
index b5a2556d73b..b2cbad80176 100644
--- a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.ts
+++ b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.ts
@@ -35,6 +35,10 @@ export class PowerBookmarkRowElement extends PolymerElement {
static get properties() {
return {
bookmark: Object,
+ checkboxChecked: {
+ type: Boolean,
+ value: false,
+ },
checkboxDisabled: {
type: Boolean,
value: false,
@@ -47,6 +51,10 @@ export class PowerBookmarkRowElement extends PolymerElement {
type: String,
value: '',
},
+ descriptionMeta: {
+ type: String,
+ value: '',
+ },
forceHover: {
type: Boolean,
value: false,
@@ -89,9 +97,11 @@ export class PowerBookmarkRowElement extends PolymerElement {
}
bookmark: chrome.bookmarks.BookmarkTreeNode;
+ checkboxChecked: boolean;
checkboxDisabled: boolean;
compact: boolean;
description: string;
+ descriptionMeta: string;
forceHover: boolean;
hasCheckbox: boolean;
hasInput: boolean;
@@ -167,8 +177,8 @@ export class PowerBookmarkRowElement extends PolymerElement {
private onRowClicked_(event: MouseEvent) {
// Ignore clicks on the row when it has an input, to ensure the row doesn't
// eat input clicks. Also ignore clicks if the row has no associated
- // bookmark.
- if (this.hasInput || !this.bookmark) {
+ // bookmark, or if the event is a right-click.
+ if (this.hasInput || !this.bookmark || event.button === 2) {
return;
}
event.preventDefault();
diff --git a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
index 1b0a97b31f9..f6edc1d5c45 100644
--- a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
+++ b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
@@ -71,19 +71,22 @@ export class PowerBookmarksContextMenuElement extends PolymerElement {
showAt(
event: MouseEvent, bookmarks: chrome.bookmarks.BookmarkTreeNode[],
- priceTracked: boolean, priceTrackingEligible: boolean) {
+ priceTracked: boolean, priceTrackingEligible: boolean,
+ onShown: Function = () => {}) {
this.bookmarks_ = bookmarks;
this.priceTracked_ = priceTracked;
this.priceTrackingEligible_ = priceTrackingEligible;
const target = event.target as HTMLElement;
afterNextRender(this, () => {
this.$.menu.showAt(target);
+ onShown();
});
}
showAtPosition(
event: MouseEvent, bookmarks: chrome.bookmarks.BookmarkTreeNode[],
- priceTracked: boolean, priceTrackingEligible: boolean) {
+ priceTracked: boolean, priceTrackingEligible: boolean,
+ onShown: Function = () => {}) {
this.bookmarks_ = bookmarks;
this.priceTracked_ = priceTracked;
this.priceTrackingEligible_ = priceTrackingEligible;
@@ -98,6 +101,7 @@ export class PowerBookmarksContextMenuElement extends PolymerElement {
minX: minX,
maxX: maxX,
});
+ onShown();
});
}
@@ -134,7 +138,8 @@ export class PowerBookmarksContextMenuElement extends PolymerElement {
},
];
- if (!loadTimeData.getBoolean('incognitoMode')) {
+ if (!loadTimeData.getBoolean('incognitoMode') &&
+ loadTimeData.getBoolean('isIncognitoModeAvailable')) {
menuItems.push({
id: MenuItemId.OPEN_INCOGNITO,
label: bookmarkCount < 2 ?
diff --git a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_drag_manager.ts b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_drag_manager.ts
index 14cac260a19..42ed3180cd4 100644
--- a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_drag_manager.ts
+++ b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_drag_manager.ts
@@ -18,6 +18,8 @@ export enum DropPosition {
}
interface PowerBookmarksDragDelegate extends HTMLElement {
+ getFallbackBookmark(): chrome.bookmarks.BookmarkTreeNode;
+ getFallbackDropTargetElement(): HTMLElement;
onFinishDrop(dropTarget: chrome.bookmarks.BookmarkTreeNode): void;
}
@@ -25,6 +27,8 @@ class DragSession {
private delegate_: PowerBookmarksDragDelegate;
private dragData_: chrome.bookmarkManagerPrivate.DragData;
private lastDragOverElement_: PowerBookmarkRowElement|null = null;
+ private lastDropTargetBookmark_: chrome.bookmarks.BookmarkTreeNode|null =
+ null;
private lastPointerWasTouch_ = false;
constructor(
@@ -44,62 +48,78 @@ class DragSession {
const dragOverElement = e.composedPath().find(target => {
return target instanceof PowerBookmarkRowElement;
}) as PowerBookmarkRowElement;
+
if (!dragOverElement) {
+ // Invalid drag over element. Cancel session.
+ this.cancel();
+ return;
+ } else if (dragOverElement === this.lastDragOverElement_) {
+ // State has not changed, nothing to update.
return;
}
- if (dragOverElement !== this.lastDragOverElement_) {
- this.resetState_();
- }
+ this.resetState_();
const dragOverBookmark = dragOverElement.bookmark;
- const isInvalidDragOverTarget = dragOverBookmark.unmodifiable ||
- dragOverBookmark.url ||
+ let dropTargetBookmark = dragOverBookmark;
+
+ const invalidDropTarget = dropTargetBookmark.unmodifiable ||
+ dropTargetBookmark.url ||
(this.dragData_.elements &&
this.dragData_.elements.some(
- element => element.id === dragOverBookmark.id));
-
- if (isInvalidDragOverTarget) {
- this.lastDragOverElement_ = null;
- return;
+ element => element.id === dropTargetBookmark.id));
+ if (invalidDropTarget) {
+ dropTargetBookmark = this.delegate_.getFallbackBookmark();
}
- if (isInvalidDragOverTarget) {
- this.lastDragOverElement_ = null;
+ const draggedBookmarks = this.dragData_.elements!;
+ let dropTargetIsParent = true;
+ draggedBookmarks.forEach((bookmark: chrome.bookmarks.BookmarkTreeNode) => {
+ if (bookmark.parentId !== dropTargetBookmark.id) {
+ dropTargetIsParent = false;
+ }
+ });
+ if (draggedBookmarks.length === 0 || dropTargetIsParent) {
+ this.cancel();
return;
}
- dragOverElement.setAttribute(DROP_POSITION_ATTR, DropPosition.INTO);
+ if (dragOverBookmark.url) {
+ this.delegate_.getFallbackDropTargetElement().setAttribute(
+ DROP_POSITION_ATTR, DropPosition.INTO);
+ } else {
+ dragOverElement.setAttribute(DROP_POSITION_ATTR, DropPosition.INTO);
+ }
this.lastDragOverElement_ = dragOverElement;
+ this.lastDropTargetBookmark_ = dropTargetBookmark;
}
cancel() {
this.resetState_();
this.lastDragOverElement_ = null;
+ this.lastDropTargetBookmark_ = null;
}
finish() {
- if (!this.lastDragOverElement_) {
+ // TODO(crbug/1444154): Ensure it is possible to drag bookmarks into an
+ // empty active folder.
+ if (!this.lastDropTargetBookmark_) {
return;
}
-
- const dropTargetBookmark = this.lastDragOverElement_.bookmark;
- this.resetState_();
-
- const draggedBookmarks = this.dragData_.elements!;
- if (draggedBookmarks.length === 0) {
- return;
- }
-
chrome.bookmarkManagerPrivate
- .drop(dropTargetBookmark.id, /* index */ undefined)
- .then(() => this.delegate_.onFinishDrop(dropTargetBookmark));
+ .drop(this.lastDropTargetBookmark_.id, /* index */ undefined)
+ .then(() => {
+ this.delegate_.onFinishDrop(this.lastDropTargetBookmark_!);
+ this.cancel();
+ });
}
private resetState_() {
if (this.lastDragOverElement_) {
this.lastDragOverElement_.removeAttribute(DROP_POSITION_ATTR);
}
+ this.delegate_.getFallbackDropTargetElement().removeAttribute(
+ DROP_POSITION_ATTR);
}
static createFromBookmark(
@@ -122,6 +142,7 @@ export class PowerBookmarksDragManager {
}
startObserving() {
+ this.eventTracker_.removeAll();
this.eventTracker_.add(
this.delegate_, 'dragstart',
(e: Event) => this.onDragStart_(e as DragEvent));
diff --git a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
index 37c6ac87f5e..c554b1338ed 100644
--- a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
+++ b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
@@ -16,6 +16,10 @@
}
}
+ :host([drop-position='into']) power-bookmark-row {
+ background: var(--cr-hover-background-color);
+ }
+
cr-icon-button {
--cr-icon-button-icon-size: 16px;
--cr-icon-button-size: 24px;
@@ -44,7 +48,6 @@
:host-context([chrome-refresh-2023]) cr-toolbar-search-field {
margin: var(--sp-body-padding);
- margin-block-end: 0;
}
hr {
@@ -65,12 +68,6 @@
visibility: hidden;
}
- :host-context([chrome-refresh-2023]) .sp-card {
- display: flex;
- flex-direction: column;
- min-height: 0;
- }
-
.bookmarks {
height: 100%;
overflow-y: auto;
@@ -97,15 +94,15 @@
}
sp-empty-state {
- margin-top: 40px;
+ padding-top: 40px;
}
#folderEmptyState {
- margin-bottom: 40px;
+ padding-bottom: 40px;
}
sp-empty-state[guest] {
- margin-top: 66px;
+ padding-top: 66px;
}
sp-heading {
@@ -113,7 +110,29 @@
}
:host-context([chrome-refresh-2023]) sp-heading {
- margin: var(--sp-card-block-padding) var(--sp-card-inline-padding);
+ padding: var(--sp-card-block-padding) var(--sp-card-inline-padding);
+ }
+
+ :host-context([chrome-refresh-2023]) .card-top {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ padding-block-end: 0;
+ }
+
+ :host-context([chrome-refresh-2023]) .card-bottom:not([is-full-card]) {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ padding-block-start: 0;
+ }
+
+ :host-context([chrome-refresh-2023]) .sp-scroller:has([is-full-card]) {
+ border-bottom: solid 1px transparent;
+ padding-bottom: var(--sp-body-padding);
+ }
+
+ :host-context([chrome-refresh-2023])
+ .sp-scroller:has([is-full-card])::-webkit-scrollbar-track {
+ margin-block-end: var(--sp-body-padding);
}
.icon-button-row {
@@ -136,7 +155,82 @@
}
:host-context([chrome-refresh-2023]) .label-row {
- margin: var(--sp-card-gap) var(--sp-body-padding);
+ margin: 4px var(--sp-body-padding) var(--sp-card-gap);
+ }
+
+ .new-folder-icon-container {
+ align-items: center;
+ background-color: var(--google-grey-50);
+ border-radius: 4px;
+ display: flex;
+ height: 100%;
+ justify-content: center;
+ width: 56px;
+ }
+
+ .new-folder-row[compact] > .new-folder-icon-container {
+ background-color: transparent;
+ height: 16px;
+ width: 16px;
+ }
+
+ :host-context([chrome-refresh-2023])
+ .new-folder-row > .new-folder-icon-container {
+ background-color: var(--color-list-item-url-favicon-background);
+ }
+
+ :host-context([chrome-refresh-2023])
+ .new-folder-row[compact] > .new-folder-icon-container {
+ height: 24px;
+ width: 24px;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ .new-folder-icon-container {
+ background-color: var(--google-grey-800);
+ }
+ }
+
+ .new-folder-row {
+ align-items: center;
+ background: none;
+ border: none;
+ color: var(--cr-primary-text-color);
+ display: flex;
+ font-family: inherit;
+ gap: 16px;
+ height: 41px;
+ padding: 6px 16px;
+ width: 100%;
+ }
+
+ .new-folder-row[compact] {
+ gap: 12px;
+ height: 32px;
+ padding: 6px 14px;
+ }
+
+ .new-folder-row[disabled] {
+ color: var(--google-grey-400);
+ }
+
+ :host-context([chrome-refresh-2023]) .new-folder-row {
+ font-size: 12px;
+ font-weight: 500;
+ }
+
+ :host-context([chrome-refresh-2023]) .new-folder-row[compact] {
+ gap: 8px;
+ }
+
+ .new-folder-row:not([disabled]):hover {
+ background-color: var(--cr-hover-background-color);
+ }
+
+ .new-folder-row:not([disabled]):active,
+ .new-folder-row:not([disabled]):focus {
+ background-color: var(--cr-active-background-color);
+ outline: none;
}
cr-toolbar-selection-overlay {
@@ -181,12 +275,11 @@
<div class="column" id="powerBookmarksContainer">
<cr-toolbar-search-field id="searchField" label="$i18n{searchBookmarks}"
clear-label="$i18n{clearSearch}" on-search-changed="onSearchChanged_"
- disabled="[[editing_]]" hidden="[[guestMode_]]"
+ disabled="[[editing_]]" hidden="[[!sectionVisibility_.search]]"
on-blur="onSearchBlurred_">
</cr-toolbar-search-field>
- <div class="label-row" hidden="[[guestMode_]]"
- header-hidden$="[[shouldHideHeader_(
- labels_.*, searchQuery_, shownBookmarks_)]]">
+ <div class="label-row" hidden="[[!sectionVisibility_.filterChips]]"
+ header-hidden$="[[!sectionVisibility_.heading]]" id="filterChips">
<template is="dom-repeat" items="[[labels_]]">
<sp-filter-chip
selected="[[item.active]]"
@@ -198,12 +291,8 @@
</template>
</div>
- <div class="sp-card"
- hidden$="[[shouldHideCard_(labels_.*, searchQuery_, shownBookmarks_,
- guestMode_)]]">
- <sp-heading
- hidden$="[[shouldHideHeader_(
- labels_.*, searchQuery_, shownBookmarks_)]]"
+ <div class="sp-card card-top" hidden$="[[!sectionVisibility_.heading]]">
+ <sp-heading id="heading"
back-button-title="$i18n{tooltipBack}"
back-button-aria-label="[[getBackButtonLabel_(activeFolderPath_.*)]]"
on-back-button-click="onBackClicked_"
@@ -222,23 +311,40 @@
aria-description="[[getSortLabel_(activeSortIndex_)]]"
on-click="onShowSortMenuClicked_">
</cr-icon-button>
- <cr-icon-button slot="buttons" iron-icon="bookmarks:create-new-folder"
- disabled="[[editing_]]"
- title="$i18n{tooltipNewFolder}"
- aria-label="$i18n{createNewFolderA11yLabel}"
- on-click="onAddNewFolderClicked_"></cr-icon-button>
- <cr-icon-button slot="buttons" iron-icon="cr:more-vert"
- disabled="[[!shownBookmarks_.length]]"
- title="$i18n{tooltipMore}"
- aria-label="$i18n{tooltipMore}"
- aria-disabled="[[!shownBookmarks_.length]]"
- on-click="onShowEditMenuClicked_">
+ <cr-icon-button id="viewButton" slot="buttons"
+ iron-icon="[[getViewButtonIcon_(compact_)]]"
+ title="[[getViewButtonTooltip_(compact_)]]"
+ aria-label="[[getViewButtonTooltip_(compact_)]]"
+ on-click="onViewToggleClicked_"></cr-icon-button>
+ <cr-icon-button id="editButton" slot="buttons" class="icon-edit"
+ disabled="[[!hasShownBookmarks_]]"
+ title="$i18n{tooltipEdit}"
+ aria-label="$i18n{editBookmarkListA11yLabel}"
+ aria-pressed="[[editing_]]"
+ on-click="onBulkEditClicked_"></cr-icon-button>
</cr-icon-button>
</sp-heading>
+ </div>
+
+ <template is="dom-if" if="[[!sectionVisibility_.bookmarksList]]">
+ <div class="sp-card card-top card-bottom"
+ hidden="[[!sectionVisibility_.newFolderButton]]">
+ <button class="new-folder-row"
+ disabled="[[editing_]]"
+ compact$="[[compact_]]"
+ aria-label="$i18n{createNewFolderA11yLabel}"
+ on-click="onAddNewFolderClicked_">
+ <div class="new-folder-icon-container">
+ <iron-icon icon="cr:add"></iron-icon>
+ </div>
+ $i18n{tooltipNewFolder}
+ </button>
+ </div>
+ </template>
- <div class="sp-scroller"
- hidden="[[!shouldShowFolderEmptyState_(shownBookmarks_.length,
- hasLoadedData_)]]">
+ <div class="sp-scroller"
+ hidden="[[!sectionVisibility_.folderEmptyState]]">
+ <div class="sp-card card-bottom">
<sp-empty-state id="folderEmptyState"
image-path="./images/bookmarks_empty.svg"
dark-image-path="./images/bookmarks_empty_dark.svg"
@@ -246,79 +352,104 @@
body="$i18n{emptyBodyFolder}">
</sp-empty-state>
</div>
+ </div>
- <div id="bookmarks" class="bookmarks sp-scroller"
- hidden="[[!shownBookmarks_.length]]"
- role="[[getBookmarksListRole_(editing_)]]"
- aria-multiselectable="[[editing_]]" scrollable>
- <iron-list id="shownBookmarksIronList"
- items="[[shownBookmarks_]]" scroll-target="bookmarks">
- <template>
- <power-bookmark-row id="bookmark-[[item.id]]" bookmark="[[item]]"
- description="[[getBookmarkDescription_(
- item, compact_, searchQuery_, item.*)]]"
- compact="[[compact_]]" trailing-icon="cr:more-vert"
- trailing-icon-aria-label="[[getBookmarkMenuA11yLabel_(
- item.url, item.title)]]"
- trailing-icon-tooltip="$i18n{tooltipMore}"
- has-checkbox="[[editing_]]"
- checkbox-disabled="[[!canEdit_(item)]]"
- has-input="[[renamingItem_(item.id, renamingId_)]]"
- on-row-clicked="onRowClicked_"
- on-context-menu="onShowContextMenuClicked_"
- on-trailing-icon-clicked="onShowContextMenuClicked_"
- on-checkbox-change="onRowSelectedChange_"
- on-input-change="onRename_"
- row-aria-label="[[getBookmarkA11yLabel_(
- item.id, item.url, item.title, editing_,
- selectedBookmarks_.*)]]"
- row-aria-description="[[getBookmarkA11yDescription_(item)]]"
- tabindex$="[[tabIndex]]"
- image-urls="[[getBookmarkImageUrls_(
- item, item.*, imageUrls_.*)]]"
- force-hover="[[getBookmarkForceHover_(
- item, contextMenuBookmark_)]]"
- draggable="[[canDrag_(editing_, renamingId_, searchQuery_,
- labels_.*)]]">
- <template is="dom-if" restamp
- if="[[isPriceTracked(item, trackedProductInfos_.*)]]">
- <sp-list-item-badge slot="badges"
- updated="[[showDiscountedPrice_(
- item, trackedProductInfos_.*)]]">
- <iron-icon icon="bookmarks:price-tracking"></iron-icon>
- <div>
- [[getCurrentPrice_(item, trackedProductInfos_.*)]]
- </div>
- <div slot="previous-badge"
- hidden$="[[!showDiscountedPrice_(
- item, trackedProductInfos_.*)]]">
- [[getPreviousPrice_(item, trackedProductInfos_.*)]]
- </div>
- </sp-list-item-badge>
+ <div id="bookmarks" class="bookmarks sp-scroller"
+ hidden="[[!sectionVisibility_.bookmarksList]]"
+ role="[[getBookmarksListRole_(editing_)]]"
+ aria-multiselectable="[[editing_]]" scrollable>
+ <div class="sp-card card-bottom"
+ is-full-card$="[[!sectionVisibility_.heading]]">
+ <button class="new-folder-row"
+ hidden="[[!sectionVisibility_.newFolderButton]]"
+ disabled="[[editing_]]"
+ compact$="[[compact_]]"
+ aria-label="$i18n{createNewFolderA11yLabel}"
+ on-click="onAddNewFolderClicked_">
+ <div class="new-folder-icon-container">
+ <iron-icon icon="cr:add"></iron-icon>
+ </div>
+ $i18n{tooltipNewFolder}
+ </button>
+ <template is="dom-repeat" items="[[displayLists_]]" as="bookmarks">
+ <template is="dom-if" restamp if="[[bookmarks.length]]">
+ <sp-heading hidden="[[!sectionVisibility_.filterHeadings]]"
+ hide-back-button>
+ <h1 slot="heading">
+ [[getFilterHeading_(index, activeFolderPath_.*)]]
+ </h1>
+ </sp-heading>
+ <iron-list id="shownBookmarksIronList[[index]]"
+ items="[[bookmarks]]" scroll-target="bookmarks">
+ <template>
+ <power-bookmark-row id="bookmark-[[item.id]]" bookmark="[[item]]"
+ description="[[getBookmarkDescription_(
+ item, compact_, searchQuery_, item.*)]]"
+ description-meta="[[getBookmarkDescriptionMeta_(
+ item, trackedProductInfos_.*)]]"
+ compact="[[compact_]]" trailing-icon="cr:more-vert"
+ trailing-icon-aria-label="[[getBookmarkMenuA11yLabel_(
+ item.url, item.title)]]"
+ trailing-icon-tooltip="$i18n{tooltipMore}"
+ has-checkbox="[[editing_]]"
+ checkbox-checked="[[bookmarkIsSelected_(
+ item, selectedBookmarks_.*)]]"
+ checkbox-disabled="[[!canEdit_(item)]]"
+ has-input="[[renamingItem_(item.id, renamingId_)]]"
+ on-row-clicked="onRowClicked_"
+ on-context-menu="onShowContextMenuClicked_"
+ on-trailing-icon-clicked="onShowContextMenuClicked_"
+ on-checkbox-change="onRowSelectedChange_"
+ on-input-change="onRename_"
+ row-aria-label="[[getBookmarkA11yLabel_(
+ item.id, item.url, item.title, editing_,
+ selectedBookmarks_.*)]]"
+ row-aria-description="[[getBookmarkA11yDescription_(item)]]"
+ tabindex$="[[tabIndex]]"
+ image-urls="[[getBookmarkImageUrls_(
+ item, item.*, imageUrls_.*)]]"
+ force-hover="[[getBookmarkForceHover_(
+ item, contextMenuBookmark_)]]"
+ draggable="[[canDrag_]]">
+ <template is="dom-if" restamp
+ if="[[isPriceTracked(item, trackedProductInfos_.*)]]">
+ <sp-list-item-badge slot="badges"
+ updated="[[showDiscountedPrice_(
+ item, trackedProductInfos_.*)]]">
+ <iron-icon icon="bookmarks:price-tracking"></iron-icon>
+ <div>
+ [[getCurrentPrice_(item, trackedProductInfos_.*)]]
+ </div>
+ <div slot="previous-badge"
+ hidden$="[[!showDiscountedPrice_(
+ item, trackedProductInfos_.*)]]">
+ [[getPreviousPrice_(item, trackedProductInfos_.*)]]
+ </div>
+ </sp-list-item-badge>
+ </template>
+ </power-bookmark-row>
</template>
- </power-bookmark-row>
+ </iron-list>
</template>
- </iron-list>
+ </template>
</div>
</div>
<sp-empty-state id="topLevelEmptyState"
- hidden="[[!shouldShowTopLevelEmptyState_(
- shownBookmarks_, guestMode_, hasLoadedData_)]]"
+ hidden="[[!sectionVisibility_.topLevelEmptyState]]"
guest$="[[guestMode_]]"
- image-path="[[getEmptyImagePath_(labels_.*, searchQuery_)]]"
- dark-image-path="[[getEmptyImagePathDark_(labels_.*, searchQuery_)]]"
- heading="[[getEmptyTitle_(labels_.*, searchQuery_)]]"
- body="[[getEmptyBody_(labels_.*, searchQuery_)]]">
+ image-path="[[getEmptyImagePath_(hasSomeActiveFilter_)]]"
+ dark-image-path="[[getEmptyImagePathDark_(hasSomeActiveFilter_)]]"
+ heading="[[getEmptyTitle_(hasSomeActiveFilter_)]]"
+ body="[[getEmptyBody_(hasSomeActiveFilter_)]]">
</sp-empty-state>
- <sp-footer
- hidden="[[shouldShowEmptySearchState_(labels_.*, searchQuery_)]]"
- pinned="[[shownBookmarks_.length]]">
+ <sp-footer id="footer"
+ hidden="[[!sectionVisibility_.footer]]"
+ pinned="[[hasShownBookmarks_]]">
<cr-button class="floating-button"
hidden="[[hideAddTabButton_(editing_)]]"
on-click="onAddTabClicked_"
- disabled="[[!canAddCurrentUrl_(shownBookmarks_.*, activeFolderPath_.*,
- currentUrl_)]]">
+ disabled="[[!canAddCurrentUrl_(activeFolderPath_.*, currentUrl_)]]">
<iron-icon slot="prefix-icon" icon="bookmarks:add-tab"></iron-icon>
$i18n{addCurrentTab}
</cr-button>
@@ -328,18 +459,18 @@
selection-label="[[getSelectedDescription_(selectedBookmarks_.*)]]"
on-clear-selected-items="onBulkEditClicked_">
<div class="sp-icon-buttons-row">
- <cr-icon-button iron-icon="bookmarks:delete"
- disabled="[[!selectedBookmarks_.length]]"
+ <cr-icon-button id="deleteButton" iron-icon="bookmarks:delete"
+ disabled="[[!getSelectedBookmarksLength_(selectedBookmarks_.*)]]"
title="$i18n{tooltipDelete}" aria-label="$i18n{tooltipDelete}"
on-click="onDeleteClicked_">
</cr-icon-button>
<cr-icon-button iron-icon="bookmarks:move"
- disabled="[[!selectedBookmarks_.length]]"
+ disabled="[[!getSelectedBookmarksLength_(selectedBookmarks_.*)]]"
title="$i18n{tooltipMove}" aria-label="$i18n{tooltipMove}"
on-click="onMoveClicked_">
</cr-icon-button>
<cr-icon-button iron-icon="cr:more-vert"
- disabled="[[!selectedBookmarks_.length]]"
+ disabled="[[!getSelectedBookmarksLength_(selectedBookmarks_.*)]]"
title="$i18n{tooltipMore}" aria-label="$i18n{tooltipMore}"
on-click="onBulkEditMenuClicked_">
</cr-icon-button>
@@ -359,22 +490,6 @@
</template>
</cr-action-menu>
-<cr-action-menu id="editMenu">
- <button class="dropdown-item" on-click="onBulkEditClicked_">
- <iron-icon icon="bookmarks:edit"></iron-icon>
- $i18n{tooltipEdit}
- </button>
- <hr>
- <button id="visualView" class="dropdown-item" on-click="onVisualViewClicked_">
- <iron-icon icon="cr:check" invisible$="[[compact_]]"></iron-icon>
- $i18n{visualView}
- </button>
- <button class="dropdown-item" on-click="onCompactViewClicked_">
- <iron-icon icon="cr:check" invisible$="[[!compact_]]"></iron-icon>
- $i18n{compactView}
- </button>
-</cr-action-menu>
-
<power-bookmarks-edit-dialog id="editDialog"
on-save="onBookmarksEdited_">
</power-bookmarks-edit-dialog>
diff --git a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
index 2e6031484b0..ca1376b2265 100644
--- a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
+++ b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
@@ -83,16 +83,29 @@ export interface PowerBookmarksListElement {
deletionToast: CrLazyRenderElement<CrToastElement>,
powerBookmarksContainer: HTMLElement,
searchField: CrToolbarSearchFieldElement,
- shownBookmarksIronList: IronListElement,
sortMenu: CrActionMenuElement,
- editMenu: CrActionMenuElement,
editDialog: PowerBookmarksEditDialogElement,
disabledFeatureDialog: CrDialogElement,
topLevelEmptyState: SpEmptyStateElement,
folderEmptyState: SpEmptyStateElement,
+ heading: HTMLElement,
+ footer: HTMLElement,
+ filterChips: HTMLElement,
};
}
+interface SectionVisibility {
+ search?: boolean;
+ filterChips?: boolean;
+ heading?: boolean;
+ filterHeadings?: boolean;
+ folderEmptyState?: boolean;
+ newFolderButton?: boolean;
+ bookmarksList?: boolean;
+ topLevelEmptyState?: boolean;
+ footer?: boolean;
+}
+
export class PowerBookmarksListElement extends PolymerElement {
static get is() {
return 'power-bookmarks-list';
@@ -104,7 +117,7 @@ export class PowerBookmarksListElement extends PolymerElement {
static get properties() {
return {
- shownBookmarks_: {
+ displayLists_: {
type: Array,
value: () => [],
},
@@ -112,6 +125,7 @@ export class PowerBookmarksListElement extends PolymerElement {
compact_: {
type: Boolean,
value: () => loadTimeData.getInteger('viewType') === 0,
+ observer: 'updateListScrollOffset_',
},
activeFolderPath_: {
@@ -166,8 +180,8 @@ export class PowerBookmarksListElement extends PolymerElement {
},
selectedBookmarks_: {
- type: Array,
- value: () => [],
+ type: Object,
+ value: {},
},
guestMode_: {
@@ -197,12 +211,39 @@ export class PowerBookmarksListElement extends PolymerElement {
type: Boolean,
value: false,
},
+
+ hasSomeActiveFilter_: {
+ type: Boolean,
+ value: false,
+ computed: 'computeHasSomeActiveFilter_(searchQuery_, labels_.*)',
+ },
+
+ hasShownBookmarks_: {
+ type: Boolean,
+ value: false,
+ computed: 'computeHasShownBookmarks_(displayLists_.*)',
+ },
+
+ canDrag_: {
+ type: Boolean,
+ value: true,
+ computed:
+ 'computeCanDrag_(editing_, renamingId_, hasSomeActiveFilter_)',
+ observer: 'onCanDragChange_',
+ },
+
+ sectionVisibility_: {
+ type: Object,
+ computed: 'computeSectionVisibility_(hasLoadedData_,' +
+ 'activeFolderPath_.length, hasShownBookmarks_,' +
+ 'labels_.length, hasSomeActiveFilter_)',
+ },
};
}
static get observers() {
return [
- 'updateShownBookmarks_(activeFolderPath_.*, labels_.*, ' +
+ 'updateDisplayLists_(activeFolderPath_.*, labels_.*, ' +
'activeSortIndex_, searchQuery_)',
];
}
@@ -212,7 +253,7 @@ export class PowerBookmarksListElement extends PolymerElement {
private shoppingListApi_: ShoppingListApiProxy =
ShoppingListApiProxyImpl.getInstance();
private shoppingListenerIds_: number[] = [];
- private shownBookmarks_: chrome.bookmarks.BookmarkTreeNode[];
+ private displayLists_: chrome.bookmarks.BookmarkTreeNode[][];
private trackedProductInfos_ = new Map<string, BookmarkProductInfo>();
private availableProductInfos_ = new Map<string, BookmarkProductInfo>();
private bookmarksService_: PowerBookmarksService =
@@ -229,7 +270,7 @@ export class PowerBookmarksListElement extends PolymerElement {
private searchQuery_: string|undefined;
private currentUrl_: string|undefined;
private editing_: boolean;
- private selectedBookmarks_: chrome.bookmarks.BookmarkTreeNode[];
+ private selectedBookmarks_: {[key: string]: boolean};
private guestMode_: boolean;
private renamingId_: string;
private deletionDescription_: string;
@@ -237,6 +278,10 @@ export class PowerBookmarksListElement extends PolymerElement {
private hasScrollbars_: boolean;
private contextMenuBookmark_: chrome.bookmarks.BookmarkTreeNode|undefined;
private hasLoadedData_: boolean;
+ private canDrag_: boolean;
+ private hasSomeActiveFilter_: boolean;
+ private hasShownBookmarks_: boolean;
+ private sectionVisibility_: SectionVisibility = {};
constructor() {
super();
@@ -277,6 +322,8 @@ export class PowerBookmarksListElement extends PolymerElement {
this.shownBookmarksResizeObserver_.observe(this.$.bookmarks);
}
+ this.updateListScrollOffset_();
+
this.bookmarksDragManager_.startObserving();
this.recordMetricsOnConnected_();
}
@@ -303,31 +350,29 @@ export class PowerBookmarksListElement extends PolymerElement {
}
onBookmarksLoaded() {
- this.updateShownBookmarks_();
+ this.updateDisplayLists_();
this.hasLoadedData_ = true;
}
onBookmarkChanged(id: string, changedInfo: chrome.bookmarks.ChangeInfo) {
- const visibleIndex = this.visibleIndex_(id);
- if (visibleIndex > -1) {
- Object.keys(changedInfo).forEach(key => {
- this.notifyPath(`shownBookmarks_.${visibleIndex}.${key}`);
- });
+ const bookmark = this.bookmarksService_.findBookmarkWithId(id)!;
+ if (this.hasSomeActiveFilter_ &&
+ (this.bookmarkShouldShow_(bookmark) ||
+ this.bookmarkIsShowing_(bookmark))) {
+ this.updateDisplayLists_();
}
+ Object.keys(changedInfo).forEach(key => {
+ this.notifyPathIfVisible_(id, key);
+ });
this.updateShoppingData_();
}
onBookmarkCreated(
bookmark: chrome.bookmarks.BookmarkTreeNode,
parent: chrome.bookmarks.BookmarkTreeNode) {
- const bookmarksToShow = this.getBookmarksToShow_(bookmark, parent);
- if (bookmarksToShow.length > 0) {
- this.shownBookmarks_.unshift(...bookmarksToShow);
- this.bookmarksService_.sortBookmarks(
- this.shownBookmarks_, this.activeSortIndex_);
- this.shownBookmarks_ = this.shownBookmarks_.slice();
- const bookmarkIndex = this.shownBookmarks_.indexOf(bookmarksToShow[0]);
- this.$.shownBookmarksIronList.scrollToIndex(bookmarkIndex);
+ if (this.bookmarkShouldShow_(bookmark)) {
+ const scrollTop = this.$.bookmarks.scrollTop;
+ this.updateDisplayLists_();
if (bookmark.url) {
getAnnouncerInstance().announce(loadTimeData.getStringF(
'bookmarkCreated', getBookmarkName(bookmark)));
@@ -335,62 +380,70 @@ export class PowerBookmarksListElement extends PolymerElement {
getAnnouncerInstance().announce(loadTimeData.getStringF(
'bookmarkFolderCreated', getBookmarkName(bookmark)));
}
+ for (let i = 0; i < this.displayLists_.length; i++) {
+ const indexInList = this.displayLists_[i].indexOf(bookmark);
+ if (indexInList > -1) {
+ const listElement = this.getDisplayListElement_(i);
+ if (listElement &&
+ (indexInList < listElement.firstVisibleIndex ||
+ indexInList > listElement.lastVisibleIndex)) {
+ listElement.scrollToIndex(indexInList);
+ } else {
+ afterNextRender(this, () => {
+ this.$.bookmarks.scrollTop = scrollTop;
+ });
+ }
+ break;
+ }
+ }
}
this.updateShoppingData_();
+ this.notifyPathIfVisible_(parent.id, 'children');
}
onBookmarkMoved(
bookmark: chrome.bookmarks.BookmarkTreeNode,
oldParent: chrome.bookmarks.BookmarkTreeNode,
newParent: chrome.bookmarks.BookmarkTreeNode) {
- const bookmarksToShow = this.getBookmarksToShow_(bookmark, newParent);
- const shouldUpdateUIAdded = bookmarksToShow.length > 0;
- const shouldUpdateUIRemoved = this.visibleParent_(oldParent);
- const shouldUpdateUIReordered =
- shouldUpdateUIAdded && shouldUpdateUIRemoved;
-
- if (shouldUpdateUIReordered) {
+ const shouldShow = this.bookmarkShouldShow_(bookmark);
+ const isShowing = this.bookmarkIsShowing_(bookmark);
+ if (oldParent === newParent && shouldShow) {
getAnnouncerInstance().announce(loadTimeData.getStringF(
'bookmarkReordered', getBookmarkName(bookmark)));
- } else if (shouldUpdateUIAdded) {
- const scrollIndex = this.$.shownBookmarksIronList.firstVisibleIndex;
- this.shownBookmarks_.unshift(...bookmarksToShow);
- this.bookmarksService_.sortBookmarks(
- this.shownBookmarks_, this.activeSortIndex_);
- this.shownBookmarks_ = this.shownBookmarks_.slice();
+ } else if (
+ (shouldShow !== isShowing) ||
+ (shouldShow && this.hasSomeActiveFilter_)) {
+ const scrollTop = this.$.bookmarks.scrollTop;
+ this.updateDisplayLists_();
getAnnouncerInstance().announce(loadTimeData.getStringF(
'bookmarkMoved', getBookmarkName(bookmark),
getBookmarkName(newParent)));
- this.$.shownBookmarksIronList.scrollToIndex(scrollIndex);
- } else if (shouldUpdateUIRemoved) {
- const scrollIndex = this.$.shownBookmarksIronList.firstVisibleIndex;
- this.splice('shownBookmarks_', this.visibleIndex_(bookmark.id), 1);
- getAnnouncerInstance().announce(loadTimeData.getStringF(
- 'bookmarkMoved', getBookmarkName(bookmark),
- getBookmarkName(newParent)));
- // If the new parent folder is visible, notify to ensure its displayed
- // child count is updated.
- const visibleIndex = this.visibleIndex_(newParent.id);
- if (visibleIndex > -1) {
- this.notifyPath(`shownBookmarks_.${visibleIndex}.children`);
- }
- this.$.shownBookmarksIronList.scrollToIndex(scrollIndex);
+ afterNextRender(this, () => {
+ this.$.bookmarks.scrollTop = scrollTop;
+ });
}
+ // If the new parent folder is visible, notify to ensure its displayed
+ // child count is updated.
+ this.notifyPathIfVisible_(newParent.id, 'children');
}
onBookmarkRemoved(bookmark: chrome.bookmarks.BookmarkTreeNode) {
- const scrollIndex = this.$.shownBookmarksIronList.firstVisibleIndex;
- const visibleIndex = this.visibleIndex_(bookmark.id);
- if (visibleIndex > -1) {
- this.splice('shownBookmarks_', visibleIndex, 1);
+ const scrollTop = this.$.bookmarks.scrollTop;
+ const isShown = this.bookmarkIsShowing_(bookmark);
+ if (isShown) {
+ this.removeNodeFromDisplayLists_(bookmark.id);
getAnnouncerInstance().announce(loadTimeData.getStringF(
'bookmarkDeleted', getBookmarkName(bookmark)));
+ afterNextRender(this, () => {
+ this.$.bookmarks.scrollTop = scrollTop;
+ });
}
this.set(`trackedProductInfos_.${bookmark.id}`, null);
this.availableProductInfos_.delete(bookmark.id);
- if (visibleIndex > -1) {
- this.$.shownBookmarksIronList.scrollToIndex(scrollIndex);
- }
+
+ // If the parent folder is visible, notify to ensure its displayed
+ // child count is updated.
+ this.notifyPathIfVisible_(bookmark.parentId!, 'children');
}
isPriceTracked(bookmark: chrome.bookmarks.BookmarkTreeNode): boolean {
@@ -407,6 +460,16 @@ export class PowerBookmarksListElement extends PolymerElement {
}
/** PowerBookmarksDragDelegate */
+ getFallbackBookmark(): chrome.bookmarks.BookmarkTreeNode {
+ return this.getParentFolder_();
+ }
+
+ /** PowerBookmarksDragDelegate */
+ getFallbackDropTargetElement(): HTMLElement {
+ return this;
+ }
+
+ /** PowerBookmarksDragDelegate */
onFinishDrop(dropTarget: chrome.bookmarks.BookmarkTreeNode): void {
this.focusBookmark_(dropTarget.id);
@@ -419,6 +482,22 @@ export class PowerBookmarksListElement extends PolymerElement {
}, {once: true});
}
+ getBookmarkDescriptionForTests(bookmark: chrome.bookmarks.BookmarkTreeNode) {
+ return this.getBookmarkDescription_(bookmark);
+ }
+
+ clickBookmarkRowForTests(bookmark: chrome.bookmarks.BookmarkTreeNode) {
+ const event = new CustomEvent('row-clicked', {
+ bubbles: true,
+ composed: true,
+ detail: {
+ bookmark: bookmark,
+ event: new MouseEvent('row-clicked'),
+ },
+ });
+ this.onRowClicked_(event);
+ }
+
setRenamingIdForTests(id: string) {
const event = new CustomEvent('rename', {
bubbles: true,
@@ -430,9 +509,18 @@ export class PowerBookmarksListElement extends PolymerElement {
this.setRenamingId_(event);
}
- private canDrag_() {
- return !this.editing_ && !this.renamingId_ && !this.searchQuery_ &&
- !this.hasActiveLabels_();
+ private notifyPathIfVisible_(id: string, key: string) {
+ for (let i = 0; i < this.displayLists_.length; i++) {
+ const listIndex = this.displayLists_[i].findIndex(b => b.id === id);
+ if (listIndex > -1) {
+ this.notifyPath(`displayLists_.${i}.${listIndex}.${key}`);
+ return;
+ }
+ }
+ }
+
+ private computeCanDrag_(): boolean {
+ return !this.editing_ && !this.renamingId_ && !this.hasSomeActiveFilter_;
}
private focusBookmark_(id: string) {
@@ -476,12 +564,20 @@ export class PowerBookmarksListElement extends PolymerElement {
}
}
- /**
- * Returns the index of the given node id in the currently shown bookmarks,
- * or -1 if not shown.
- */
- private visibleIndex_(nodeId: string): number {
- return this.shownBookmarks_.findIndex(b => b.id === nodeId);
+ private bookmarkIsShowing_(bookmark: chrome.bookmarks.BookmarkTreeNode):
+ boolean {
+ return this.displayLists_.some(list => list.includes(bookmark));
+ }
+
+ private removeNodeFromDisplayLists_(nodeId: string) {
+ for (let listIndex = 0; listIndex < this.displayLists_.length;
+ listIndex++) {
+ const itemIndex =
+ this.displayLists_[listIndex].findIndex(b => b.id === nodeId);
+ if (itemIndex > -1) {
+ this.splice(`displayLists_.${listIndex}`, itemIndex, 1);
+ }
+ }
}
/**
@@ -491,19 +587,18 @@ export class PowerBookmarksListElement extends PolymerElement {
private visibleParent_(parent: chrome.bookmarks.BookmarkTreeNode): boolean {
const activeFolder = this.getActiveFolder_();
return (!activeFolder && parent.parentId === '0' &&
- this.visibleIndex_(parent.id) === -1) ||
+ !this.bookmarkIsShowing_(parent)) ||
parent === activeFolder;
}
- private getBookmarksToShow_(
- bookmark: chrome.bookmarks.BookmarkTreeNode,
- parent: chrome.bookmarks.BookmarkTreeNode):
- chrome.bookmarks.BookmarkTreeNode[] {
- if (!this.visibleParent_(parent)) {
- return [];
+ private bookmarkShouldShow_(bookmark: chrome.bookmarks.BookmarkTreeNode):
+ boolean {
+ if (this.hasSomeActiveFilter_) {
+ return this.bookmarksService_.bookmarkMatchesSearchQueryAndLabels(
+ bookmark, this.labels_, this.searchQuery_);
}
- return this.bookmarksService_.applySearchQueryAndLabels(
- this.labels_, this.searchQuery_, [bookmark]);
+ return this.visibleParent_(
+ this.bookmarksService_.findBookmarkWithId(bookmark.parentId)!);
}
private getActiveFolder_(): chrome.bookmarks.BookmarkTreeNode|undefined {
@@ -554,6 +649,29 @@ export class PowerBookmarksListElement extends PolymerElement {
}
}
+ private getBookmarkDescriptionMeta_(bookmark:
+ chrome.bookmarks.BookmarkTreeNode) {
+ // If there is a price available for the product and it isn't being
+ // tracked, return the current price which will be added to the description
+ // meta section.
+ const productInfo = this.availableProductInfos_.get(bookmark.id);
+ if (productInfo && productInfo.info.currentPrice &&
+ !this.isPriceTracked(bookmark)) {
+ return productInfo.info.currentPrice;
+ }
+
+ return '';
+ }
+
+ private getViewButtonIcon_() {
+ return this.compact_ ? 'bookmarks:compact-view' : 'bookmarks:visual-view';
+ }
+
+ private getViewButtonTooltip_() {
+ return this.compact_ ? loadTimeData.getString('compactView') :
+ loadTimeData.getString('visualView');
+ }
+
private getBookmarkMenuA11yLabel_(url: string, title: string): string {
if (url) {
return loadTimeData.getStringF('bookmarkMenuLabel', title);
@@ -565,7 +683,7 @@ export class PowerBookmarksListElement extends PolymerElement {
private getBookmarkA11yLabel_(id: string, url: string, title: string):
string {
if (this.editing_) {
- if (this.selectedBookmarks_.findIndex(b => b.id === id) > -1) {
+ if (this.get(`selectedBookmarks_.${id}`)) {
if (url) {
return loadTimeData.getStringF('deselectBookmarkLabel', title);
}
@@ -663,13 +781,47 @@ export class PowerBookmarksListElement extends PolymerElement {
}
/**
- * Update the list of bookmarks and folders displayed to the user.
+ * Update the lists of bookmarks and folders displayed to the user.
*/
- private updateShownBookmarks_() {
- this.shownBookmarks_ = this.bookmarksService_.filterBookmarks(
- this.getActiveFolder_(), this.activeSortIndex_, this.searchQuery_,
- this.labels_);
- this.bookmarksService_.refreshDataForBookmarks(this.shownBookmarks_);
+ private updateDisplayLists_() {
+ const activeFolder = this.getActiveFolder_();
+ const primaryList = this.bookmarksService_.filterBookmarks(
+ activeFolder, this.activeSortIndex_, this.searchQuery_, this.labels_);
+ this.displayLists_ = [primaryList];
+ if (this.hasSomeActiveFilter_ && !!activeFolder) {
+ const secondaryList = this.bookmarksService_.filterBookmarks(
+ undefined, this.activeSortIndex_, this.searchQuery_, this.labels_,
+ activeFolder);
+ this.displayLists_.push(secondaryList);
+ }
+ this.displayLists_.forEach(
+ list => this.bookmarksService_.refreshDataForBookmarks(list));
+ this.updateListScrollOffset_();
+ }
+
+ private updateListScrollOffset_() {
+ // Set scrollOffset so the iron-list scrolling accounts for the space the
+ // other scrolling UI elements take.
+ afterNextRender(this, () => {
+ const primaryList = this.getDisplayListElement_(0);
+ const secondaryList = this.getDisplayListElement_(1);
+ const bookmarksOffsetTop = this.$.bookmarks.offsetTop;
+ if (primaryList) {
+ primaryList.scrollOffset = primaryList.offsetTop - bookmarksOffsetTop;
+ }
+ if (secondaryList) {
+ secondaryList.scrollOffset =
+ secondaryList.offsetTop - bookmarksOffsetTop;
+ }
+ });
+ }
+
+ private onCanDragChange_() {
+ if (this.canDrag_) {
+ this.bookmarksDragManager_.startObserving();
+ } else {
+ this.bookmarksDragManager_.stopObserving();
+ }
}
private recordMetricsOnConnected_() {
@@ -708,6 +860,11 @@ export class PowerBookmarksListElement extends PolymerElement {
sortType.sortOrder;
}
+ private bookmarkIsSelected_(bookmark: chrome.bookmarks.BookmarkTreeNode):
+ boolean {
+ return this.get(`selectedBookmarks_.${bookmark.id.toString()}`);
+ }
+
/**
* Invoked when the user clicks a power bookmarks row. This will either
* display children in the case of a folder row, or open the URL in the case
@@ -724,12 +881,17 @@ export class PowerBookmarksListElement extends PolymerElement {
// Cancel search when changing active folder.
this.$.searchField.setValue('');
afterNextRender(this, () => {
- this.$.shownBookmarksIronList.focusItem(0);
+ for (let i = 0; i < this.displayLists_.length; i++) {
+ if (this.displayLists_[i].length > 0) {
+ this.getDisplayListElement_(i)!.focusItem(0);
+ break;
+ }
+ }
});
} else {
this.bookmarksApi_.openBookmark(
event.detail.bookmark.id, this.activeFolderPath_.length, {
- middleButton: false,
+ middleButton: event.detail.event.button === 1,
altKey: event.detail.event.altKey,
ctrlKey: event.detail.event.ctrlKey,
metaKey: event.detail.event.metaKey,
@@ -741,7 +903,9 @@ export class PowerBookmarksListElement extends PolymerElement {
// Workaround for this issue, causing unexpected list scrolling when
// refocusing the list after changing tabs:
// https://github.com/PolymerElements/iron-list/issues/270
- (event.target as HTMLElement).blur();
+ if (event.target) {
+ (event.target as HTMLElement).blur();
+ }
}
private onRowSelectedChange_(
@@ -749,13 +913,13 @@ export class PowerBookmarksListElement extends PolymerElement {
{bookmark: chrome.bookmarks.BookmarkTreeNode, checked: boolean}>) {
event.preventDefault();
event.stopPropagation();
- if (event.detail.checked) {
- this.unshift('selectedBookmarks_', event.detail.bookmark);
- } else {
- this.splice(
- 'selectedBookmarks_',
- this.selectedBookmarks_.findIndex(b => b === event.detail.bookmark),
- 1);
+ const isSelected = this.bookmarkIsSelected_(event.detail.bookmark);
+ if (event.detail.checked && !isSelected) {
+ this.set(
+ `selectedBookmarks_.${event.detail.bookmark.id.toString()}`, true);
+ } else if (!event.detail.checked && isSelected) {
+ this.set(
+ `selectedBookmarks_.${event.detail.bookmark.id.toString()}`, false);
}
}
@@ -781,7 +945,7 @@ export class PowerBookmarksListElement extends PolymerElement {
this.bookmarksApi_.editBookmarks(
event.detail.bookmarks.map(bookmark => bookmark.id), event.detail.name,
event.detail.url, parentId);
- this.selectedBookmarks_ = [];
+ this.selectedBookmarks_ = {};
this.editing_ = false;
}
@@ -799,41 +963,44 @@ export class PowerBookmarksListElement extends PolymerElement {
this.renamingId_ = '';
}
- private hasActiveLabels_(): boolean {
- for (const label of this.labels_) {
- if (label.active) {
- return true;
- }
- }
- return false;
- }
-
- private shouldShowEmptySearchState_(): boolean {
- return this.hasActiveLabels_() || !!this.searchQuery_;
+ private getDisplayListElement_(index: number): IronListElement|null {
+ return this.shadowRoot!.querySelector<IronListElement>(
+ `#shownBookmarksIronList${index}`);
}
- private shouldShowTopLevelEmptyState_(): boolean {
- return this.guestMode_ ||
- (this.hasLoadedData_ && this.shownBookmarks_.length === 0 &&
- (!!this.searchQuery_ || this.activeFolderPath_.length === 0));
+ private notifyBookmarksListResize_() {
+ for (let i = 0; i < this.displayLists_.length; i++) {
+ if (this.displayLists_[i].length > 0) {
+ this.getDisplayListElement_(i)!.notifyResize();
+ }
+ }
}
- private shouldShowFolderEmptyState_(): boolean {
- return this.hasLoadedData_ && this.shownBookmarks_.length === 0;
+ private getFilterHeading_(index: number) {
+ if (index === 0) {
+ return loadTimeData.getStringF(
+ 'primaryFilterHeading', this.getActiveFolderLabel_());
+ }
+ return loadTimeData.getString('secondaryFilterHeading');
}
- private shouldHideCard_(): boolean {
- return this.guestMode_ ||
- (this.shouldHideHeader_() && this.shownBookmarks_.length === 0);
+ private getSelectedDescription_() {
+ return loadTimeData.getStringF(
+ 'selectedBookmarkCount', this.getSelectedBookmarksLength_());
}
- private shouldHideHeader_(): boolean {
- return this.hasActiveLabels_() || !!this.searchQuery_;
+ private getSelectedBookmarksList_(): chrome.bookmarks.BookmarkTreeNode[] {
+ const selectedEntries = Object.entries(this.selectedBookmarks_)
+ .filter(([_id, selected]) => selected);
+ const selectedIds = selectedEntries.map(([id, _selected]) => id);
+ return selectedIds.map(
+ (id) => this.bookmarksService_.findBookmarkWithId(id)!);
}
- private getSelectedDescription_() {
- return loadTimeData.getStringF(
- 'selectedBookmarkCount', this.selectedBookmarks_.length);
+ private getSelectedBookmarksLength_(): number {
+ return Object.values(this.selectedBookmarks_)
+ .filter((selected) => selected)
+ .length;
}
/**
@@ -875,6 +1042,10 @@ export class PowerBookmarksListElement extends PolymerElement {
SearchAction.COUNT);
}
+ private onContextMenuShown_(bookmark: chrome.bookmarks.BookmarkTreeNode) {
+ this.contextMenuBookmark_ = bookmark;
+ }
+
private onShowContextMenuClicked_(
event: CustomEvent<
{bookmark: chrome.bookmarks.BookmarkTreeNode, event: MouseEvent}>) {
@@ -883,15 +1054,15 @@ export class PowerBookmarksListElement extends PolymerElement {
const priceTracked = this.isPriceTracked(event.detail.bookmark);
const priceTrackingEligible =
this.isPriceTrackingEligible_(event.detail.bookmark);
- this.contextMenuBookmark_ = event.detail.bookmark;
+ const bookmark = event.detail.bookmark;
if (event.detail.event.button === 0) {
this.$.contextMenu.showAt(
- event.detail.event, [this.contextMenuBookmark_], priceTracked,
- priceTrackingEligible);
+ event.detail.event, [bookmark], priceTracked, priceTrackingEligible,
+ this.onContextMenuShown_.bind(this, bookmark));
} else {
this.$.contextMenu.showAtPosition(
- event.detail.event, [this.contextMenuBookmark_], priceTracked,
- priceTrackingEligible);
+ event.detail.event, [bookmark], priceTracked, priceTrackingEligible,
+ this.onContextMenuShown_.bind(this, bookmark));
}
}
@@ -907,12 +1078,6 @@ export class PowerBookmarksListElement extends PolymerElement {
this.$.sortMenu.showAt(event.target as HTMLElement);
}
- private onShowEditMenuClicked_(event: MouseEvent) {
- event.preventDefault();
- event.stopPropagation();
- this.$.editMenu.showAt(event.target as HTMLElement);
- }
-
private onAddNewFolderClicked_(event: MouseEvent) {
event.preventDefault();
event.stopPropagation();
@@ -932,25 +1097,25 @@ export class PowerBookmarksListElement extends PolymerElement {
private onBulkEditClicked_(event: MouseEvent) {
event.preventDefault();
event.stopPropagation();
- this.$.editMenu.close();
this.editing_ = !this.editing_;
if (!this.editing_) {
- this.selectedBookmarks_ = [];
+ this.selectedBookmarks_ = {};
}
}
private onDeleteClicked_(event: MouseEvent) {
event.preventDefault();
event.stopPropagation();
- if (editingDisabledByPolicy(this.selectedBookmarks_)) {
+ const selectedBookmarksList = this.getSelectedBookmarksList_();
+ if (editingDisabledByPolicy(selectedBookmarksList)) {
this.showDisabledFeatureDialog_();
return;
}
this.bookmarksApi_
- .deleteBookmarks(this.selectedBookmarks_.map(bookmark => bookmark.id))
+ .deleteBookmarks(selectedBookmarksList.map((bookmark) => bookmark.id))
.then(() => {
- this.showDeletionToastWithCount_(this.selectedBookmarks_.length);
- this.selectedBookmarks_ = [];
+ this.showDeletionToastWithCount_(selectedBookmarksList.length);
+ this.selectedBookmarks_ = {};
this.editing_ = false;
});
}
@@ -972,7 +1137,7 @@ export class PowerBookmarksListElement extends PolymerElement {
event.preventDefault();
event.stopPropagation();
this.showDeletionToastWithCount_(event.detail.bookmarks.length);
- this.selectedBookmarks_ = [];
+ this.selectedBookmarks_ = {};
this.editing_ = false;
}
@@ -1010,11 +1175,12 @@ export class PowerBookmarksListElement extends PolymerElement {
private onMoveClicked_(event: MouseEvent) {
event.preventDefault();
event.stopPropagation();
- if (editingDisabledByPolicy(this.selectedBookmarks_)) {
+ const selectedBookmarksList = this.getSelectedBookmarksList_();
+ if (editingDisabledByPolicy(selectedBookmarksList)) {
this.showDisabledFeatureDialog_();
return;
}
- this.showEditDialog_(this.selectedBookmarks_, true);
+ this.showEditDialog_(selectedBookmarksList, true);
}
private showEditDialog_(
@@ -1028,7 +1194,7 @@ export class PowerBookmarksListElement extends PolymerElement {
event.preventDefault();
event.stopPropagation();
this.$.contextMenu.showAt(
- event, this.selectedBookmarks_.slice(), false, false);
+ event, this.getSelectedBookmarksList_(), false, false);
}
private onSortTypeClicked_(event: DomRepeatEvent<SortOption>) {
@@ -1042,28 +1208,15 @@ export class PowerBookmarksListElement extends PolymerElement {
SortOrder.kCount);
}
- private onVisualViewClicked_(event: MouseEvent) {
- event.preventDefault();
- event.stopPropagation();
- this.$.editMenu.close();
- this.compact_ = false;
- this.$.shownBookmarksIronList.notifyResize();
- this.bookmarksApi_.setViewType(ViewType.kExpanded);
- chrome.metricsPrivate.recordEnumerationValue(
- 'PowerBookmarks.SidePanel.ViewTypeShown', ViewType.kExpanded,
- ViewType.kCount);
- }
-
- private onCompactViewClicked_(event: MouseEvent) {
+ private onViewToggleClicked_(event: MouseEvent) {
event.preventDefault();
event.stopPropagation();
- this.$.editMenu.close();
- this.compact_ = true;
- this.$.shownBookmarksIronList.notifyResize();
- this.bookmarksApi_.setViewType(ViewType.kCompact);
+ this.compact_ = !this.compact_;
+ this.notifyBookmarksListResize_();
+ const viewType = this.compact_ ? ViewType.kCompact : ViewType.kExpanded;
+ this.bookmarksApi_.setViewType(viewType);
chrome.metricsPrivate.recordEnumerationValue(
- 'PowerBookmarks.SidePanel.ViewTypeShown', ViewType.kCompact,
- ViewType.kCount);
+ 'PowerBookmarks.SidePanel.ViewTypeShown', viewType, ViewType.kCount);
}
private onAddTabClicked_() {
@@ -1087,7 +1240,7 @@ export class PowerBookmarksListElement extends PolymerElement {
private getEmptyTitle_(): string {
if (this.guestMode_) {
return loadTimeData.getString('emptyTitleGuest');
- } else if (this.shouldShowEmptySearchState_()) {
+ } else if (this.hasSomeActiveFilter_) {
return loadTimeData.getString('emptyTitleSearch');
} else {
return loadTimeData.getString('emptyTitle');
@@ -1097,7 +1250,7 @@ export class PowerBookmarksListElement extends PolymerElement {
private getEmptyBody_(): string {
if (this.guestMode_) {
return loadTimeData.getString('emptyBodyGuest');
- } else if (this.shouldShowEmptySearchState_()) {
+ } else if (this.hasSomeActiveFilter_) {
return loadTimeData.getString('emptyBodySearch');
} else {
return loadTimeData.getString('emptyBody');
@@ -1105,14 +1258,47 @@ export class PowerBookmarksListElement extends PolymerElement {
}
private getEmptyImagePath_(): string {
- return this.shouldShowEmptySearchState_() ? '' :
- './images/bookmarks_empty.svg';
+ return this.hasSomeActiveFilter_ ? '' : './images/bookmarks_empty.svg';
}
private getEmptyImagePathDark_(): string {
- return this.shouldShowEmptySearchState_() ?
- '' :
- './images/bookmarks_empty_dark.svg';
+ return this.hasSomeActiveFilter_ ? '' : './images/bookmarks_empty_dark.svg';
+ }
+
+ private computeHasSomeActiveFilter_(): boolean {
+ return !!this.searchQuery_ || this.labels_.some(label => label.active);
+ }
+
+ private computeHasShownBookmarks_(): boolean {
+ return this.displayLists_.some((list) => list.length > 0);
+ }
+
+ private computeSectionVisibility_(): SectionVisibility {
+ if (this.guestMode_) {
+ return {topLevelEmptyState: true};
+ }
+
+ if (!this.hasLoadedData_) {
+ return {search: true, footer: true};
+ }
+
+ const hasActiveFolder = this.activeFolderPath_.length > 0;
+ const hasShownBookmarks = this.hasShownBookmarks_;
+ const hasSomeActiveFilter = this.hasSomeActiveFilter_;
+
+ return {
+ search: true,
+ filterChips: this.labels_.length > 0,
+ heading: !hasSomeActiveFilter && (hasActiveFolder || hasShownBookmarks),
+ filterHeadings: hasSomeActiveFilter,
+ folderEmptyState:
+ !hasShownBookmarks && !hasSomeActiveFilter && hasActiveFolder,
+ newFolderButton: !hasSomeActiveFilter,
+ bookmarksList: hasShownBookmarks,
+ topLevelEmptyState:
+ !hasShownBookmarks && (hasSomeActiveFilter || !hasActiveFolder),
+ footer: !hasSomeActiveFilter,
+ };
}
/**
@@ -1148,10 +1334,10 @@ export class PowerBookmarksListElement extends PolymerElement {
}
private onShownBookmarksResize_() {
- // The iron-list of `shownBookmarks_` is in a dynamically sized card.
+ // The iron-lists of `displayLists_` are in a dynamically sized card.
// Any time the size changes, let iron-list know so that iron-list can
// properly adjust to its possibly new height.
- this.$.shownBookmarksIronList.notifyResize();
+ this.notifyBookmarksListResize_();
this.hasScrollbars_ =
this.$.bookmarks.scrollHeight > this.$.bookmarks.offsetHeight;
diff --git a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_service.ts b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_service.ts
index 4edcf48866c..68478ef25f3 100644
--- a/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_service.ts
+++ b/chromium/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_service.ts
@@ -58,12 +58,17 @@ export function editingDisabledByPolicy(
}
// Return an array that includes folder and all its descendants.
-export function getFolderDescendants(folder: chrome.bookmarks.BookmarkTreeNode):
- chrome.bookmarks.BookmarkTreeNode[] {
+export function getFolderDescendants(
+ folder: chrome.bookmarks.BookmarkTreeNode,
+ excludeFolder: chrome.bookmarks.BookmarkTreeNode|undefined =
+ undefined): chrome.bookmarks.BookmarkTreeNode[] {
+ if (folder === excludeFolder) {
+ return [];
+ }
let expanded: chrome.bookmarks.BookmarkTreeNode[] = [folder];
if (folder.children) {
folder.children.forEach((child: chrome.bookmarks.BookmarkTreeNode) => {
- expanded = expanded.concat(getFolderDescendants(child));
+ expanded = expanded.concat(getFolderDescendants(child, excludeFolder));
});
}
return expanded;
@@ -228,11 +233,12 @@ export class PowerBookmarksService {
*/
filterBookmarks(
activeFolder: chrome.bookmarks.BookmarkTreeNode|undefined,
- activeSortIndex: number, searchQuery: string|undefined,
- labels: Label[]): chrome.bookmarks.BookmarkTreeNode[] {
- let shownBookmarks;
+ activeSortIndex: number, searchQuery: string|undefined, labels: Label[],
+ excludeFolder: chrome.bookmarks.BookmarkTreeNode|
+ undefined = undefined): chrome.bookmarks.BookmarkTreeNode[] {
+ let bookmarks: chrome.bookmarks.BookmarkTreeNode[] = [];
if (activeFolder) {
- shownBookmarks = activeFolder.children!.slice();
+ bookmarks = activeFolder.children!.slice();
} else {
let topLevelBookmarks: chrome.bookmarks.BookmarkTreeNode[] = [];
this.folders_.forEach(
@@ -240,19 +246,14 @@ export class PowerBookmarksService {
(folder.id === loadTimeData.getString('bookmarksBarId')) ?
[folder] :
folder.children!));
- shownBookmarks = topLevelBookmarks;
+ bookmarks = topLevelBookmarks;
}
if (searchQuery || labels.find((label) => label.active)) {
- shownBookmarks =
- this.applySearchQueryAndLabels(labels, searchQuery, shownBookmarks);
- }
- const sortChangedPosition =
- this.sortBookmarks(shownBookmarks, activeSortIndex);
- if (sortChangedPosition) {
- return shownBookmarks.slice();
- } else {
- return shownBookmarks;
+ bookmarks = this.applySearchQueryAndLabels_(
+ labels, searchQuery, bookmarks, excludeFolder);
}
+ const sortChangedPosition = this.sortBookmarks(bookmarks, activeSortIndex);
+ return sortChangedPosition ? bookmarks.slice() : bookmarks;
}
/**
@@ -337,27 +338,39 @@ export class PowerBookmarksService {
return folder.children!.findIndex(b => b.url === url) === -1;
}
- applySearchQueryAndLabels(
+ bookmarkMatchesSearchQueryAndLabels(
+ bookmark: chrome.bookmarks.BookmarkTreeNode, labels: Label[],
+ searchQuery: string|undefined): boolean {
+ return this.nodeMatchesContentFilters_(bookmark, labels) &&
+ (!searchQuery ||
+ (!!bookmark.title &&
+ bookmark.title.toLocaleLowerCase().includes(searchQuery!)) ||
+ (!!bookmark.url &&
+ bookmark.url.toLocaleLowerCase().includes(searchQuery!)));
+ }
+
+ setMaxImageServiceRequestsForTesting(max: number) {
+ this.maxImageServiceRequests_ = max;
+ }
+
+
+ private applySearchQueryAndLabels_(
labels: Label[], searchQuery: string|undefined,
- shownBookmarks: chrome.bookmarks.BookmarkTreeNode[]) {
+ shownBookmarks: chrome.bookmarks.BookmarkTreeNode[],
+ excludeFolder: chrome.bookmarks.BookmarkTreeNode|
+ undefined): chrome.bookmarks.BookmarkTreeNode[] {
let searchSpace: chrome.bookmarks.BookmarkTreeNode[] = [];
// Search space should include all descendants of the shown bookmarks, in
- // addition to the shown bookmarks themselves.
+ // addition to the shown bookmarks themselves, excluding the excludeFolder
+ // and its descendants.
shownBookmarks.forEach((bookmark: chrome.bookmarks.BookmarkTreeNode) => {
- searchSpace = searchSpace.concat(getFolderDescendants(bookmark));
+ searchSpace =
+ searchSpace.concat(getFolderDescendants(bookmark, excludeFolder));
});
return searchSpace.filter(
(bookmark: chrome.bookmarks.BookmarkTreeNode) =>
- this.nodeMatchesContentFilters_(bookmark, labels) &&
- (!searchQuery ||
- (bookmark.title &&
- bookmark.title.toLocaleLowerCase().includes(searchQuery!)) ||
- (bookmark.url &&
- bookmark.url.toLocaleLowerCase().includes(searchQuery!))));
- }
-
- setMaxImageServiceRequestsForTesting(max: number) {
- this.maxImageServiceRequests_ = max;
+ this.bookmarkMatchesSearchQueryAndLabels(
+ bookmark, labels, searchQuery));
}
private nodeMatchesContentFilters_(
diff --git a/chromium/chrome/browser/resources/side_panel/commerce/app.html b/chromium/chrome/browser/resources/side_panel/commerce/app.html
index c54e2b793af..05745cc153e 100644
--- a/chromium/chrome/browser/resources/side_panel/commerce/app.html
+++ b/chromium/chrome/browser/resources/side_panel/commerce/app.html
@@ -14,6 +14,8 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+ margin: 0;
+ padding: 0;
}
.panel-subtitle {
@@ -48,11 +50,14 @@
.history-subtitle {
line-height: 20px;
max-width: 100%;
- overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
+ #priceRange {
+ overflow: hidden;
+ }
+
shopping-insights-history-graph {
margin-top: 8px;
}
@@ -60,7 +65,7 @@
<div id="insightsContainer">
<div class="section" id="titleSection">
- <div class="panel-title">[[productInfo.clusterTitle]]</div>
+ <h1 class="panel-title">[[productInfo.clusterTitle]]</h1>
<div id="priceRange" class="panel-subtitle"
hidden="[[!priceInsightsInfo.typicalLowPrice.length]]">
[[getRangeDescription_(priceInsightsInfo)]]
@@ -77,7 +82,7 @@
<div class="divider"></div>
<div class="section" id="historySection">
<div class="section-title" id="historyTitle">
- [[getHistoryTitle_(priceInsightsInfo)]]
+ $i18n{historyTitle}
</div>
<catalog-attributes-row class="history-subtitle"
price-insights-info="[[priceInsightsInfo]]"
diff --git a/chromium/chrome/browser/resources/side_panel/commerce/app.ts b/chromium/chrome/browser/resources/side_panel/commerce/app.ts
index 3c4afa9c2cd..ad7386fa9b8 100644
--- a/chromium/chrome/browser/resources/side_panel/commerce/app.ts
+++ b/chromium/chrome/browser/resources/side_panel/commerce/app.ts
@@ -11,7 +11,7 @@ import './catalog_attributes_row.js';
import './insights_comment_row.js';
import {ShoppingListApiProxy, ShoppingListApiProxyImpl} from '//shopping-insights-side-panel.top-chrome/shared/commerce/shopping_list_api_proxy.js';
-import {PriceInsightsInfo, PriceInsightsInfo_PriceBucket, ProductInfo} from '//shopping-insights-side-panel.top-chrome/shared/shopping_list.mojom-webui.js';
+import {PriceInsightsInfo, ProductInfo} from '//shopping-insights-side-panel.top-chrome/shared/shopping_list.mojom-webui.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {listenOnce} from 'chrome://resources/js/util_ts.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -50,14 +50,8 @@ export class ShoppingInsightsAppElement extends PolymerElement {
private shoppingApi_: ShoppingListApiProxy =
ShoppingListApiProxyImpl.getInstance();
- override async connectedCallback() {
- super.connectedCallback();
-
- // Push showInsightsSidePanelUI() callback to the event queue to allow
- // deferred rendering to take place.
- listenOnce(this.$.insightsContainer, 'dom-change', () => {
- setTimeout(() => this.shoppingApi_.showInsightsSidePanelUi(), 0);
- });
+ override async ready() {
+ super.ready();
const {productInfo} = await this.shoppingApi_.getProductInfoForCurrentUrl();
this.productInfo = productInfo;
@@ -71,6 +65,16 @@ export class ShoppingInsightsAppElement extends PolymerElement {
eligible && (priceInsightsInfo.clusterId !== BigInt(0));
}
+ override connectedCallback() {
+ super.connectedCallback();
+
+ // Push showInsightsSidePanelUI() callback to the event queue to allow
+ // deferred rendering to take place.
+ listenOnce(this.$.insightsContainer, 'dom-change', () => {
+ setTimeout(() => this.shoppingApi_.showInsightsSidePanelUi(), 0);
+ });
+ }
+
private getRangeDescription_(info: PriceInsightsInfo): string {
const lowPrice: string = info.typicalLowPrice;
const highPrice: string = info.typicalHighPrice;
@@ -84,25 +88,6 @@ export class ShoppingInsightsAppElement extends PolymerElement {
loadTimeData.getStringF('rangeSingleOptionOnePrice', lowPrice) :
loadTimeData.getStringF('rangeSingleOption', lowPrice, highPrice);
}
-
- private getHistoryTitle_(info: PriceInsightsInfo): string {
- switch (info.bucket) {
- case PriceInsightsInfo_PriceBucket.kLow:
- return loadTimeData.getString(
- info.hasMultipleCatalogs ? 'lowPriceMultipleOptions' :
- 'lowPriceSingleOption');
- case PriceInsightsInfo_PriceBucket.kTypical:
- return loadTimeData.getString(
- info.hasMultipleCatalogs ? 'typicalPriceMultipleOptions' :
- 'typicalPriceSingleOption');
- case PriceInsightsInfo_PriceBucket.kHigh:
- return loadTimeData.getString(
- info.hasMultipleCatalogs ? 'highPriceMultipleOptions' :
- 'highPriceSingleOption');
- default:
- return loadTimeData.getString('historyTitle');
- }
- }
}
declare global {
diff --git a/chromium/chrome/browser/resources/side_panel/commerce/history_graph.html b/chromium/chrome/browser/resources/side_panel/commerce/history_graph.html
index 33e24e92fb6..413c5486aae 100644
--- a/chromium/chrome/browser/resources/side_panel/commerce/history_graph.html
+++ b/chromium/chrome/browser/resources/side_panel/commerce/history_graph.html
@@ -77,4 +77,4 @@
}
</style>
-<div id="historyGraph"></div> \ No newline at end of file
+<div id="historyGraph" tabindex="0" aria-live="polite"></div> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/side_panel/commerce/history_graph.ts b/chromium/chrome/browser/resources/side_panel/commerce/history_graph.ts
index e287505e7e6..d3646a9734b 100644
--- a/chromium/chrome/browser/resources/side_panel/commerce/history_graph.ts
+++ b/chromium/chrome/browser/resources/side_panel/commerce/history_graph.ts
@@ -64,6 +64,7 @@ export class ShoppingInsightsHistoryGraphElement extends PolymerElement {
currency: string;
private points: Array<{date: Date, price: number}>;
private isGraphInteracted_: boolean = false;
+ private currentPricePointIndex_?: number;
override connectedCallback() {
super.connectedCallback();
@@ -158,7 +159,10 @@ export class ShoppingInsightsHistoryGraphElement extends PolymerElement {
'transform', `translate(0,${graphHeightPx - graphMarginBottomPx})`)
.call(xAxis)
.call(g => g.select('.domain').classed(CssClass.AXIS, true))
- .call(g => g.selectAll('text').classed(CssClass.TICK, true));
+ .call(
+ g => g.selectAll('text')
+ .classed(CssClass.TICK, true)
+ .attr('aria-hidden', 'true'));
// Add the Y axis.
svg.append('g')
@@ -172,7 +176,10 @@ export class ShoppingInsightsHistoryGraphElement extends PolymerElement {
'x2',
graphWidthPx - graphMarginLeftPx - graphMarginRightPx)
.classed(CssClass.DIVIDER, true))
- .call(g => g.selectAll('text').classed(CssClass.TICK, true));
+ .call(
+ g => g.selectAll('text')
+ .classed(CssClass.TICK, true)
+ .attr('aria-hidden', 'true'));
// Add the line.
svg.append('path')
@@ -203,7 +210,8 @@ export class ShoppingInsightsHistoryGraphElement extends PolymerElement {
const tooltip = svg.append('text')
.attr('y', BUBBLE_PADDING_PX + tooltipHeight / 2)
.attr('dominant-baseline', 'middle')
- .attr('opacity', 0);
+ .attr('opacity', 0)
+ .attr('aria-hidden', 'true');
const initialIndex = this.points.length - 1;
this.showTooltip_(
@@ -235,6 +243,31 @@ export class ShoppingInsightsHistoryGraphElement extends PolymerElement {
xScale(this.points[nearestIndex].date),
yScale(this.points[nearestIndex].price), graphWidthPx);
});
+
+ this.$.historyGraph.addEventListener('keydown', (e: KeyboardEvent) => {
+ if (this.currentPricePointIndex_ != null) {
+ let nextIndex = -1;
+
+ if (e.key === 'ArrowLeft') {
+ nextIndex = this.currentPricePointIndex_ - 1;
+ } else if (e.key === 'ArrowRight') {
+ nextIndex = this.currentPricePointIndex_ + 1;
+ }
+
+ if (nextIndex >= 0 && nextIndex <= this.points.length - 1) {
+ this.showTooltip_(
+ verticalLine, circle, bubble, tooltip, nextIndex,
+ xScale(this.points[nextIndex].date),
+ yScale(this.points[nextIndex].price), graphWidthPx);
+ }
+ }
+ });
+
+ this.$.historyGraph.setAttribute(
+ 'aria-label',
+ loadTimeData.getString('historyTitle') +
+ this.getTooltipText_(initialIndex) +
+ loadTimeData.getString('historyGraphAccessibility'));
}
// Calculate y-axis ticks.
@@ -245,21 +278,24 @@ export class ShoppingInsightsHistoryGraphElement extends PolymerElement {
const ticks: number[] = [];
const formattedTicks: string[] = [];
- const minPrice = this.points.reduce((min, value) => {
+ let minPrice = this.points.reduce((min, value) => {
return Math.min(min, value.price);
}, this.points[0].price);
- const maxPrice = this.points.reduce((max, value) => {
+ let maxPrice = this.points.reduce((max, value) => {
return Math.max(max, value.price);
}, this.points[0].price);
+
+ // Ensure the line is in the middle of the graph.
+ minPrice = Math.max(minPrice - 1, 0);
+ maxPrice = maxPrice + 1;
const valueRange = maxPrice - minPrice;
let tickInterval = valueRange / (TICK_COUNT_Y - 1);
// Ensure the tick interval is a multiple of below values to improve the
// readability. Bigger values are used when possible.
- const multipliers =
- [100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01];
+ const multipliers = [100, 50, 20, 10, 5, 2, 1];
- let multiplier = 0.01;
+ let multiplier = 1;
for (const tempMultiplier of multipliers) {
if (tickInterval >= 2 * tempMultiplier) {
multiplier = tempMultiplier;
@@ -277,13 +313,11 @@ export class ShoppingInsightsHistoryGraphElement extends PolymerElement {
Math.ceil((maxPrice - tickLow) / (TICK_COUNT_Y - 1) / multiplier) *
multiplier;
- const fractionDigits = tickInterval > 1 ? 0 : 2;
-
const formatter = new Intl.NumberFormat(this.locale, {
style: 'currency',
currency: this.currency,
- minimumFractionDigits: fractionDigits,
- maximumFractionDigits: fractionDigits,
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
});
// Populate results.
@@ -383,6 +417,9 @@ export class ShoppingInsightsHistoryGraphElement extends PolymerElement {
}
tooltip.attr('x', bubbleStart + bubbleWidth / 2).attr('opacity', 1);
bubble.attr('x', bubbleStart).attr('width', bubbleWidth).attr('opacity', 1);
+
+ this.$.historyGraph.setAttribute('aria-label', this.getTooltipText_(index));
+ this.currentPricePointIndex_ = index;
}
}
diff --git a/chromium/chrome/browser/resources/side_panel/commerce/price_tracking_section.html b/chromium/chrome/browser/resources/side_panel/commerce/price_tracking_section.html
index 45ea7e8a61e..07080a0c387 100644
--- a/chromium/chrome/browser/resources/side_panel/commerce/price_tracking_section.html
+++ b/chromium/chrome/browser/resources/side_panel/commerce/price_tracking_section.html
@@ -47,7 +47,8 @@
folder name and period will be in the same line. -->
<div id="toggleAnnotation">[[toggleAnnotationText_]]<a href="#"
id="toggleAnnotationButton"
- on-click="onFolderClicked_">[[folderName_]]</a><span>.</span>
+ on-click="onFolderClicked_"
+ hidden="[[!folderName_]]">[[folderName_]]</a><span>.</span>
</div>
</div>
<cr-toggle id="toggle" checked="{{isProductTracked_}}"
diff --git a/chromium/chrome/browser/resources/side_panel/companion/companion.ts b/chromium/chrome/browser/resources/side_panel/companion/companion.ts
index 16720d1379b..330d81b9041 100644
--- a/chromium/chrome/browser/resources/side_panel/companion/companion.ts
+++ b/chromium/chrome/browser/resources/side_panel/companion/companion.ts
@@ -71,6 +71,9 @@ enum ParamType {
// Arguments for sending Visual Search results from browser to iframe.
VISUAL_SEARCH_PARAMS = 'visualSearchParams',
+ // Arguments for sending Visual Search alt text from browser to iframe.
+ VISUAL_SEARCH_IMAGE_ALT_TEXTS = 'visualSearchImageAltTexts',
+
// Arguments for sending companion loading state from iframe to browser.
COMPANION_LOADING_STATE = 'companionLoadingState',
}
@@ -176,25 +179,16 @@ function initialize() {
}
});
- companionProxy.callbackRouter.onNavigationError.addListener(() => {
- const networkErrorOverlay = document.getElementById('network-error-page');
- const frame = document.body.querySelector('iframe');
- assert(frame);
- assert(networkErrorOverlay);
-
- // Hide the frame and show the network error overlay.
- networkErrorOverlay.style.display = 'block';
- frame.style.display = 'none';
- });
-
// POST dataUris from the Visual Search classification results to the iframe
companionProxy.callbackRouter.onDeviceVisualClassificationResult.addListener(
(results: VisualSearchResult[]) => {
const dataUris = results.map(result => result.dataUri);
+ const altTexts = results.map(result => result.altText);
const message = {
[ParamType.METHOD_TYPE]:
MethodType.kOnDeviceVisualClassificationResult,
[ParamType.VISUAL_SEARCH_PARAMS]: dataUris,
+ [ParamType.VISUAL_SEARCH_IMAGE_ALT_TEXTS]: altTexts,
};
const companionOrigin =
@@ -206,6 +200,17 @@ function initialize() {
}
});
+ companionProxy.callbackRouter.onNavigationError.addListener(() => {
+ const networkErrorOverlay = document.getElementById('network-error-page');
+ const frame = document.body.querySelector('iframe');
+ assert(frame);
+ assert(networkErrorOverlay);
+
+ // Hide the frame and show the network error overlay.
+ networkErrorOverlay.style.display = 'block';
+ frame.style.display = 'none';
+ });
+
companionProxy.callbackRouter.notifyLinkOpen.addListener(
(openedUrl: Url, metadata: LinkOpenMetadata) => {
const companionOrigin =
@@ -283,6 +288,8 @@ function onCompanionMessageEvent(event: MessageEvent) {
} else if (methodType === MethodType.kCompanionLoadingState) {
companionProxy.handler.onLoadingState(
data[ParamType.COMPANION_LOADING_STATE]);
+ } else if (methodType === MethodType.kRefreshCompanionPage) {
+ companionProxy.handler.refreshCompanionPage();
}
}
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/BUILD.gn b/chromium/chrome/browser/resources/side_panel/customize_chrome/BUILD.gn
index 7b84eec4f82..abf2731304d 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/BUILD.gn
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/BUILD.gn
@@ -23,18 +23,17 @@ build_webui("build") {
"categories.ts",
"check_mark_wrapper.ts",
"chrome_colors.ts",
- "color.ts",
- "colors.ts",
"hover_button.ts",
"shortcuts.ts",
"themes.ts",
"theme_snapshot.ts",
+ "wallpaper_search.ts",
]
non_web_component_files = [
"chrome_cart_proxy.ts",
- "color_utils.ts",
"customize_chrome_api_proxy.ts",
+ "window_proxy.ts",
]
mojo_files_deps = [
@@ -53,6 +52,7 @@ build_webui("build") {
"//ui/webui/resources/cr_components/color_change_listener:build_ts",
"//ui/webui/resources/cr_components/help_bubble:build_ts",
"//ui/webui/resources/cr_components/managed_dialog:build_ts",
+ "//ui/webui/resources/cr_components/theme_color_picker:build_ts",
"//ui/webui/resources/cr_elements:build_ts",
"//ui/webui/resources/js:build_ts",
"//ui/webui/resources/mojo:build_ts",
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/app.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/app.html
index dbf32a50c04..1f0854ccbea 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/app.html
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/app.html
@@ -4,6 +4,7 @@
color: var(--cr-primary-text-color);
height: 100%;
overflow: auto;
+ position: relative;
}
:host-context([chrome-refresh-2023]) #container {
@@ -53,15 +54,30 @@
customize-chrome-cards,
customize-chrome-categories,
customize-chrome-themes,
- customize-chrome-chrome-colors {
+ customize-chrome-chrome-colors,
+ customize-chrome-wallpaper-search {
display: block;
max-width: 320px;
width: 100%;
}
+
+ :host-context([chrome-refresh-2023]) customize-chrome-appearance {
+ margin-bottom: 0;
+ }
</style>
-<iron-pages id="container" class="sp-scroller"
+<iron-pages id="container"
+ class="sp-scroller sp-scroller-top-of-page sp-scroller-bottom-of-page"
selected="[[page_]]" attr-for-selected="page-name">
<div page-name="overview" id="overviewPage">
+ <template is="dom-if" if="[[wallpaperSearchEnabled_]]" restamp>
+ <div id="wallpaper" class="section sp-card">
+ <sp-heading hide-back-button>
+ <h2 slot="heading">Wallpaper Search</h2>
+ </sp-heading>
+ <customize-chrome-wallpaper-search></customize-chrome-wallpaper-search>
+ </div>
+ <hr class="sp-cards-separator">
+ </template>
<div id="appearance" class="section sp-card">
<sp-heading hide-back-button>
<h2 slot="heading">$i18n{appearanceHeader}</h2>
@@ -86,6 +102,14 @@
<customize-chrome-cards></customize-chrome-cards>
</div>
</template>
+ <template is="dom-if" if="[[extensionsCardEnabled_]]" restamp>
+ <hr class="sp-cards-separator">
+ <div id="extensions" class="section sp-card">
+ <sp-heading hide-back-button>
+ <h2 slot="heading">Extensions</h2>
+ </sp-heading>
+ </div>
+ </template>
</div>
<customize-chrome-categories on-back-click="onBackClick_"
on-collection-select="onCollectionSelect_" page-name="categories"
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/app.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/app.ts
index 46d16db5fb0..f87bb462b31 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/app.ts
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/app.ts
@@ -11,6 +11,7 @@ import './categories.js';
import './chrome_colors.js';
import './shortcuts.js';
import './themes.js';
+import './wallpaper_search.js';
import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
import {HelpBubbleMixin, HelpBubbleMixinInterface} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js';
@@ -78,6 +79,14 @@ export class AppElement extends AppElementBase {
type: Object,
value: null,
},
+ extensionsCardEnabled_: {
+ type: Boolean,
+ value: () => loadTimeData.getBoolean('extensionsCardEnabled'),
+ },
+ wallpaperSearchEnabled_: {
+ type: Boolean,
+ value: () => loadTimeData.getBoolean('wallpaperSearchEnabled'),
+ },
};
}
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.html
index 281f70908c0..531a667c96d 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.html
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.html
@@ -4,7 +4,7 @@
width: calc(100% - 32px);
}
- :host([third-party-theme-id_]) #editThemeButton {
+ .theme-button:not([hidden]) ~ #editThemeButton {
margin: 24px 16px 16px;
}
@@ -31,6 +31,10 @@
--cr-icon-image: url('chrome://resources/images/open_in_new.svg');
}
+ #uploadedImageButton {
+ --cr-icon-image: url(icons/upload.svg);
+ }
+
managed-dialog {
--cr-dialog-width: min(calc(100% - 32px), 512px);
}
@@ -38,39 +42,86 @@
customize-color-scheme-mode,
.sp-hr {
display: none;
- margin: 0 16px 16px;
width: calc(100% - 32px);
}
+ .sp-hr {
+ margin: 0 16px;
+ }
+
+ customize-color-scheme-mode {
+ margin: 16px;
+ }
+
+ :host-context([chrome-refresh-2023]) cr-theme-color-picker {
+ display: block;
+ margin-bottom: 16px;
+ }
+
:host-context([chrome-refresh-2023]) customize-color-scheme-mode,
:host-context([chrome-refresh-2023]) .sp-hr {
display: block;
}
+ :host-context([chrome-refresh-2023]) .theme-button {
+ margin-top: 16px;
+ }
+
+ :host-context([chrome-refresh-2023]) .theme-button:not([hidden]) ~ #editThemeButton {
+ margin: 20px 16px;
+ }
+
:host-context([chrome-refresh-2023]) #editThemeButton {
margin-bottom: 16px;
}
+
+ #followThemeToggle {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: 8px 0;
+ padding: 10px var(--sp-card-inline-padding);
+ }
+
+ #followThemeToggleTitle {
+ font-size: inherit;
+ line-height: inherit;
+ }
</style>
<customize-chrome-theme-snapshot id="themeSnapshot"
- hidden$="[[!showFirstPartyThemeView_]]">
+ on-edit-theme-click="onEditThemeClicked_"
+ hidden$="[[!showThemeSnapshot_]]">
</customize-chrome-theme-snapshot>
-<customize-chrome-hover-button id="thirdPartyLinkButton"
+<customize-chrome-hover-button id="thirdPartyLinkButton" class="theme-button"
hidden$="[[!thirdPartyThemeId_]]" on-click="onThirdPartyLinkButtonClick_"
label="[[thirdPartyThemeName_]]"
label-description="$i18n{currentTheme}">
</customize-chrome-hover-button>
+<customize-chrome-hover-button id="uploadedImageButton" class="theme-button"
+ hidden$="[[!showUploadedImageButton_]]"
+ on-click="onUploadedImageButtonClick_"
+ label="$i18n{yourUploadedImage}"
+ label-description="$i18n{currentTheme}">
+</customize-chrome-hover-button>
<cr-button id="editThemeButton" on-click="onEditThemeClicked_"
class$="[[themeButtonClass_]]">
<div id="imageIcon" class="cr-icon" slot="prefix-icon"></div>
$i18n{changeTheme}
</cr-button>
-<hr class="sp-hr" hidden$="[[!showFirstPartyThemeView_]]">
-<customize-color-scheme-mode
- hidden$="[[!showFirstPartyThemeView_]]">
+<hr class="sp-hr">
+<customize-color-scheme-mode>
</customize-color-scheme-mode>
-<customize-chrome-colors id="chromeColors"
- hidden$="[[!showFirstPartyThemeView_]]">
-</customize-chrome-colors>
+<cr-theme-color-picker id="chromeColors"
+ hidden$="[[!showColorPicker_]]">
+</cr-theme-color-picker>
+<hr class="sp-hr" hidden$="[[!showBottomDivider_]]">
+<div id="followThemeToggle" class="sp-card-content"
+ hidden$="[[!showDeviceThemeToggle_]]">
+ <div id="followThemeToggleTitle">$i18n{followThemeToggle}</div>
+ <cr-toggle id="followThemeToggleControl" title="$i18n{followThemeToggle}"
+ checked="[[theme_.followDeviceTheme]]"
+ on-change="onFollowThemeToggleChange_">
+ </cr-toggle>
</div>
<customize-chrome-hover-button id="setClassicChromeButton"
hidden$="[[!showClassicChromeButton_]]"
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.ts
index 470a53ff16c..8ec4f20af7e 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.ts
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/appearance.ts
@@ -2,16 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import './colors.js';
import './theme_snapshot.js';
import './hover_button.js';
import './strings.m.js'; // Required by <managed-dialog>.
import 'chrome://resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.js';
+import 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.js';
+import 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
import 'chrome://resources/cr_elements/cr_icons.css.js';
+import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
import {assert} from 'chrome://resources/js/assert_ts.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './appearance.html.js';
@@ -25,6 +29,9 @@ export interface AppearanceElement {
themeSnapshot: HTMLElement,
setClassicChromeButton: HTMLButtonElement,
thirdPartyLinkButton: HTMLButtonElement,
+ followThemeToggle: HTMLElement,
+ followThemeToggleControl: CrToggleElement,
+ uploadedImageButton: HTMLButtonElement,
};
}
@@ -42,6 +49,12 @@ export class AppearanceElement extends PolymerElement {
theme_: Object,
themeButtonClass_: String,
+ chromeRefresh2023Enabled_: {
+ type: Boolean,
+ value: () =>
+ document.documentElement.hasAttribute('chrome-refresh-2023'),
+ },
+
thirdPartyThemeId_: {
type: String,
computed: 'computeThirdPartyThemeId_(theme_)',
@@ -54,12 +67,11 @@ export class AppearanceElement extends PolymerElement {
reflectToAttribute: true,
},
- // Prevents side panel from showing theme snapshot and colors before
- // thirdPartyThemeName_ is determined if third party theme is installed.
- showFirstPartyThemeView_: {
+ showBottomDivider_: {
type: Boolean,
value: false,
- computed: 'computeShowFirstPartyThemeView_(theme_)',
+ computed:
+ 'computeShowBottomDivider_(showClassicChromeButton_, showDeviceThemeToggle_)',
},
showClassicChromeButton_: {
@@ -68,16 +80,45 @@ export class AppearanceElement extends PolymerElement {
computed: 'computeShowClassicChromeButton_(theme_)',
},
+ showColorPicker_: {
+ type: Boolean,
+ value: false,
+ computed: 'computeShowColorPicker_(theme_)',
+ },
+
+ showDeviceThemeToggle_: {
+ type: Boolean,
+ value: false,
+ computed: 'computeShowDeviceThemeToggle_(theme_)',
+ },
+
+ showThemeSnapshot_: {
+ type: Boolean,
+ value: false,
+ computed: 'computeShowThemeSnapshot_(theme_)',
+ },
+
+ showUploadedImageButton_: {
+ type: Boolean,
+ value: false,
+ computed: 'computeShowUploadedImageButton_(theme_)',
+ },
+
showManagedDialog_: Boolean,
};
}
private theme_: Theme|undefined = undefined;
private themeButtonClass_: string;
+ private chromeRefresh2023Enabled_: boolean;
private thirdPartyThemeId_: string|null = null;
private thirdPartyThemeName_: string|null = null;
+ private hasUploadedImage_: boolean;
+ private showBottomDivider_: boolean;
private showClassicChromeButton_: boolean;
- private showFirstPartyThemeView_: boolean;
+ private showColorPicker_: boolean;
+ private showDeviceThemeToggle_: boolean;
+ private showThemeSnapshot: boolean;
private showManagedDialog_: boolean;
private setThemeListenerId_: number|null = null;
@@ -94,9 +135,7 @@ export class AppearanceElement extends PolymerElement {
override connectedCallback() {
super.connectedCallback();
this.themeButtonClass_ =
- document.documentElement.hasAttribute('chrome-refresh-2023') ?
- 'floating-button' :
- 'action-button';
+ this.chromeRefresh2023Enabled_ ? 'floating-button' : 'action-button';
this.setThemeListenerId_ =
this.callbackRouter_.setTheme.addListener((theme: Theme) => {
this.theme_ = theme;
@@ -131,8 +170,8 @@ export class AppearanceElement extends PolymerElement {
}
}
- private computeShowFirstPartyThemeView_(): boolean {
- return !!this.theme_ && !this.theme_.thirdPartyThemeInfo;
+ private computeShowBottomDivider_(): boolean {
+ return !!(this.showClassicChromeButton_ || this.showDeviceThemeToggle_);
}
private computeShowClassicChromeButton_(): boolean {
@@ -141,6 +180,29 @@ export class AppearanceElement extends PolymerElement {
(this.theme_.backgroundImage || this.theme_.thirdPartyThemeInfo));
}
+ private computeShowColorPicker_(): boolean {
+ return !!this.theme_ && !this.theme_.thirdPartyThemeInfo;
+ }
+
+ private computeShowDeviceThemeToggle_(): boolean {
+ return loadTimeData.getBoolean('showDeviceThemeToggle') &&
+ !(!!this.theme_ && !!this.theme_.thirdPartyThemeInfo);
+ }
+
+ private computeShowThemeSnapshot_(): boolean {
+ return !!this.theme_ && !this.theme_.thirdPartyThemeInfo &&
+ (!this.chromeRefresh2023Enabled_ ||
+ !(this.theme_.backgroundImage &&
+ this.theme_.backgroundImage.isUploadedImage));
+ }
+
+ private computeShowUploadedImageButton_(): boolean {
+ return !!(
+ this.chromeRefresh2023Enabled_ && this.theme_ &&
+ this.theme_.backgroundImage &&
+ this.theme_.backgroundImage.isUploadedImage);
+ }
+
private onEditThemeClicked_() {
if (this.handleClickForManagedThemes_()) {
return;
@@ -154,6 +216,10 @@ export class AppearanceElement extends PolymerElement {
}
}
+ private onUploadedImageButtonClick_() {
+ this.pageHandler_.chooseLocalCustomBackground();
+ }
+
private onSetClassicChromeClicked_() {
if (this.handleClickForManagedThemes_()) {
return;
@@ -162,6 +228,10 @@ export class AppearanceElement extends PolymerElement {
this.pageHandler_.setDefaultColor();
}
+ private onFollowThemeToggleChange_(e: CustomEvent<boolean>) {
+ this.pageHandler_.setFollowDeviceTheme(e.detail);
+ }
+
private onManagedDialogClosed_() {
this.showManagedDialog_ = false;
}
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/categories.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/categories.html
index 065ad8464e5..ab414197d21 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/categories.html
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/categories.html
@@ -45,6 +45,12 @@
background-color: var(--color-side-panel-customize-chrome-theme-background);
}
+ :host-context([chrome-refresh-2023]) #cornerNewTabPageTile {
+ background-color: rgb( 211, 227, 253);
+ border: solid 1px
+ var(--color-side-panel-customize-chrome-corner-ntp-border);
+ }
+
:host-context([chrome-refresh-2023]) #uploadImageTile .image-container {
--cr-icon-color: var(
--color-side-panel-customize-chrome-custom-option-foreground);
@@ -54,7 +60,7 @@
:host-context([chrome-refresh-2023]) #chromeWebStoreTile .image-container {
border: solid 1px
- var(--color-side-panel-customize-chrome-web-store-option-border);
+ var(--color-side-panel-customize-chrome-web-store-border);
}
:host-context([chrome-refresh-2023]) #chromeWebStoreTile .cr-icon {
@@ -85,53 +91,53 @@
line-height: 16px;
}
- .tile:nth-child(-n+4) {
+ .tile:nth-of-type(-n+4) {
grid-column: span 3;
/* To result in an extra 5px horizontal gap between first 4 tiles. */
padding-inline-end: 2.5px;
}
- :host-context([chrome-refresh-2023]) .tile:nth-child(-n+4) {
+ :host-context([chrome-refresh-2023]) .tile:nth-of-type(-n+4) {
/* To result in an extra 8px horizontal gap between first 4 tiles. */
padding-inline-end: 4px;
}
- :host-context([chrome-refresh-2023]) .tile:nth-child(-n+4) {
+ :host-context([chrome-refresh-2023]) .tile:nth-of-type(-n+4) {
/* To result in an extra 10px vertical gap between large and small tiles. */
padding-block-end: 10px;
/* To result in an extra 8px vertical gap between first 4 tiles. */
padding-block-start: 4px;
}
- :host-context([chrome-refresh-2023]) .tile:nth-child(-n+2) {
+ :host-context([chrome-refresh-2023]) .tile:nth-of-type(-n+2) {
/* To result in an extra 8px vertical gap between first 4 tiles. */
padding-block-end: 4px;
padding-block-start: 0;
}
- .tile:nth-child(-n+4):nth-child(even) {
+ .tile:nth-of-type(-n+4):nth-of-type(even) {
padding-inline-end: 0;
/* To result in an extra 5px horizontal gap between first 4 tiles. */
padding-inline-start: 2.5px;
}
- :host-context([chrome-refresh-2023]) .tile:nth-child(-n+4):nth-child(even) {
+ :host-context([chrome-refresh-2023]) .tile:nth-of-type(-n+4):nth-of-type(even) {
padding-inline-end: 0;
/* To result in an extra 8px horizontal gap between first 4 tiles. */
padding-inline-start: 4px;
}
- .tile:nth-child(-n+4) .image-container {
+ .tile:nth-of-type(-n+4) .image-container {
border-radius: 16px;
}
- .tile:nth-child(-n+4) .label {
+ .tile:nth-of-type(-n+4) .label {
font-size: 13px;
line-height: 20px;
padding-top: 12px;
}
- :host-context([chrome-refresh-2023]) .tile:nth-child(-n+4) .label {
+ :host-context([chrome-refresh-2023]) .tile:nth-of-type(-n+4) .label {
font-size: 12px;
line-height: 16px;
padding-top: 8px;
@@ -172,11 +178,20 @@
width: 21px;
}
- #miniNewTabPage {
+ #cornerNewTabPage {
left: 12%;
top: 15%;
}
+ :host-context([chrome-refresh-2023]) #cornerNewTabPage {
+ border-radius: 4px;
+ box-shadow: var(--cr-elevation-2);
+ height: 128px;
+ width: 128px;
+ left: 20px;
+ top: 20px;
+ }
+
svg {
height: 100%;
top: 0;
@@ -198,7 +213,7 @@
width: 32px;
}
- .tile:nth-child(-n+4) customize-chrome-check-mark-wrapper {
+ .tile:nth-of-type(-n+4) customize-chrome-check-mark-wrapper {
--customize-chrome-check-mark-wrapper-end: -6px;
--customize-chrome-check-mark-wrapper-size: 24px;
--customize-chrome-check-mark-wrapper-top: -9px;
@@ -243,8 +258,17 @@
aria-current$="[[boolToString_(isClassicChromeSelected_)]]">
<customize-chrome-check-mark-wrapper
checked="[[isClassicChromeSelected_]]">
- <div class="image-container">
- <img id="miniNewTabPage" src="icons/corner_new_tab_page.svg"></img>
+ <div id="cornerNewTabPageTile" class="image-container">
+ <template is="dom-if" if="[[!chromeRefresh2023Enabled_]]">
+ <img id="cornerNewTabPage"
+ src="icons/corner_new_tab_page.svg">
+ </img>
+ </template>
+ <template is="dom-if" if="[[chromeRefresh2023Enabled_]]">
+ <img id="cornerNewTabPage"
+ src="icons/gm3_corner_new_tab_page.svg">
+ </img>
+ </template>
</div>
</customize-chrome-check-mark-wrapper>
<div class="label">$i18n{classicChrome}</div>
@@ -260,23 +284,25 @@
</customize-chrome-check-mark-wrapper>
<div class="label">$i18n{uploadImage}</div>
</div>
- <div class="tile" tabindex="0" id="chromeColorsTile"
- role="button" on-click="onChromeColorsClick_"
- aria-current$="[[boolToString_(isChromeColorsSelected_)]]">
- <customize-chrome-check-mark-wrapper
- checked="[[isChromeColorsSelected_]]">
- <div class="image-container">
- <svg viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink">
- <rect id="foreground" x="0" y="0" width="50" height="50">
- </rect>
- <rect id="background" x="0" y="25" width="50" height="25">
- </rect>
- </svg>
- </div>
- </customize-chrome-check-mark-wrapper>
- <div class="label">$i18n{chromeColors}</div>
- </div>
+ <template is="dom-if" if="[[!chromeRefresh2023Enabled_]]">
+ <div class="tile" tabindex="0" id="chromeColorsTile"
+ role="button" on-click="onChromeColorsClick_"
+ aria-current$="[[boolToString_(isChromeColorsSelected_)]]">
+ <customize-chrome-check-mark-wrapper
+ checked="[[isChromeColorsSelected_]]">
+ <div class="image-container">
+ <svg viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <rect id="foreground" x="0" y="0" width="50" height="50">
+ </rect>
+ <rect id="background" x="0" y="25" width="50" height="25">
+ </rect>
+ </svg>
+ </div>
+ </customize-chrome-check-mark-wrapper>
+ <div class="label">$i18n{chromeColors}</div>
+ </div>
+ </template>
<template is="dom-repeat" id="collectionsRepeat" items="[[collections_]]"
on-rendered-item-count-changed="onCollectionsRendered_">
<div class="tile collection" tabindex="0" role="button"
@@ -288,7 +314,8 @@
<div class="image-container">
<img is="cr-auto-img"
auto-src="[[item.previewImageUrl.url]]"
- draggable="false">
+ draggable="false"
+ on-load="onPreviewImageLoad_">
</img>
</div>
</customize-chrome-check-mark-wrapper>
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/categories.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/categories.ts
index b4964479b39..c7edef30f40 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/categories.ts
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/categories.ts
@@ -19,6 +19,7 @@ import {DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/po
import {getTemplate} from './categories.html.js';
import {BackgroundCollection, CustomizeChromePageHandlerInterface, Theme} from './customize_chrome.mojom-webui.js';
import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js';
+import {WindowProxy} from './window_proxy.js';
export enum CategoryType {
NONE,
@@ -44,7 +45,6 @@ const CategoriesElementBase = HelpBubbleMixin(PolymerElement) as
export interface CategoriesElement {
$: {
chromeWebStoreTile: HTMLElement,
- chromeColorsTile: HTMLElement,
classicChromeTile: HTMLElement,
heading: SpHeading,
uploadImageTile: HTMLElement,
@@ -62,6 +62,11 @@ export class CategoriesElement extends CategoriesElementBase {
static get properties() {
return {
+ chromeRefresh2023Enabled_: {
+ type: Boolean,
+ value: () =>
+ document.documentElement.hasAttribute('chrome-refresh-2023'),
+ },
collections_: Array,
theme_: Object,
selectedCategory_: {
@@ -86,13 +91,15 @@ export class CategoriesElement extends CategoriesElementBase {
private collections_: BackgroundCollection[];
private selectedCategory_: SelectedCategory;
private theme_: Theme;
- private setThemeListenerId_: number|null = null;
private pageHandler_: CustomizeChromePageHandlerInterface;
+ private previewImageLoadStartEpoch_: number;
+ private setThemeListenerId_: number|null = null;
constructor() {
super();
this.pageHandler_ = CustomizeChromeApiProxy.getInstance().handler;
+ this.previewImageLoadStartEpoch_ = WindowProxy.getInstance().now();
this.pageHandler_.getBackgroundCollections().then(({collections}) => {
this.collections_ = collections;
});
@@ -133,6 +140,20 @@ export class CategoriesElement extends CategoriesElementBase {
}
}
+ private onPreviewImageLoad_() {
+ chrome.metricsPrivate.recordValue(
+ {
+ metricName: 'NewTabPage.Images.ShownTime.CollectionPreviewImage',
+ type: chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LOG,
+ min: 1,
+ max: 60000, // 60 seconds.
+ buckets: 100,
+ },
+ Math.floor(
+ WindowProxy.getInstance().now() -
+ this.previewImageLoadStartEpoch_));
+ }
+
private computeSelectedCategory_() {
if (!this.theme_ || this.theme_.thirdPartyThemeInfo) {
return {type: CategoryType.NONE};
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.html
index 327c5953ccc..c801073aad3 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.html
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.html
@@ -51,8 +51,9 @@
fill: var(--customize-chrome-check-mark-wrapper-color);
}
- :host([checked]) ::slotted(*) {
- border: 2px solid var(--customize-chrome-check-mark-wrapper-background);
+ :host([checked]):host(:not([checkmark-border-hidden])) ::slotted(*) {
+ border: 2px solid
+ var(--customize-chrome-check-mark-wrapper-background) !important;
}
</style>
<svg id="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.ts
index f521e10e616..2df4de420d6 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.ts
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/check_mark_wrapper.ts
@@ -27,10 +27,16 @@ export class CheckMarkWrapperElement extends PolymerElement {
type: Boolean,
reflectToAttribute: true,
},
+ checkmarkBorderHidden: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ },
};
}
checked: boolean;
+ checkmarkBorderHidden: boolean;
}
declare global {
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.html
index 7fd3b1dd326..c6cfa070631 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.html
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.html
@@ -49,11 +49,11 @@
width: 20px;
}
- customize-chrome-color {
- --customize-chrome-color-border-radius: 12px;
- --customize-chrome-color-check-mark-end: -4px;
- --customize-chrome-color-check-mark-size: 20px;
- --customize-chrome-color-check-mark-top: -6px;
+ cr-theme-color {
+ --cr-theme-color-border-radius: 12px;
+ --cr-theme-color-check-mark-end: -4px;
+ --cr-theme-color-check-mark-size: 20px;
+ --cr-theme-color-check-mark-top: -6px;
padding: 0;
}
@@ -80,17 +80,17 @@
aria-current$="[[boolToString_(isCustomColorSelected_)]]"
tabindex="0"
on-click="onCustomColorClick_">
- <customize-chrome-color
+ <cr-theme-color
id="customColor"
background-color="[[customColor_.background]]"
foreground-color="[[customColor_.foreground]]"
checked="[[isCustomColorSelected_]]">
- </customize-chrome-color>
+ </cr-theme-color>
<div id="colorPickerIcon"></div>
<input id="colorPicker" type="color" tabindex="-1" aria-hidden="true"
on-change="onCustomColorChange_">
</div>
- <customize-chrome-color
+ <cr-theme-color
id="defaultColor"
class="tile"
title="$i18n{defaultColorName}"
@@ -102,9 +102,9 @@
background-color="[[defaultColor_.background]]"
foreground-color="[[defaultColor_.foreground]]"
checked="[[isDefaultColorSelected_]]">
- </customize-chrome-color>
+ </cr-theme-color>
<template is="dom-repeat" id="chromeColorsRepeat" items="[[colors_]]">
- <customize-chrome-color
+ <cr-theme-color
class="chrome-color tile"
title="[[item.name]]"
aria-label$="[[item.name]]"
@@ -116,7 +116,7 @@
background-color="[[item.background]]"
foreground-color="[[item.foreground]]"
checked="[[isChromeColorSelected_(item.seed, selectedColor_)]]">
- </customize-chrome-color>
+ </cr-theme-color>
</template>
</cr-grid>
</div>
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.ts
index d654aa70d74..639d30fd060 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.ts
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/chrome_colors.ts
@@ -4,30 +4,31 @@
import 'chrome://customize-chrome-side-panel.top-chrome/shared/sp_heading.js';
import 'chrome://customize-chrome-side-panel.top-chrome/shared/sp_shared_style.css.js';
+import 'chrome://resources/cr_components/theme_color_picker/theme_color.js';
import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
import 'chrome://resources/cr_elements/cr_grid/cr_grid.js';
import 'chrome://resources/cr_elements/cr_icons.css.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
-import './color.js';
import {SpHeading} from 'chrome://customize-chrome-side-panel.top-chrome/shared/sp_heading.js';
+import {ThemeColorPickerBrowserProxy} from 'chrome://resources/cr_components/theme_color_picker/browser_proxy.js';
+import {Color, ColorType, DARK_DEFAULT_COLOR, LIGHT_DEFAULT_COLOR, SelectedColor} from 'chrome://resources/cr_components/theme_color_picker/color_utils.js';
+import {ThemeColorElement} from 'chrome://resources/cr_components/theme_color_picker/theme_color.js';
+import {ChromeColor, Theme, ThemeColorPickerHandlerInterface} from 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.mojom-webui.js';
import {hexColorToSkColor, skColorToRgba} from 'chrome://resources/js/color_utils.js';
import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
+import {BrowserColorVariant} from 'chrome://resources/mojo/ui/base/mojom/themes.mojom-webui.js';
import {DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './chrome_colors.html.js';
-import {ColorElement} from './color.js';
-import {Color, ColorType, DARK_DEFAULT_COLOR, LIGHT_DEFAULT_COLOR, SelectedColor} from './color_utils.js';
-import {ChromeColor, CustomizeChromePageHandlerInterface, Theme} from './customize_chrome.mojom-webui.js';
-import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js';
export interface ChromeColorsElement {
$: {
colorPicker: HTMLInputElement,
colorPickerIcon: HTMLElement,
- defaultColor: ColorElement,
- customColor: ColorElement,
+ defaultColor: ThemeColorElement,
+ customColor: ThemeColorElement,
customColorContainer: HTMLElement,
heading: SpHeading,
};
@@ -85,20 +86,24 @@ export class ChromeColorsElement extends PolymerElement {
private customColor_: Color;
private selectedColor_: SelectedColor;
- private pageHandler_: CustomizeChromePageHandlerInterface;
+ private pageHandler_: ThemeColorPickerHandlerInterface;
constructor() {
super();
- this.pageHandler_ = CustomizeChromeApiProxy.getInstance().handler;
- this.pageHandler_.getChromeColors().then(({colors}) => {
- this.colors_ = colors;
- });
+ this.pageHandler_ = ThemeColorPickerBrowserProxy.getInstance().handler;
+ this.pageHandler_
+ .getChromeColors(
+ /* isDarkMode (unimportant for this component) */ false,
+ /* extendedList */ true)
+ .then(({colors}) => {
+ this.colors_ = colors;
+ });
}
override connectedCallback() {
super.connectedCallback();
this.setThemeListenerId_ =
- CustomizeChromeApiProxy.getInstance()
+ ThemeColorPickerBrowserProxy.getInstance()
.callbackRouter.setTheme.addListener((theme: Theme) => {
this.theme_ = theme;
});
@@ -108,7 +113,7 @@ export class ChromeColorsElement extends PolymerElement {
override disconnectedCallback() {
super.disconnectedCallback();
- CustomizeChromeApiProxy.getInstance().callbackRouter.removeListener(
+ ThemeColorPickerBrowserProxy.getInstance().callbackRouter.removeListener(
this.setThemeListenerId_!);
}
@@ -126,8 +131,8 @@ export class ChromeColorsElement extends PolymerElement {
private computeSelectedColor_(): SelectedColor {
// None will be considered selected if it isn't classic chrome.
- if (!this.colors_ || !this.theme_ || this.theme_.backgroundImage ||
- this.theme_.thirdPartyThemeInfo) {
+ if (!this.colors_ || !this.theme_ || this.theme_.hasBackgroundImage ||
+ this.theme_.hasThirdPartyTheme) {
return {type: ColorType.NONE};
}
if (!this.theme_.foregroundColor) {
@@ -171,7 +176,8 @@ export class ChromeColorsElement extends PolymerElement {
}
private onChromeColorClick_(e: DomRepeatEvent<ChromeColor>) {
- this.pageHandler_.setSeedColor(e.model.item.seed);
+ this.pageHandler_.setSeedColor(
+ e.model.item.seed, BrowserColorVariant.kTonalSpot);
this.pageHandler_.removeBackgroundImage();
}
@@ -182,7 +188,8 @@ export class ChromeColorsElement extends PolymerElement {
private onCustomColorChange_(e: Event) {
this.pageHandler_.setSeedColor(
- hexColorToSkColor((e.target as HTMLInputElement).value));
+ hexColorToSkColor((e.target as HTMLInputElement).value),
+ BrowserColorVariant.kTonalSpot);
this.pageHandler_.removeBackgroundImage();
}
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/color.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/color.html
deleted file mode 100644
index 7ccf9612c9d..00000000000
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/color.html
+++ /dev/null
@@ -1,87 +0,0 @@
-<style>
- :host {
- --customize-chrome-color-border-radius: 50%;
- --customize-chrome-color-check-mark-end: -1px;
- --customize-chrome-color-check-mark-size: 16px;
- --customize-chrome-color-check-mark-top: 0;
- box-sizing: border-box;
- cursor: pointer;
- display: block;
- outline: none;
- padding: 8px;
- width: 100%;
- }
-
- :host(:focus):host-context(.focus-outline-visible) {
- box-shadow: 0 0 0 2px var(--cr-focus-outline-color);
- }
-
- svg {
- border-radius: var(--customize-chrome-color-border-radius);
- box-sizing: border-box;
- display: block;
- width: 100%;
- }
-
- customize-chrome-check-mark-wrapper {
- --customize-chrome-check-mark-wrapper-end: var(--customize-chrome-color-check-mark-end);
- --customize-chrome-check-mark-wrapper-size: var(--customize-chrome-color-check-mark-size);
- --customize-chrome-check-mark-wrapper-top: var(--customize-chrome-color-check-mark-top);
- }
-
- :host-context([chrome-refresh-2023]) customize-chrome-check-mark-wrapper {
- --customize-chrome-check-mark-wrapper-background: var(
- --color-side-panel-customize-chrome-color-picker-checkmark-background);
- --customize-chrome-check-mark-wrapper-color: var(
- --color-side-panel-customize-chrome-color-picker-checkmark-foreground);
- }
-
- #background {
- fill: var(--customize-chrome-color-background-color);
- }
-
- :host(:not([checked])) svg {
- border: 1px solid var(--customize-chrome-color-border-color,
- var(--customize-chrome-color-foreground-color));
- }
-
- :host([background-color-hidden]) #background {
- display: none;
- }
-
- #foreground {
- fill: var(--customize-chrome-color-foreground-color);
- }
-
- #base {
- display: none;
- fill: var(--customize-chrome-color-base-color);
- }
-
- /* Set styles for high contrast mode in Windows. */
- @media (forced-colors: active) {
- :host(:focus):host-context(.focus-outline-visible) {
- /* Set outline to hcm (high contrast mode) value. */
- outline: var(--cr-focus-outline-hcm) !important;
- }
- }
-
- :host([basic-color]):host-context([chrome-refresh-2023]) svg {
- border: none;
- }
-
- :host([basic-color]):host(:not([background-color-hidden])):host-context([chrome-refresh-2023]) #base {
- display: block;
- }
-</style>
-<customize-chrome-check-mark-wrapper checked="[[checked]]">
- <svg viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink">
- <rect id="foreground" x="0" y="0" width="50" height="50">
- </rect>
- <rect id="background" x="0" y="25" width="50" height="25">
- </rect>
- <rect id="base" x="25" y="25" width="25" height="25">
- </rect>
- </svg>
-</customize-chrome-check-mark-wrapper>
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/color.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/color.ts
deleted file mode 100644
index 97a155c17cb..00000000000
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/color.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import './check_mark_wrapper.js';
-
-import {skColorToRgba} from 'chrome://resources/js/color_utils.js';
-import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js';
-import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {getTemplate} from './color.html.js';
-
-export interface ColorElement {
- $: {
- background: Element,
- foreground: Element,
- };
-}
-
-export class ColorElement extends PolymerElement {
- static get is() {
- return 'customize-chrome-color';
- }
-
- static get template() {
- return getTemplate();
- }
-
- static get properties() {
- return {
- backgroundColor: {
- type: Object,
- value: 0,
- observer: 'onColorChange_',
- },
- foregroundColor: {
- type: Object,
- value: 0,
- observer: 'onColorChange_',
- },
- baseColor: {
- type: Object,
- value: 0,
- observer: 'onColorChange_',
- },
- checked: {
- type: Boolean,
- reflectToAttribute: true,
- },
- backgroundColorHidden: {
- type: Boolean,
- reflectToAttribute: true,
- },
- };
- }
-
- public backgroundColor: SkColor;
- public foregroundColor: SkColor;
- public baseColor: SkColor;
- public checked: boolean;
- public backgroundColorHidden: boolean;
-
- override connectedCallback() {
- super.connectedCallback();
- FocusOutlineManager.forDocument(document);
- }
-
- private onColorChange_() {
- this.updateStyles({
- '--customize-chrome-color-foreground-color':
- skColorToRgba(this.foregroundColor ?? 0),
- '--customize-chrome-color-background-color':
- skColorToRgba(this.backgroundColor ?? 0),
- '--customize-chrome-color-base-color': skColorToRgba(this.baseColor ?? 0),
- });
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'customize-chrome-color': ColorElement;
- }
-}
-
-customElements.define(ColorElement.is, ColorElement);
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/color_utils.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/color_utils.ts
deleted file mode 100644
index 47f3254a0f7..00000000000
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/color_utils.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
-
-export interface Color {
- background: SkColor;
- foreground: SkColor;
- base?: SkColor;
-}
-
-export const LIGHT_DEFAULT_COLOR: Color = {
- background: {value: 0xffffffff},
- foreground: {value: 0xffdee1e6},
- base: {value: 0},
-};
-
-export const DARK_DEFAULT_COLOR: Color = {
- background: {value: 0xff323639},
- foreground: {value: 0xff202124},
- base: {value: 0},
-};
-
-export const LIGHT_BASELINE_BLUE_COLOR: Color = {
- background: {value: 0xff0b57d0},
- foreground: {value: 0xffd3e3fd},
- base: {value: 0xffc7c7c7},
-};
-
-export const DARK_BASELINE_BLUE_COLOR: Color = {
- background: {value: 0xffa8c7fa},
- foreground: {value: 0xff0842a0},
- base: {value: 0xff757575},
-};
-
-export enum ColorType {
- NONE,
- DEFAULT,
- MAIN,
- CHROME,
- CUSTOM,
-}
-
-export interface SelectedColor {
- type: ColorType;
- chromeColor?: SkColor;
-}
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/colors.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/colors.html
deleted file mode 100644
index eb5e2ac736d..00000000000
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/colors.html
+++ /dev/null
@@ -1,147 +0,0 @@
-<style>
- cr-grid {
- --cr-column-width: 1fr;
- --cr-grid-gap: 8px;
- box-sizing: border-box;
- display: block;
- padding: 0 16px;
- width: 100%;
- }
-
- @media (prefers-color-scheme: dark) {
- #defaultColor {
- --customize-chrome-color-border-color: #323639;
- }
- }
-
- #customColorContainer {
- position: relative;
- }
-
- :host-context([chrome-refresh-2023]) #customColor {
- background: var(
- --color-side-panel-customize-chrome-custom-option-background);
- }
-
- /* colorPicker is placed on top of the theme icon to work around
- https://crbug.com/1162053. */
- #colorPicker {
- border: 0;
- height: 50px;
- left: 0;
- margin: 0;
- opacity: 0;
- padding: 0;
- pointer-events: none;
- position: absolute;
- top: 0;
- width: 50px;
- }
-
- #colorPickerIcon {
- -webkit-mask-image: url(chrome://resources/cr_components/customize_themes/colorize.svg);
- -webkit-mask-repeat: no-repeat;
- -webkit-mask-size: 100%;
- background-color: var(--google-grey-700);
- height: 20px;
- left: calc(50% - 10px);
- pointer-events: none;
- position: absolute;
- top: calc(50% - 10px);
- width: 20px;
- }
-
- :host-context([chrome-refresh-2023]) #colorPickerIcon {
- background-color: var(
- --color-side-panel-customize-chrome-custom-option-foreground);
- }
-
- managed-dialog {
- --cr-dialog-width: min(calc(100% - 32px), 512px);
- }
-
- :host-context([chrome-refresh-2023]) customize-chrome-color {
- background-color: var(
- --color-side-panel-customize-chrome-color-picker-option-background);
- border-radius: 12px;
- }
-</style>
-<!-- TODO(crbug.com/1395210): Make grid adaptive. -->
-<cr-grid columns="4" role="radiogroup"
- aria-label="$i18n{colorsContainerLabel}">
- <template is="dom-if" if="[[!themeHasMainColor_(theme_)]]" restamp>
- <customize-chrome-color
- id="defaultColor"
- background-color="[[defaultColor_.background]]"
- base-color="[[defaultColor_.base]]"
- foreground-color="[[defaultColor_.foreground]]"
- background-color-hidden="[[themeHasBackgroundImage_(theme_)]]"
- title="$i18n{defaultColorName}"
- aria-label="$i18n{defaultColorName}"
- role="radio"
- checked="[[isDefaultColorSelected_]]"
- aria-checked$="[[boolToString_(isDefaultColorSelected_)]]"
- tabindex$="[[tabIndex_(isDefaultColorSelected_)]]"
- on-click="onDefaultColorClick_"
- basic-color>
- </customize-chrome-color>
- </template>
- <template is="dom-if" if="[[themeHasMainColor_(theme_)]]" restamp>
- <customize-chrome-color
- id="mainColor"
- foreground-color="[[mainColor_]]"
- background-color-hidden
- title="$i18n{mainColorName}"
- aria-label="$i18n{mainColorName}"
- role="radio"
- checked="[[isMainColorSelected_]]"
- aria-checked$="[[boolToString_(isMainColorSelected_)]]"
- tabindex$="[[tabIndex_(isMainColorSelected_)]]"
- on-click="onMainColorClick_"
- basic-color>
- </customize-chrome-color>
- </template>
- <template id="chromeColors" is="dom-repeat" items="[[colors_]]">
- <customize-chrome-color
- class="chrome-color"
- background-color="[[item.background]]"
- base-color="[[item.base]]"
- foreground-color="[[item.foreground]]"
- background-color-hidden="[[themeHasBackgroundImage_(theme_)]]"
- title="[[item.name]]"
- aria-label$="[[item.name]]"
- role="radio"
- checked="[[isChromeColorSelected_(item.seed, selectedColor_)]]"
- aria-checked$=
- "[[getChromeColorCheckedStatus_(item.seed, selectedColor_)]]"
- tabindex$="[[chromeColorTabIndex_(item.seed, selectedColor_)]]"
- on-click="onChromeColorClick_"
- basic-color>
- </customize-chrome-color>
- </template>
- <div id="customColorContainer"
- title="$i18n{colorPickerLabel}"
- aria-label="$i18n{colorPickerLabel}"
- role="radio"
- aria-checked$="[[boolToString_(isCustomColorSelected_)]]"
- tabindex$="[[tabIndex_(isCustomColorSelected_)]]"
- on-click="onCustomColorClick_">
- <customize-chrome-color
- id="customColor"
- background-color="[[customColor_.background]]"
- foreground-color="[[customColor_.foreground]]"
- background-color-hidden="[[themeHasBackgroundImage_(theme_)]]"
- checked="[[isCustomColorSelected_]]"
- basic-color>
- </customize-chrome-color>
- <div id="colorPickerIcon"></div>
- <input id="colorPicker" type="color" tabindex="-1" aria-hidden="true"
- on-change="onCustomColorChange_">
- </div>
-</cr-grid>
-<template is="dom-if" if="[[showManagedDialog_]]" restamp>
- <managed-dialog on-close="onManagedDialogClosed_"
- title="$i18n{managedColorsTitle}"
- body="$i18n{managedColorsBody}">
- </managed-dialog>
-</template>
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/colors.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/colors.ts
deleted file mode 100644
index e46c369b227..00000000000
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/colors.ts
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/cr_elements/cr_grid/cr_grid.js';
-import './color.js';
-import 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
-import './strings.m.js'; // Required by <managed-dialog>.
-
-import {hexColorToSkColor, skColorToRgba} from 'chrome://resources/js/color_utils.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
-import {DomRepeat, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {ColorElement} from './color.js';
-import {Color, ColorType, DARK_BASELINE_BLUE_COLOR, DARK_DEFAULT_COLOR, LIGHT_BASELINE_BLUE_COLOR, LIGHT_DEFAULT_COLOR, SelectedColor} from './color_utils.js';
-import {getTemplate} from './colors.html.js';
-import {ChromeColor, Theme} from './customize_chrome.mojom-webui.js';
-import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js';
-
-export interface ColorsElement {
- $: {
- chromeColors: DomRepeat,
- customColorContainer: HTMLElement,
- customColor: ColorElement,
- colorPicker: HTMLInputElement,
- colorPickerIcon: HTMLElement,
- };
-}
-
-export class ColorsElement extends PolymerElement {
- static get is() {
- return 'customize-chrome-colors';
- }
-
- static get template() {
- return getTemplate();
- }
-
- static get properties() {
- return {
- defaultColor_: {
- type: Object,
- computed: 'computeDefaultColor_(theme_)',
- },
- mainColor_: {
- type: Object,
- computed: 'computeMainColor_(theme_)',
- },
- colors_: Array,
- theme_: Object,
- selectedColor_: {
- type: Object,
- computed: 'computeSelectedColor_(theme_, colors_)',
- },
- isDefaultColorSelected_: {
- type: Object,
- computed: 'computeIsDefaultColorSelected_(selectedColor_)',
- },
- isMainColorSelected_: {
- type: Object,
- computed: 'computeIsMainColorSelected_(selectedColor_)',
- },
- isCustomColorSelected_: {
- type: Object,
- computed: 'computeIsCustomColorSelected_(selectedColor_)',
- },
- customColor_: {
- type: Object,
- value: {
- background: {value: 0xffffffff},
- foreground: {value: 0xfff1f3f4},
- },
- },
- showManagedDialog_: Boolean,
- };
- }
-
- static get observers() {
- return [
- 'updateCustomColor_(colors_, theme_, isCustomColorSelected_)',
- 'updateColors_(theme_)',
- ];
- }
-
- private colors_: ChromeColor[];
- private theme_: Theme;
- private selectedColor_: SelectedColor;
- private isCustomColorSelected_: boolean;
- private customColor_: Color;
- private setThemeListenerId_: number|null = null;
- private showManagedDialog_: boolean;
-
- override connectedCallback() {
- super.connectedCallback();
- this.setThemeListenerId_ =
- CustomizeChromeApiProxy.getInstance()
- .callbackRouter.setTheme.addListener((theme: Theme) => {
- this.theme_ = theme;
- });
- CustomizeChromeApiProxy.getInstance().handler.updateTheme();
- }
-
- override disconnectedCallback() {
- super.disconnectedCallback();
- CustomizeChromeApiProxy.getInstance().callbackRouter.removeListener(
- this.setThemeListenerId_!);
- }
-
- private computeDefaultColor_(): Color {
- if (loadTimeData.getString('chromeRefresh2023Attribute')) {
- return this.theme_.isDarkMode ? DARK_BASELINE_BLUE_COLOR :
- LIGHT_BASELINE_BLUE_COLOR;
- }
- return this.theme_.isDarkMode ? DARK_DEFAULT_COLOR : LIGHT_DEFAULT_COLOR;
- }
-
- private computeMainColor_(): SkColor|undefined {
- return this.theme_ && this.theme_.backgroundImage &&
- this.theme_.backgroundImage.mainColor;
- }
-
- private computeSelectedColor_(): SelectedColor {
- if (!this.colors_ || !this.theme_) {
- return {type: ColorType.NONE};
- }
- if (!this.theme_.foregroundColor) {
- return {type: ColorType.DEFAULT};
- }
- if (this.theme_.backgroundImage && this.theme_.backgroundImage.mainColor &&
- this.theme_.backgroundImage.mainColor!.value ===
- this.theme_.seedColor.value) {
- return {type: ColorType.MAIN};
- }
- if (this.colors_.find(
- (color: ChromeColor) =>
- color.seed.value === this.theme_.seedColor.value)) {
- return {
- type: ColorType.CHROME,
- chromeColor: this.theme_.seedColor,
- };
- }
- return {type: ColorType.CUSTOM};
- }
-
- private computeIsDefaultColorSelected_(): boolean {
- return this.selectedColor_.type === ColorType.DEFAULT;
- }
-
- private computeIsMainColorSelected_(): boolean {
- return this.selectedColor_.type === ColorType.MAIN;
- }
-
- private computeIsCustomColorSelected_(): boolean {
- return this.selectedColor_.type === ColorType.CUSTOM;
- }
-
- private isChromeColorSelected_(color: SkColor): boolean {
- return this.selectedColor_.type === ColorType.CHROME &&
- this.selectedColor_.chromeColor!.value === color.value;
- }
-
- private boolToString_(value: boolean): string {
- return value ? 'true' : 'false';
- }
-
- private getChromeColorCheckedStatus_(color: SkColor): string {
- return this.boolToString_(this.isChromeColorSelected_(color));
- }
-
- private tabIndex_(selected: boolean): string {
- return selected ? '0' : '-1';
- }
-
- private chromeColorTabIndex_(color: SkColor): string {
- return this.selectedColor_.type === ColorType.CHROME &&
- this.selectedColor_.chromeColor!.value === color.value ?
- '0' :
- '-1';
- }
-
- private themeHasBackgroundImage_(): boolean {
- return !!this.theme_ && !!this.theme_.backgroundImage;
- }
-
- private themeHasMainColor_(): boolean {
- return !!this.theme_ && !!this.theme_.backgroundImage &&
- !!this.theme_.backgroundImage.mainColor;
- }
-
- private onDefaultColorClick_() {
- if (this.handleClickForManagedColors_()) {
- return;
- }
- CustomizeChromeApiProxy.getInstance().handler.setDefaultColor();
- }
-
- private onMainColorClick_() {
- if (this.handleClickForManagedColors_()) {
- return;
- }
- CustomizeChromeApiProxy.getInstance().handler.setSeedColor(
- this.theme_!.backgroundImage!.mainColor!);
- }
-
- private onChromeColorClick_(e: Event) {
- if (this.handleClickForManagedColors_()) {
- return;
- }
- CustomizeChromeApiProxy.getInstance().handler.setSeedColor(
- this.$.chromeColors.itemForElement(e.target as HTMLElement).seed);
- }
-
- private onCustomColorClick_() {
- if (this.handleClickForManagedColors_()) {
- return;
- }
- this.$.colorPicker.focus();
- this.$.colorPicker.click();
- }
-
- private onCustomColorChange_(e: Event) {
- CustomizeChromeApiProxy.getInstance().handler.setSeedColor(
- hexColorToSkColor((e.target as HTMLInputElement).value));
- }
-
- private updateCustomColor_() {
- // We only change the custom color when theme updates to a new custom color
- // so that the picked color persists while clicking on other color circles.
- if (!this.isCustomColorSelected_) {
- return;
- }
- this.customColor_ = {
- background: this.theme_.backgroundColor,
- foreground: this.theme_.foregroundColor!,
- };
- this.$.colorPickerIcon.style.setProperty(
- 'background-color', skColorToRgba(this.theme_.colorPickerIconColor));
- }
-
- private updateColors_() {
- CustomizeChromeApiProxy.getInstance()
- .handler.getOverviewChromeColors(this.theme_.isDarkMode)
- .then(({colors}) => {
- this.colors_ = colors;
- });
- }
-
- private onManagedDialogClosed_() {
- this.showManagedDialog_ = false;
- }
-
- private handleClickForManagedColors_(): boolean {
- if (!this.theme_ || !this.theme_.colorsManagedByPolicy) {
- return false;
- }
- this.showManagedDialog_ = true;
- return true;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'customize-chrome-colors': ColorsElement;
- }
-}
-
-customElements.define(ColorsElement.is, ColorsElement);
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/hover_button.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/hover_button.html
index 229a576b775..477c9d1d84a 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/hover_button.html
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/hover_button.html
@@ -29,6 +29,21 @@
margin: 0;
width: 18px;
}
+
+ :host-context([chrome-refresh-2023]) #hoverButton {
+ line-height: 16px;
+ padding: 8px 16px;
+ }
+
+ :host-context([chrome-refresh-2023]) #icon {
+ --cr-icon-size: 20px;
+ height: 20px;
+ width: 20px;
+ }
+
+ :host-context([chrome-refresh-2023]) customize-chrome-button-label {
+ align-self: center;
+ }
</style>
<div id="hoverButton" role="button" tabindex="0">
<customize-chrome-button-label label="[[label]]"
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/BUILD.gn b/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/BUILD.gn
index 3e244399f8e..d71a6647b5c 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/BUILD.gn
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/BUILD.gn
@@ -12,6 +12,7 @@ generate_grd("build_grdp") {
input_files = [
"chrome_web_store.svg",
"corner_new_tab_page.svg",
+ "gm3_corner_new_tab_page.svg",
"gm3_mini_new_tab_page.svg",
"image.svg",
"mini_new_tab_page.svg",
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_corner_new_tab_page.svg b/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_corner_new_tab_page.svg
new file mode 100644
index 00000000000..bb20a450d40
--- /dev/null
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_corner_new_tab_page.svg
@@ -0,0 +1 @@
+<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path fill="#D3E3FD" d="M0 0h128v128H0z"/><path fill="#fff" d="M0 36h128v92H0z"/><path d="M0 20.48a3.413 3.413 0 0 1 3.413-3.413H128v19.2H0V20.48Z" fill="#fff"/><g clip-path="url(#b)"><path d="m9.388 29.729-2.596-2.596 2.596-2.595.524.524-1.697 1.698h3.769v.747h-3.77l1.698 1.697-.524.525Z" fill="#474747"/></g><path d="m24.747 29.729-.525-.525 1.698-1.697h-3.769v-.747h3.77l-1.699-1.698.525-.524 2.595 2.595-2.595 2.596Z" fill="#474747"/><g clip-path="url(#c)"><path d="M40.097 29.764A2.559 2.559 0 0 1 38.23 29a2.559 2.559 0 0 1-.764-1.867c0-.729.255-1.348.764-1.857a2.544 2.544 0 0 1 1.867-.774c.397 0 .764.083 1.102.249.338.16.628.38.871.658v-.907h.676v2.24h-2.24v-.675h1.155a1.868 1.868 0 0 0-1.564-.827c-.528 0-.975.184-1.342.551a1.827 1.827 0 0 0-.551 1.342c0 .528.183.975.55 1.343.368.367.816.55 1.343.55.492 0 .913-.16 1.262-.48a1.88 1.88 0 0 0 .605-1.2h.755a2.55 2.55 0 0 1-.835 1.725 2.535 2.535 0 0 1-1.787.693Z" fill="#474747"/></g><path d="M51.2 26.55a7.33 7.33 0 0 1 7.33-7.33H128v14.66H58.53a7.33 7.33 0 0 1-7.33-7.33Z" fill="#FDFCFB"/><path d="M51.2 26.55a7.33 7.33 0 0 1 7.33-7.33H128v14.66H58.53a7.33 7.33 0 0 1-7.33-7.33Z" fill="#6991D6" fill-opacity=".12"/><path d="M0 3.413A3.413 3.413 0 0 1 3.413 0H128v17.067H0V3.413Z" fill="#D3E3FD"/><rect x="2.56" y="2.56" width="11.947" height="11.947" rx="4.267" fill="#ECF3FE"/><g clip-path="url(#d)"><path d="M10.24 8.107 8.533 9.813 6.827 8.107l.452-.452 1.254 1.254 1.255-1.254.452.452Z" fill="#041E49"/></g><path d="M17.067 6.827a4.267 4.267 0 0 1 4.266-4.267h85.334a4.266 4.266 0 0 1 4.266 4.267v10.24H17.067V6.827Z" fill="#fff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M17.067 11.947v5.12h-5.12a5.12 5.12 0 0 0 5.12-5.12Zm93.885 0h-.019v5.12h5.12a5.12 5.12 0 0 1-5.101-5.12Z" fill="#fff"/><g clip-path="url(#e)"><g clip-path="url(#f)"><path d="M22.72 8.533c0 .324.115.597.345.828.23.23.508.345.828.345.32 0 .597-.115.828-.345.23-.23.345-.508.345-.828 0-.32-.115-.597-.345-.828a1.131 1.131 0 0 0-.828-.345c-.32 0-.597.115-.828.345-.23.23-.345.508-.345.828Zm1.173 1.813c.064 0 .128 0 .188-.008.06-.004.12-.017.179-.03l-.7 1.212a2.919 2.919 0 0 1-1.89-.964 2.889 2.889 0 0 1-.764-2.002c0-.209.022-.41.06-.605.038-.201.098-.389.18-.568l1.194 2.07c.154.268.367.486.64.648.273.162.576.247.913.247Zm0-3.584a1.7 1.7 0 0 0-1.058.35c-.307.23-.525.525-.645.883l-.704-1.211A3.088 3.088 0 0 1 22.52 5.9a2.836 2.836 0 0 1 1.374-.338c.5 0 .947.111 1.361.329.414.213.755.503 1.032.866h-2.389l-.004.004Zm2.735.598a2.992 2.992 0 0 1 .256 1.194c0 .773-.252 1.438-.764 1.993a2.903 2.903 0 0 1-1.864.968l1.194-2.069c.077-.128.133-.269.17-.418.044-.154.07-.316.07-.478 0-.235-.044-.452-.129-.648a1.8 1.8 0 0 0-.337-.547h1.404v.005Z" fill="#1F1F1F"/></g></g><path fill="#E1E3E1" d="M0 36.565h128v-.425H0z"/></g><defs><clipPath id="a"><path d="M0 3a3 3 0 0 1 3-3h125v128H0V3Z" fill="#fff"/></clipPath><clipPath id="b"><path fill="#fff" transform="translate(5.12 22.4)" d="M0 0h8.533v8.533H0z"/></clipPath><clipPath id="c"><path fill="#fff" transform="translate(35.84 22.4)" d="M0 0h8.533v8.533H0z"/></clipPath><clipPath id="d"><path fill="#fff" transform="rotate(-90 8.533 3.414)" d="M0 0h6.827v6.827H0z"/></clipPath><clipPath id="e"><path fill="#fff" transform="translate(20.48 5.12)" d="M0 0h6.827v6.827H0z"/></clipPath><clipPath id="f"><path fill="#fff" transform="translate(20.48 5.12)" d="M0 0h6.827v6.827H0z"/></clipPath></defs></svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_mini_new_tab_page.svg b/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_mini_new_tab_page.svg
index 929599676ef..83221a8e6f9 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_mini_new_tab_page.svg
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_mini_new_tab_page.svg
@@ -1 +1 @@
-<svg width="240" height="126" viewBox="0 0 240 126" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path fill="#fff" d="M0 0h240v126H0z"/><path d="M0 0h240v40H0V0Z" fill="#D3E3FD"/><rect x="6" y="6" width="28" height="28" rx="10" fill="#ECF3FE"/><g clip-path="url(#b)"><path d="m24 19-4 4-4-4 1.06-1.06L20 20.88l2.94-2.94L24 19Z" fill="#041E49"/></g><path fill="#fff" d="M0 86h240v40H0zm0-38a8 8 0 0 1 8-8h224a8 8 0 0 1 8 8v38H0V48Z"/><g clip-path="url(#c)"><path d="M22 69.083 15.917 63 22 56.917l1.23 1.229-3.98 3.979h8.833v1.75H19.25l3.98 3.98L22 69.082Z" fill="#474747"/></g><g clip-path="url(#d)"><path d="m58 69.083-1.23-1.229 3.98-3.979h-8.833v-1.75h8.833l-3.98-3.98L58 56.918 64.083 63 58 69.083Z" fill="#474747"/></g><g clip-path="url(#e)"><path d="M93.98 69.167c-1.71 0-3.168-.598-4.376-1.792-1.194-1.208-1.791-2.667-1.791-4.375 0-1.708.597-3.16 1.791-4.354 1.209-1.209 2.667-1.813 4.375-1.813.93 0 1.792.195 2.584.584a6.203 6.203 0 0 1 2.041 1.541v-2.125h1.584v5.25h-5.25V60.5h2.708a4.378 4.378 0 0 0-3.667-1.938c-1.236 0-2.285.431-3.146 1.292-.86.861-1.291 1.91-1.291 3.146s.43 2.285 1.291 3.146c.861.86 1.91 1.291 3.146 1.291 1.153 0 2.14-.374 2.959-1.124a4.404 4.404 0 0 0 1.416-2.813h1.771c-.125 1.597-.778 2.944-1.958 4.042-1.167 1.083-2.563 1.625-4.188 1.625Z" fill="#474747"/></g><path d="M120 63c0-7.18 5.82-13 13-13h107v26H133c-7.18 0-13-5.82-13-13Z" fill="#FDFCFB"/><path d="M120 63c0-7.18 5.82-13 13-13h107v26H133c-7.18 0-13-5.82-13-13Z" fill="#6991D6" fill-opacity=".12"/><path d="M40 16c0-5.523 4.477-10 10-10h190v34H40V16Z" fill="#fff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M40 28v12H28c6.627 0 12-5.373 12-12Z" fill="#fff"/><g clip-path="url(#f)"><g clip-path="url(#g)"><path d="M53.25 20c0 .76.27 1.4.81 1.94s1.19.81 1.94.81 1.4-.27 1.94-.81.81-1.19.81-1.94-.27-1.4-.81-1.94-1.19-.81-1.94-.81-1.4.27-1.94.81-.81 1.19-.81 1.94ZM56 24.25c.15 0 .3 0 .44-.02.14-.01.28-.04.42-.07L55.22 27c-1.76-.2-3.24-.95-4.43-2.26C49.6 23.42 49 21.86 49 20.05c0-.49.05-.96.14-1.42.09-.47.23-.91.42-1.33l2.8 4.85c.36.63.86 1.14 1.5 1.52.64.38 1.35.58 2.14.58Zm0-8.4c-.93 0-1.76.27-2.48.82-.72.54-1.23 1.23-1.51 2.07l-1.65-2.84c.64-.86 1.45-1.55 2.42-2.07.98-.53 2.05-.79 3.22-.79 1.17 0 2.22.26 3.19.77.97.5 1.77 1.18 2.42 2.03h-5.6l-.01.01Zm6.41 1.4c.2.43.34.88.44 1.35.1.47.16.95.16 1.45 0 1.81-.59 3.37-1.79 4.67-1.18 1.31-2.64 2.06-4.37 2.27l2.8-4.85c.18-.3.31-.63.4-.98.1-.36.16-.74.16-1.12 0-.55-.1-1.06-.3-1.52-.19-.48-.45-.9-.79-1.28h3.29v.01Z" fill="#1F1F1F"/></g></g><path fill="#E1E3E1" d="M0 86h240v-1H0z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h240v126H0z"/></clipPath><clipPath id="b"><path fill="#fff" transform="rotate(-90 20 8)" d="M0 0h16v16H0z"/></clipPath><clipPath id="c"><path fill="#fff" transform="translate(12 53)" d="M0 0h20v20H0z"/></clipPath><clipPath id="d"><path fill="#fff" transform="translate(48 53)" d="M0 0h20v20H0z"/></clipPath><clipPath id="e"><path fill="#fff" transform="translate(84 53)" d="M0 0h20v20H0z"/></clipPath><clipPath id="f"><path fill="#fff" transform="translate(48 12)" d="M0 0h16v16H0z"/></clipPath><clipPath id="g"><path fill="#fff" transform="translate(48 12)" d="M0 0h16v16H0z"/></clipPath></defs></svg> \ No newline at end of file
+<svg id="miniNewTabPage" width="240" height="126" viewBox="0 0 240 126" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path fill="var(--color-side-panel-customize-chrome-mini-ntp-background)" d="M0 0h240v126H0z"/><path d="M0 0h240v40H0V0Z" fill="var(--color-side-panel-customize-chrome-mini-ntp-tab-strip-background)"/><rect x="6" y="6" width="28" height="28" rx="10" fill="var(--color-side-panel-customize-chrome-mini-ntp-caron-container)"/><g clip-path="url(#b)"><path d="m24 19-4 4-4-4 1.06-1.06L20 20.88l2.94-2.94L24 19Z" fill="var(--color-side-panel-customize-chrome-mini-ntp-caron)"/></g><path fill="var(--color-side-panel-customize-chrome-mini-ntp-background)" d="M0 86h240v40H0zm0-38a8 8 0 0 1 8-8h224a8 8 0 0 1 8 8v38H0V48Z"/><g clip-path="url(#c)"><path d="M22 69.083 15.917 63 22 56.917l1.23 1.229-3.98 3.979h8.833v1.75H19.25l3.98 3.98L22 69.082Z" fill="var(--color-side-panel-customize-chrome-mini-ntp-arrows-and-refresh-button)"/></g><g clip-path="url(#d)"><path d="m58 69.083-1.23-1.229 3.98-3.979h-8.833v-1.75h8.833l-3.98-3.98L58 56.918 64.083 63 58 69.083Z" fill="var(--color-side-panel-customize-chrome-mini-ntp-arrows-and-refresh-button)"/></g><g clip-path="url(#e)"><path d="M93.98 69.167c-1.71 0-3.168-.598-4.376-1.792-1.194-1.208-1.791-2.667-1.791-4.375 0-1.708.597-3.16 1.791-4.354 1.209-1.209 2.667-1.813 4.375-1.813.93 0 1.792.195 2.584.584a6.203 6.203 0 0 1 2.041 1.541v-2.125h1.584v5.25h-5.25V60.5h2.708a4.378 4.378 0 0 0-3.667-1.938c-1.236 0-2.285.431-3.146 1.292-.86.861-1.291 1.91-1.291 3.146s.43 2.285 1.291 3.146c.861.86 1.91 1.291 3.146 1.291 1.153 0 2.14-.374 2.959-1.124a4.404 4.404 0 0 0 1.416-2.813h1.771c-.125 1.597-.778 2.944-1.958 4.042-1.167 1.083-2.563 1.625-4.188 1.625Z" fill="var(--color-side-panel-customize-chrome-mini-ntp-arrows-and-refresh-button)"/></g><path d="M120 63c0-7.18 5.82-13 13-13h107v26H133c-7.18 0-13-5.82-13-13Z" fill="var(--color-side-panel-customize-chrome-mini-ntp-omnibox)"/><path d="M40 16c0-5.523 4.477-10 10-10h190v34H40V16Z" fill="var(--color-side-panel-customize-chrome-mini-ntp-background)"/><path fill-rule="evenodd" clip-rule="evenodd" d="M40 28v12H28c6.627 0 12-5.373 12-12Z" fill="var(--color-side-panel-customize-chrome-mini-ntp-background)"/><g clip-path="url(#f)"><g clip-path="url(#g)"><path d="M53.25 20c0 .76.27 1.4.81 1.94s1.19.81 1.94.81 1.4-.27 1.94-.81.81-1.19.81-1.94-.27-1.4-.81-1.94-1.19-.81-1.94-.81-1.4.27-1.94.81-.81 1.19-.81 1.94ZM56 24.25c.15 0 .3 0 .44-.02.14-.01.28-.04.42-.07L55.22 27c-1.76-.2-3.24-.95-4.43-2.26C49.6 23.42 49 21.86 49 20.05c0-.49.05-.96.14-1.42.09-.47.23-.91.42-1.33l2.8 4.85c.36.63.86 1.14 1.5 1.52.64.38 1.35.58 2.14.58Zm0-8.4c-.93 0-1.76.27-2.48.82-.72.54-1.23 1.23-1.51 2.07l-1.65-2.84c.64-.86 1.45-1.55 2.42-2.07.98-.53 2.05-.79 3.22-.79 1.17 0 2.22.26 3.19.77.97.5 1.77 1.18 2.42 2.03h-5.6l-.01.01Zm6.41 1.4c.2.43.34.88.44 1.35.1.47.16.95.16 1.45 0 1.81-.59 3.37-1.79 4.67-1.18 1.31-2.64 2.06-4.37 2.27l2.8-4.85c.18-.3.31-.63.4-.98.1-.36.16-.74.16-1.12 0-.55-.1-1.06-.3-1.52-.19-.48-.45-.9-.79-1.28h3.29v.01Z" fill="var(--color-side-panel-customize-chrome-mini-ntp-chrome-logo)"/></g></g><path fill="var(--color-side-panel-customize-chrome-mini-ntp-border)" d="M0 86h240v-1H0z"/></g><defs><clipPath id="a"><path fill="var(--color-side-panel-customize-chrome-mini-ntp-background)" d="M0 0h240v126H0z"/></clipPath><clipPath id="b"><path fill="var(--color-side-panel-customize-chrome-mini-ntp-background)" transform="rotate(-90 20 8)" d="M0 0h16v16H0z"/></clipPath><clipPath id="c"><path fill="var(--color-side-panel-customize-chrome-mini-ntp-background)" transform="translate(12 53)" d="M0 0h20v20H0z"/></clipPath><clipPath id="d"><path fill="var(--color-side-panel-customize-chrome-mini-ntp-background)" transform="translate(48 53)" d="M0 0h20v20H0z"/></clipPath><clipPath id="e"><path fill="var(--color-side-panel-customize-chrome-mini-ntp-background)" transform="translate(84 53)" d="M0 0h20v20H0z"/></clipPath><clipPath id="f"><path fill="var(--color-side-panel-customize-chrome-mini-ntp-background)" transform="translate(48 12)" d="M0 0h16v16H0z"/></clipPath><clipPath id="g"><path fill="var(--color-side-panel-customize-chrome-mini-ntp-background)" transform="translate(48 12)" d="M0 0h16v16H0z"/></clipPath></defs></svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/shortcuts.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/shortcuts.html
index e214570c523..733818ca672 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/shortcuts.html
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/shortcuts.html
@@ -35,6 +35,7 @@
iron-collapse {
--iron-collapse-transition-duration: 300ms;
+ width: 100%;
}
cr-radio-group {
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.html
index c31794781e6..8117511f58b 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.html
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.html
@@ -52,7 +52,8 @@
width: 272px;
}
- :host-context([chrome-refresh-2023]) img {
+ :host-context([chrome-refresh-2023]) img,
+ :host-context([chrome-refresh-2023]) svg {
border: 0;
border-radius: 8px;
box-shadow: var(--cr-elevation-2);
@@ -79,11 +80,11 @@
}
:host-context([chrome-refresh-2023]) .image-background:hover .overlay {
- background-color: var(--cr-fallback-color-state-hover-on-subtle);
+ background-color: var(--cr-hover-on-subtle-background-color);
}
paper-ripple {
- color: var(--cr-fallback-color-state-ripple-neutral-on-subtle);
+ color: var(--cr-active-neutral-on-subtle-background-color);
z-index: 1;
}
@@ -97,7 +98,9 @@
<iron-pages selected="[[themeType_]]" attr-for-selected="theme-type">
<div class="snapshot-container"
theme-type="customTheme">
- <div class="image-background">
+ <div class="image-background"
+ id="customThemeImageBackground"
+ on-click="onThemeSnapshotClick_">
<img class="image" id="customThemeImage" is="cr-auto-img"
auto-src="[[theme_.backgroundImage.snapshotUrl.url]]"
draggable="false"
@@ -112,16 +115,18 @@
</div>
<div class="snapshot-container"
theme-type="classicChrome">
- <div class="image-background image" id="classicChromeBackground">
+ <div class="image-background image"
+ id="classicChromeBackground"
+ on-click="onThemeSnapshotClick_">
<template is="dom-if" if="[[!chromeRefresh2023Enabled_]]">
<img id="miniNewTabPage" src="icons/mini_new_tab_page.svg"
aria-labelledby="classicChromeThemeTitle">
</img>
</template>
<template is="dom-if" if="[[chromeRefresh2023Enabled_]]">
- <img src="icons/gm3_mini_new_tab_page.svg"
- aria-labelledby="classicChromeThemeTitle">
- </img>
+ <svg aria-labelledby="classicChromeThemeTitle">
+ <use href="icons/gm3_mini_new_tab_page.svg#miniNewTabPage"></use>
+ </svg>
</template>
<div class="overlay"></div>
<paper-ripple></paper-ripple>
@@ -132,7 +137,9 @@
</div>
<div class="snapshot-container"
theme-type="uploadedImage">
- <div class="image-background">
+ <div class="image-background"
+ id="uploadedThemeImageBackground"
+ on-click="onThemeSnapshotClick_">
<img class="image" id="uploadedThemeImage"
src="icons/uploaded_image.svg"
aria-labelledby="uploadedThemeTitle">
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.ts
index dc083a3f104..e7d17841694 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.ts
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.ts
@@ -95,6 +95,14 @@ export class ThemeSnapshotElement extends PolymerElement {
}
return null;
}
+
+ private onThemeSnapshotClick_() {
+ if (!this.chromeRefresh2023Enabled_ ||
+ (this.theme_ && this.theme_.backgroundManagedByPolicy)) {
+ return;
+ }
+ this.dispatchEvent(new Event('edit-theme-click'));
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/themes.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/themes.html
index aecd497dcee..513384e64c4 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/themes.html
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/themes.html
@@ -124,7 +124,8 @@
<div class="image-container">
<img is="cr-auto-img"
auto-src="[[item.previewImageUrl.url]]"
- draggable="false">
+ draggable="false"
+ on-load="onPreviewImageLoad_">
</img>
</div>
</customize-chrome-check-mark-wrapper>
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/themes.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/themes.ts
index 146ee80c3fb..5ade0098c5a 100644
--- a/chromium/chrome/browser/resources/side_panel/customize_chrome/themes.ts
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/themes.ts
@@ -22,6 +22,7 @@ import {DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/po
import {BackgroundCollection, CollectionImage, CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerInterface, Theme} from './customize_chrome.mojom-webui.js';
import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js';
import {getTemplate} from './themes.html.js';
+import {WindowProxy} from './window_proxy.js';
export const CHROME_THEME_ELEMENT_ID =
'CustomizeChromeUI::kChromeThemeElementId';
@@ -54,6 +55,7 @@ export class ThemesElement extends ThemesElementBase {
value: null,
observer: 'onCollectionChange_',
},
+ header_: String,
isRefreshToggleChecked_: {
type: Boolean,
computed: `computeIsRefreshToggleChecked_(theme_, selectedCollection)`,
@@ -63,7 +65,6 @@ export class ThemesElement extends ThemesElementBase {
value: undefined,
},
themes_: Array,
- header_: String,
};
}
@@ -73,10 +74,11 @@ export class ThemesElement extends ThemesElementBase {
private isRefreshToggleChecked_: boolean;
private theme_: Theme|undefined;
private themes_: CollectionImage[];
- private setThemeListenerId_: number|null = null;
private callbackRouter_: CustomizeChromePageCallbackRouter;
private pageHandler_: CustomizeChromePageHandlerInterface;
+ private previewImageLoadStartEpoch_: number;
+ private setThemeListenerId_: number|null = null;
constructor() {
super();
@@ -117,10 +119,25 @@ export class ThemesElement extends ThemesElementBase {
}
}
+ private onPreviewImageLoad_() {
+ chrome.metricsPrivate.recordValue(
+ {
+ metricName: 'NewTabPage.Images.ShownTime.ThemePreviewImage',
+ type: chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LOG,
+ min: 1,
+ max: 60000, // 60 seconds.
+ buckets: 100,
+ },
+ Math.floor(
+ WindowProxy.getInstance().now() -
+ this.previewImageLoadStartEpoch_));
+ }
+
private onCollectionChange_() {
this.header_ = '';
this.themes_ = [];
if (this.selectedCollection) {
+ this.previewImageLoadStartEpoch_ = WindowProxy.getInstance().now();
this.pageHandler_.getBackgroundImages(this.selectedCollection!.id)
.then(({images}) => {
this.themes_ = images;
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search.html b/chromium/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search.html
new file mode 100644
index 00000000000..96fd46af941
--- /dev/null
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search.html
@@ -0,0 +1,12 @@
+<style include="sp-shared-style">
+ :host {
+ box-sizing: border-box;
+ padding: 0 16px;
+ }
+</style>
+<cr-input
+ type="text"
+ placeholder="Query"
+ value="{{query_}}">
+</cr-input>
+<cr-button id="submitButton">Search</cr-button> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search.ts
new file mode 100644
index 00000000000..d546935d33e
--- /dev/null
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search.ts
@@ -0,0 +1,37 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://customize-chrome-side-panel.top-chrome/shared/sp_shared_style.css.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.js';
+
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './wallpaper_search.html.js';
+
+export class WallpaperSearchElement extends PolymerElement {
+ static get is() {
+ return 'customize-chrome-wallpaper-search';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ query_: String,
+ };
+ }
+
+ private query_: string;
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'customize-chrome-wallpaper-search': WallpaperSearchElement;
+ }
+}
+
+customElements.define(WallpaperSearchElement.is, WallpaperSearchElement);
diff --git a/chromium/chrome/browser/resources/side_panel/customize_chrome/window_proxy.ts b/chromium/chrome/browser/resources/side_panel/customize_chrome/window_proxy.ts
new file mode 100644
index 00000000000..f575cbea99d
--- /dev/null
+++ b/chromium/chrome/browser/resources/side_panel/customize_chrome/window_proxy.ts
@@ -0,0 +1,28 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+let instance: WindowProxy|null = null;
+
+declare global {
+ interface Window {
+ // https://github.com/microsoft/TypeScript/issues/40807
+ requestIdleCallback(callback: () => void, options?: {timeout: number}):
+ void;
+ }
+}
+
+/** Abstracts some builtin JS functions to mock them in tests. */
+export class WindowProxy {
+ static getInstance(): WindowProxy {
+ return instance || (instance = new WindowProxy());
+ }
+
+ static setInstance(newInstance: WindowProxy) {
+ instance = newInstance;
+ }
+
+ now(): number {
+ return Date.now();
+ }
+}
diff --git a/chromium/chrome/browser/resources/side_panel/history_clusters/BUILD.gn b/chromium/chrome/browser/resources/side_panel/history_clusters/BUILD.gn
index d49180f1422..3915ec58da6 100644
--- a/chromium/chrome/browser/resources/side_panel/history_clusters/BUILD.gn
+++ b/chromium/chrome/browser/resources/side_panel/history_clusters/BUILD.gn
@@ -14,8 +14,16 @@ build_webui("build") {
web_component_files = [ "app.ts" ]
ts_deps = [
+ "../shared:build_ts",
"//third_party/polymer/v3_0:library",
+ "//ui/webui/resources/cr_components/color_change_listener:build_ts",
"//ui/webui/resources/cr_components/history_clusters:build_ts",
"//ui/webui/resources/cr_elements:build_ts",
]
+
+ ts_path_mappings =
+ [ "chrome://history-clusters-side-panel.top-chrome/shared/*|" +
+ rebase_path(
+ "$root_gen_dir/chrome/browser/resources/side_panel/shared/tsc/*",
+ target_gen_dir) ]
}
diff --git a/chromium/chrome/browser/resources/side_panel/history_clusters/app.html b/chromium/chrome/browser/resources/side_panel/history_clusters/app.html
index a9255fc340e..5dbb2656ef4 100644
--- a/chromium/chrome/browser/resources/side_panel/history_clusters/app.html
+++ b/chromium/chrome/browser/resources/side_panel/history_clusters/app.html
@@ -1,15 +1,33 @@
-<style>
+<style include="sp-shared-style">
:host {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ }
+
+ #searchbox {
+ margin: 16px auto 4px;
+ flex-shrink: 0;
+ }
+
+ :host-context(html:not([chrome-refresh-2023])) #searchbox {
--cr-toolbar-field-max-width: calc(100% - 32px);
}
- #history-clusters {
- height: calc(100% - 60px); /* 40px search field + 20px margin */
+ :host-context([chrome-refresh-2023]) #searchbox {
+ margin: var(--sp-body-padding);
+ width: auto;
}
- #searchbox {
- margin: 16px auto 4px;
+ #history-clusters {
+ flex: 1;
}
+
+ :host-context([chrome-refresh-2023]) #history-clusters {
+ margin-inline-start: var(--sp-body-padding);
+ margin-bottom: var(--sp-body-padding);
+ }
+
</style>
<cr-toolbar-search-field id="searchbox" on-search-changed="onSearchChanged_"
label="$i18n{historyClustersSearchPrompt}"
@@ -19,5 +37,5 @@
<history-clusters id="history-clusters"
query="[[query]]"
path="journeys"
- on-query-changed-by-user="onQueryChangedByUser_">
+ on-query-changed-by-user="onQueryChangedByUser_" class="sp-scroller">
</history-clusters> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/side_panel/history_clusters/app.ts b/chromium/chrome/browser/resources/side_panel/history_clusters/app.ts
index 95aee3d2cdd..188aaad5b42 100644
--- a/chromium/chrome/browser/resources/side_panel/history_clusters/app.ts
+++ b/chromium/chrome/browser/resources/side_panel/history_clusters/app.ts
@@ -3,10 +3,12 @@
// found in the LICENSE file.
import '../strings.m.js';
+import 'chrome://history-clusters-side-panel.top-chrome/shared/sp_shared_style.css.js';
import 'chrome://resources/cr_components/history_clusters/browser_proxy.js';
import 'chrome://resources/cr_components/history_clusters/clusters.js';
import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
+import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
import {BrowserProxyImpl} from 'chrome://resources/cr_components/history_clusters/browser_proxy.js';
import {CrToolbarSearchFieldElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -40,6 +42,11 @@ export class HistoryClustersAppElement extends PolymerElement {
};
}
+ constructor() {
+ super();
+ ColorChangeUpdater.forDocument().start();
+ }
+
//============================================================================
// Properties
//============================================================================
diff --git a/chromium/chrome/browser/resources/side_panel/history_clusters/history_clusters.html b/chromium/chrome/browser/resources/side_panel/history_clusters/history_clusters.html
index 30117b0794d..8ab14479ce7 100644
--- a/chromium/chrome/browser/resources/side_panel/history_clusters/history_clusters.html
+++ b/chromium/chrome/browser/resources/side_panel/history_clusters/history_clusters.html
@@ -7,6 +7,7 @@
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
<link rel="stylesheet" href="chrome://resources/css/md_colors.css">
+ <link rel="stylesheet" href="chrome://theme/colors.css?sets=ui,chrome">
<style>
html,
body {
@@ -21,6 +22,10 @@
overflow: hidden;
}
+ html[chrome-refresh-2023] body {
+ background: var(--color-side-panel-content-background);
+ }
+
@media (prefers-color-scheme: dark) {
body {
background: var(--google-grey-900);
diff --git a/chromium/chrome/browser/resources/side_panel/read_anything/BUILD.gn b/chromium/chrome/browser/resources/side_panel/read_anything/BUILD.gn
index 3a86dc8301b..7f771b19362 100644
--- a/chromium/chrome/browser/resources/side_panel/read_anything/BUILD.gn
+++ b/chromium/chrome/browser/resources/side_panel/read_anything/BUILD.gn
@@ -14,11 +14,16 @@ build_webui("build") {
"images/empty_state.svg",
]
- web_component_files = [ "app.ts" ]
+ web_component_files = [
+ "app.ts",
+ "read_anything_toolbar.ts",
+ ]
+ icons_html_files = [ "icons.html" ]
ts_composite = true
ts_deps = [
"//third_party/polymer/v3_0:library",
+ "//ui/webui/resources/cr_components/color_change_listener:build_ts",
"//ui/webui/resources/cr_elements:build_ts",
"//ui/webui/resources/js:build_ts",
"//ui/webui/resources/mojo:build_ts",
@@ -26,6 +31,7 @@ build_webui("build") {
ts_definitions = [
"read_anything.d.ts",
"//tools/typescript/definitions/pending.d.ts",
+ "//tools/typescript/definitions/metrics_private.d.ts",
]
ts_path_mappings =
[ "//read-anything-side-panel.top-chrome/shared/*|" +
diff --git a/chromium/chrome/browser/resources/side_panel/read_anything/app.html b/chromium/chrome/browser/resources/side_panel/read_anything/app.html
index fa948e57fef..9cb472aefab 100644
--- a/chromium/chrome/browser/resources/side_panel/read_anything/app.html
+++ b/chromium/chrome/browser/resources/side_panel/read_anything/app.html
@@ -1,5 +1,23 @@
<style include="cr-hidden-style">
+ :host-context([chrome-refresh-2023]) #container-parent,
+ :host-context([chrome-refresh-2023]) #empty-state-container {
+ margin: 0 8px;
+ border-radius: 16px;
+ display: block;
+ }
+ :host-context([chrome-refresh-2023]) #empty-state-container {
+ padding: 20px 0;
+ }
+ :host-context([chrome-refresh-2023]) #container {
+ border-radius: 16px;
+ display: block;
+ }
+ #container-parent {
+ background: var(--background-color);
+ height: 100%;
+ }
#container {
+ background: var(--background-color);
color: var(--foreground-color);
font-family: var(--font-family);
font-size: var(--font-size);
@@ -16,6 +34,8 @@
color: var(--visited-link-color);
}
#empty-state-container {
+ background: var(--background-color);
+ height: 100%;
padding: 20px;
}
sp-empty-state {
@@ -33,7 +53,12 @@
}
}
</style>
-<div id="container" hidden="[[!hasContent_]]"></div>
+<div id="toolbar-container" hidden="[[!isWebUIToolbarVisible_]]">
+ <read-anything-toolbar id="toolbar"></read-anything-toolbar>
+</div>
+<div id="container-parent" hidden="[[!hasContent_]]">
+ <div id="container"></div>
+</div>
<div id="empty-state-container" hidden="[[hasContent_]]">
<sp-empty-state
image-path="[[emptyStateImagePath_]]"
diff --git a/chromium/chrome/browser/resources/side_panel/read_anything/app.ts b/chromium/chrome/browser/resources/side_panel/read_anything/app.ts
index 105618e5256..32d0bf27fcd 100644
--- a/chromium/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chromium/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -5,7 +5,9 @@
import '//read-anything-side-panel.top-chrome/shared/sp_empty_state.js';
import '//resources/cr_elements/cr_hidden_style.css.js';
import '../strings.m.js';
+import './read_anything_toolbar.js';
+import {ColorChangeUpdater} from '//resources/cr_components/color_change_listener/colors_css_updater.js';
import {WebUiListenerMixin} from '//resources/cr_elements/web_ui_listener_mixin.js';
import {assert} from '//resources/js/assert_ts.js';
import {rgbToSkColor, skColorToRgba} from '//resources/js/color_utils.js';
@@ -14,6 +16,7 @@ import {SkColor} from '//resources/mojo/skia/public/mojom/skcolor.mojom-webui.js
import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './app.html.js';
+import {ReadAnythingToolbar} from './read_anything_toolbar.js';
const ReadAnythingElementBase = WebUiListenerMixin(PolymerElement);
@@ -21,6 +24,8 @@ interface LinkColor {
default: string;
visited: string;
}
+// TODO(crbug.com/1465029): Remove colors defined here once the Views toolbar is
+// removed.
const style = getComputedStyle(document.body);
const darkThemeBackgroundSkColor =
rgbToSkColor(style.getPropertyValue('--google-grey-900-rgb'));
@@ -68,9 +73,9 @@ class TwoWayMap extends Map {
}
}
-////////////////////////////////////////////////////////////
-// Called by ReadAnythingPageHandler via callback router. //
-////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+// Called by ReadAnythingUntrustedPageHandler via callback router. //
+/////////////////////////////////////////////////////////////////////
// The chrome.readingMode context is created by the ReadAnythingAppController
// which is only instantiated when the kReadAnything feature is enabled. This
@@ -106,6 +111,12 @@ if (chrome.readingMode) {
assert(readAnythingApp);
readAnythingApp.showEmpty();
};
+
+ chrome.readingMode.restoreSettingsFromPrefs = () => {
+ const readAnythingApp = document.querySelector('read-anything-app');
+ assert(readAnythingApp);
+ readAnythingApp.restoreSettingsFromPrefs();
+ };
}
export class ReadAnythingElement extends ReadAnythingElementBase {
@@ -120,8 +131,6 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
// Defines the valid font names that can be passed to front-end and maps
// them to a corresponding class style in app.html. Must stay in-sync with
// the names set in read_anything_font_model.cc.
- // TODO(1266555): The displayed names of the fonts should be messages that
- // will be translated to other languages.
private defaultFontName_: string = 'sans-serif';
private validFontNames_: Array<{name: string, css: string}> = [
{name: 'Poppins', css: 'Poppins'},
@@ -145,6 +154,25 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
private emptyStateHeading_: string;
private emptyStateSubheading_: string;
+ // If the WebUI toolbar should be shown. This happens when the WebUI feature
+ // flag is enabled.
+ private isWebUIToolbarVisible_: boolean;
+
+ synth = window.speechSynthesis;
+
+ // State for speech synthesis needs to be tracked separately because there
+ // are bugs with window.speechSynthesis.paused and
+ // window.speechSynthesis.speaking on some platforms.
+ paused = true;
+ speechStarted = false;
+
+ constructor() {
+ super();
+ if (chrome.readingMode && chrome.readingMode.isWebUIToolbarVisible) {
+ ColorChangeUpdater.forDocument().start();
+ }
+ }
+
override connectedCallback() {
super.connectedCallback();
if (chrome.readingMode) {
@@ -163,6 +191,8 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
assert(selection);
const {anchorNode, anchorOffset, focusNode, focusOffset} = selection;
if (!anchorNode || !focusNode) {
+ // The selection was collapsed by clicking inside the selection.
+ chrome.readingMode.onCollapseSelection();
return;
}
const anchorNodeId = this.domNodeToAxNodeIdMap_.get(anchorNode);
@@ -183,6 +213,8 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
chrome.readingMode.onCopy();
return false;
};
+
+ this.isWebUIToolbarVisible_ = chrome.readingMode.isWebUIToolbarVisible;
}
private buildSubtree_(nodeId: number): Node {
@@ -249,7 +281,7 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
}
showEmpty() {
- if (chrome.readingMode.isSelectable()) {
+ if (chrome.readingMode.isSelectable) {
this.emptyStateHeading_ = loadTimeData.getString('emptyStateHeader');
} else {
this.emptyStateHeading_ = loadTimeData.getString('notSelectableHeader');
@@ -270,6 +302,8 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
this.hasContent_ = false;
}
+ // TODO(crbug.com/1474951): Handle focus changes for speech, including
+ // updating speech state.
updateContent() {
const shadowRoot = this.shadowRoot;
assert(shadowRoot);
@@ -335,13 +369,117 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
startElement.scrollIntoViewIfNeeded();
}
- private validatedFontName_(): string {
+
+ stopSpeech() {
+ // TODO(crbug.com/1474951): When pausing, can we pause on the previous
+ // word so that speech doesn't resume in the middle of the word?
+ this.synth.pause();
+ this.paused = true;
+ }
+
+ playSpeech() {
+ if (this.speechStarted && this.paused) {
+ this.synth.resume();
+ this.paused = false;
+ return;
+ }
+ const shadowRoot = this.shadowRoot;
+ assert(shadowRoot);
+ const container = shadowRoot.getElementById('container');
+ assert(container);
+ if (container.textContent) {
+ this.paused = false;
+ this.playMessage(container.textContent);
+ }
+ }
+
+ playMessage(text: string) {
+ // TODO(crbug.com/1474951): 200 characters is set to avoid the issue on
+ // Linux where we don't get too-long text errors. We should investigate a
+ // more robust solution.
+ let maxTextLength = 200;
+ if (text.length < maxTextLength) {
+ maxTextLength = text.length;
+ }
+ let smallerText = text.substring(0, maxTextLength);
+ // TODO(crbug.com/1474951): Instead of splitting sentences by character
+ // search, which is brittle, use the accessibility APIs to get sentence
+ // boundaries, which will be more robust for internationalization and other
+ // types of sentences.
+ const textArray = smallerText.split('.');
+ if (textArray.length > 1) {
+ // TODO(crbug.com/1474951): Use a more efficient way of traversing through
+ // the text.
+ const splice = textArray[textArray.length - 1];
+ const index = smallerText.lastIndexOf(splice);
+ smallerText = text.substring(0, index);
+ maxTextLength = index;
+ }
+
+ const message = new SpeechSynthesisUtterance(smallerText);
+ message.lang = 'en-US';
+
+ // TODO(crbug.com/1474951): Add callbacks for onboundary and onpause.
+ message.onerror = function() {
+ // TODO(crbug.com/1474951): Add more sophisticated error handling.
+ window.speechSynthesis.cancel();
+ };
+
+ message.onend = function() {
+ const readAnythingApp = document.querySelector('read-anything-app');
+ assert(readAnythingApp);
+ if (text.length > maxTextLength) {
+ // Continue speaking with the next block of text.
+ readAnythingApp.playMessage(text.substring(maxTextLength, text.length));
+ } else {
+ readAnythingApp?.onSpeechStopped();
+ }
+ };
+
+ // TODO(crbug.com/1474951): Allow voice selection.
+ // This just selects the default English voice. If no voice is available,
+ // nothing happens.
+ const voices =
+ this.synth.getVoices().filter(voice => voice.lang === 'en-US');
+ message.voice = voices[0];
+
+ // TODO(crbug.com/1474951): Ensure the correct default values are used.
+ message.volume = 1;
+ message.pitch = 1;
+
+ // TODO(crbug.com/1474951): Allow rate to be customized.
+ message.rate = 1;
+
+ this.speechStarted = true;
+ this.synth.cancel();
+ this.synth.speak(message);
+ }
+
+ private onSpeechStopped() {
+ this.speechStarted = false;
+ const shadowRoot = this.shadowRoot;
+ assert(shadowRoot);
+ const toolbar = shadowRoot.getElementById('toolbar');
+ assert(toolbar);
+ if (toolbar instanceof ReadAnythingToolbar) {
+ toolbar.updateUiForPausing();
+ }
+ }
+
+ // TODO(b/1465029): Once the IsReadAnythingWebUIEnabled flag is removed
+ // this should be renamed to just validatedFontName_ and the current
+ // validatedFontName_ method can be removed.
+ private validatedFontNameFromName_(fontName: string): string {
// Validate that the given font name is a valid choice, or use the default.
- const validFontName = this.validFontNames_.find(
- (f: {name: string}) => f.name === chrome.readingMode.fontName);
+ const validFontName =
+ this.validFontNames_.find((f: {name: string}) => f.name === fontName);
return validFontName ? validFontName.css : this.defaultFontName_;
}
+ private validatedFontName_(): string {
+ return this.validatedFontNameFromName_(chrome.readingMode.fontName);
+ }
+
private getLinkColor_(backgroundSkColor: SkColor): LinkColor {
switch (backgroundSkColor.value) {
case darkThemeBackgroundSkColor.value:
@@ -359,6 +497,15 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
defaultThemeEmptyStateBodyColor;
}
+ // TODO(crbug.com/1465029): This method should be renamed to
+ // getEmptyStateBodyColor_() and replace the one above once we've removed the
+ // Views toolbar.
+ private getEmptyStateBodyColorFromWebUi_(colorSuffix: string): string {
+ const isDark = colorSuffix.includes('dark');
+ return isDark ? darkThemeEmptyStateBodyColor :
+ defaultThemeEmptyStateBodyColor;
+ }
+
private getSelectionColor_(backgroundSkColor: SkColor): string {
switch (backgroundSkColor.value) {
case darkThemeBackgroundSkColor.value:
@@ -370,6 +517,93 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
}
}
+ restoreSettingsFromPrefs() {
+ this.updateLineSpacing(chrome.readingMode.lineSpacing);
+ this.updateLetterSpacing(chrome.readingMode.letterSpacing);
+ this.updateFont(chrome.readingMode.fontName);
+ this.updateStyles({
+ '--font-size': chrome.readingMode.fontSize + 'em',
+ });
+ let colorSuffix: string|undefined;
+ switch (chrome.readingMode.colorTheme) {
+ case chrome.readingMode.defaultTheme:
+ colorSuffix = '';
+ break;
+ case chrome.readingMode.lightTheme:
+ colorSuffix = '-light';
+ break;
+ case chrome.readingMode.darkTheme:
+ colorSuffix = '-dark';
+ break;
+ case chrome.readingMode.yellowTheme:
+ colorSuffix = '-yellow';
+ break;
+ case chrome.readingMode.blueTheme:
+ colorSuffix = '-blue';
+ break;
+ default:
+ // Do nothing
+ }
+ if (colorSuffix !== undefined) {
+ this.updateThemeFromWebUi(colorSuffix);
+ }
+ }
+
+ updateLineSpacing(newLineHeight: number) {
+ this.updateStyles({
+ '--line-height': newLineHeight,
+ });
+ }
+
+ updateLetterSpacing(newLetterSpacing: number) {
+ this.updateStyles({
+ '--letter-spacing': newLetterSpacing + 'em',
+ });
+ }
+
+ updateFont(fontName: string) {
+ const validatedFontName = this.validatedFontNameFromName_(fontName);
+ this.updateStyles({
+ '--font-family': validatedFontName,
+ });
+
+ // Also update the font on the toolbar itself with the validated font name.
+ const shadowRoot = this.shadowRoot;
+ assert(shadowRoot);
+ const toolbar = shadowRoot.getElementById('toolbar');
+ if (toolbar) {
+ toolbar.style.fontFamily = validatedFontName;
+ }
+ }
+
+ updateFontSize() {
+ this.updateStyles({
+ '--font-size': chrome.readingMode.fontSize + 'em',
+ });
+ }
+
+ // TODO(crbug.com/1465029): This method should be renamed to updateTheme()
+ // and replace the one below once we've removed the Views toolbar.
+ updateThemeFromWebUi(colorSuffix: string) {
+ const emptyStateBodyColor = colorSuffix ?
+ this.getEmptyStateBodyColorFromWebUi_(colorSuffix) :
+ 'var(--color-side-panel-card-secondary-foreground)';
+ this.updateStyles({
+ '--background-color':
+ `var(--color-read-anything-background${colorSuffix})`,
+ '--foreground-color':
+ `var(--color-read-anything-foreground${colorSuffix})`,
+ '--sp-empty-state-heading-color':
+ `var(--color-read-anything-foreground${colorSuffix})`,
+ '--sp-empty-state-body-color': emptyStateBodyColor,
+ '--selection-color':
+ `var(--color-read-anything-text-selection${colorSuffix})`,
+ '--link-color': `var(--color-read-anything-link-default${colorSuffix})`,
+ '--visited-link-color':
+ `var(--color-read-anything-link-visited${colorSuffix})`,
+ });
+ }
+
updateTheme() {
const foregroundColor:
SkColor = {value: chrome.readingMode.foregroundColor};
@@ -378,6 +612,7 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
const linkColor = this.getLinkColor_(backgroundColor);
this.updateStyles({
+ '--background-color': skColorToRgba(backgroundColor),
'--font-family': this.validatedFontName_(),
'--font-size': chrome.readingMode.fontSize + 'em',
'--foreground-color': skColorToRgba(foregroundColor),
@@ -390,7 +625,9 @@ export class ReadAnythingElement extends ReadAnythingElementBase {
this.getEmptyStateBodyColor_(backgroundColor),
'--visited-link-color': linkColor.visited,
});
- document.body.style.background = skColorToRgba(backgroundColor);
+ if (!chrome.readingMode.isWebUIToolbarVisible) {
+ document.body.style.background = skColorToRgba(backgroundColor);
+ }
}
}
diff --git a/chromium/chrome/browser/resources/side_panel/read_anything/icons.html b/chromium/chrome/browser/resources/side_panel/read_anything/icons.html
new file mode 100644
index 00000000000..0293fffc7e3
--- /dev/null
+++ b/chromium/chrome/browser/resources/side_panel/read_anything/icons.html
@@ -0,0 +1,92 @@
+<iron-iconset-svg name="read-anything" size="16">
+ <svg>
+ <defs>
+ <g id="font-size" viewBox="0 0 14 10">
+ <path d="M8.6 9.93333V1.8H5.4V0.0666656H13.5333V1.8H10.3333V9.93333H8.6ZM2.46667 9.93333V5.11667H0.466667V3.4H6.2V5.11667H4.2V9.93333H2.46667Z"></path>
+ </g>
+ <g id="font-size-increase" viewBox="0 0 10 10">
+ <path d="M4.3 5.7H0.866667V4.3H4.3V0.866666H5.7V4.3H9.13333V5.7H5.7V9.13333H4.3V5.7Z"></path>
+ </g>
+ <g id="font-size-decrease" viewBox="0 0 10 2">
+ <path d="M0.733334 1.7V0.299999H9.26667V1.7H0.733334Z"></path>
+ </g>
+ <g id="font">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M2.66683 1.33301H13.3335C14.0668 1.33301 14.6668 1.93301 14.6668 2.66634V13.333C14.6668 14.0663 14.0668 14.6663 13.3335 14.6663H2.66683C1.9335 14.6663 1.3335 14.0663 1.3335 13.333V2.66634C1.3335 1.93301 1.9335 1.33301 2.66683 1.33301ZM2.66683 13.333H13.3335V2.66634H2.66683V13.333ZM7.12683 3.99967H8.86016L11.8668 11.9997H10.2002L9.52683 10.0863H6.46683L5.80016 11.9997H4.1335L7.12683 3.99967ZM8.3335 6.69301L9.04016 8.70634H6.9535L7.66683 6.69301L7.96016 5.73301H8.04683L8.3335 6.69301Z"></path>
+ </g>
+ <g id="color">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M1.33337 7.99967C1.33337 11.673 4.32671 14.6663 8.00004 14.6663C8.92004 14.6663 9.66671 13.9197 9.66671 12.9997C9.66671 12.593 9.51337 12.1997 9.24004 11.8863C9.18671 11.8197 9.15337 11.7463 9.15337 11.6663C9.15337 11.4797 9.30004 11.333 9.48671 11.333H10.6667C12.8734 11.333 14.6667 9.53967 14.6667 7.33301C14.6667 4.02634 11.6734 1.33301 8.00004 1.33301C4.32671 1.33301 1.33337 4.32634 1.33337 7.99967ZM2.66671 7.99967C2.66671 5.05967 5.06004 2.66634 8.00004 2.66634C10.94 2.66634 13.3334 4.75967 13.3334 7.33301C13.3334 8.80634 12.14 9.99967 10.6667 9.99967H9.48671C8.56671 9.99967 7.82004 10.7463 7.82004 11.6663C7.82004 12.0663 7.96671 12.4597 8.24004 12.7663C8.28004 12.813 8.33337 12.893 8.33337 12.9997C8.33337 13.1863 8.18671 13.333 8.00004 13.333C5.06004 13.333 2.66671 10.9397 2.66671 7.99967ZM4.33337 8.66634C4.88566 8.66634 5.33337 8.21863 5.33337 7.66634C5.33337 7.11406 4.88566 6.66634 4.33337 6.66634C3.78109 6.66634 3.33337 7.11406 3.33337 7.66634C3.33337 8.21863 3.78109 8.66634 4.33337 8.66634ZM7.33337 4.99967C7.33337 5.55196 6.88566 5.99967 6.33337 5.99967C5.78109 5.99967 5.33337 5.55196 5.33337 4.99967C5.33337 4.44739 5.78109 3.99967 6.33337 3.99967C6.88566 3.99967 7.33337 4.44739 7.33337 4.99967ZM9.66671 5.99967C10.219 5.99967 10.6667 5.55196 10.6667 4.99967C10.6667 4.44739 10.219 3.99967 9.66671 3.99967C9.11442 3.99967 8.66671 4.44739 8.66671 4.99967C8.66671 5.55196 9.11442 5.99967 9.66671 5.99967ZM12.6667 7.66634C12.6667 8.21863 12.219 8.66634 11.6667 8.66634C11.1144 8.66634 10.6667 8.21863 10.6667 7.66634C10.6667 7.11406 11.1144 6.66634 11.6667 6.66634C12.219 6.66634 12.6667 7.11406 12.6667 7.66634Z"></path>
+ </g>
+ <g id="line-spacing">
+ <path d="M4.00004 13.3334L1.33337 10.6667L2.26671 9.73337L3.33337 10.7667V5.23337L2.26671 6.26671L1.33337 5.33337L4.00004 2.66671L6.66671 5.33337L5.73337 6.26671L4.66671 5.23337V10.7667L5.73337 9.73337L6.66671 10.6667L4.00004 13.3334ZM8.00004 12.6667V11.3334H14.6667V12.6667H8.00004ZM8.00004 8.66671V7.33337H14.6667V8.66671H8.00004ZM8.00004 4.66671V3.33337H14.6667V4.66671H8.00004Z"></path>
+ </g>
+ <g id="letter-spacing">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M8.29764 2H8.66302L11.2784 9.00002H9.73995L9.25448 7.52406H6.7386L6.25436 9.00002H4.7207L7.32167 2H7.67744H8.29764ZM7.10928 6.39425H8.88287L7.99496 3.69472L7.10928 6.39425ZM2 11.8461L4.57144 13.9999V12.564H11.4277V11.1281H4.57144V9.69219L2 11.8461ZM11.4283 14L13.9998 11.8461L11.4283 9.69229V14Z"></path>
+ </g>
+ <g id="settings-outline">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10.2062 14.02C10.1395 14.4733 9.72617 14.8333 9.23284 14.8333H6.76617C6.27284 14.8333 5.85951 14.4733 5.79951 13.9867L5.61951 12.7267C5.43951 12.6333 5.26617 12.5333 5.09284 12.42L3.89284 12.9C3.42617 13.0733 2.91284 12.88 2.68617 12.4667L1.46617 10.3533C1.23284 9.91334 1.33284 9.39334 1.70617 9.10001L2.72617 8.30667C2.71951 8.20667 2.71284 8.10667 2.71284 8.00001C2.71284 7.90001 2.71951 7.79334 2.72617 7.69334L1.71284 6.90001C1.31951 6.60001 1.21951 6.06001 1.46617 5.64667L2.69951 3.52001C2.92617 3.10667 3.43951 2.92001 3.89284 3.10001L5.09951 3.58667C5.27284 3.47334 5.44617 3.37334 5.61951 3.28001L5.79951 2.00667C5.85951 1.54001 6.27284 1.17334 6.75951 1.17334H9.22617C9.71951 1.17334 10.1328 1.53334 10.1928 2.02001L10.3728 3.28001C10.5528 3.37334 10.7262 3.47334 10.8995 3.58667L12.0995 3.10667C12.5728 2.93334 13.0862 3.12667 13.3128 3.54001L14.5395 5.66001C14.7795 6.10001 14.6728 6.62001 14.2995 6.91334L13.2862 7.70667C13.2928 7.80667 13.2995 7.90667 13.2995 8.01334C13.2995 8.12001 13.2928 8.22001 13.2862 8.32001L14.2995 9.11334C14.6728 9.41334 14.7795 9.93334 14.5462 10.3533L13.3062 12.5C13.0795 12.9133 12.5662 13.1 12.1062 12.92L10.9062 12.44C10.7328 12.5533 10.5595 12.6533 10.3862 12.7467L10.2062 14.02ZM7.07951 13.5H8.91951L9.16617 11.8L9.51951 11.6533C9.81284 11.5333 10.1062 11.36 10.4128 11.1333L10.7128 10.9067L12.2995 11.5467L13.2195 9.94667L11.8662 8.89334L11.9128 8.52001L11.9149 8.50206C11.9342 8.33514 11.9528 8.17378 11.9528 8.00001C11.9528 7.82001 11.9328 7.64667 11.9128 7.48001L11.8662 7.10667L13.2195 6.05334L12.2928 4.45334L10.6995 5.09334L10.3995 4.86001C10.1195 4.64667 9.81951 4.47334 9.51284 4.34667L9.16617 4.20001L8.91951 2.50001H7.07951L6.83284 4.20001L6.47951 4.34001C6.18617 4.46667 5.89284 4.63334 5.58617 4.86667L5.28617 5.08667L3.69951 4.45334L2.77284 6.04667L4.12617 7.10001L4.07951 7.47334C4.05951 7.64667 4.03951 7.82667 4.03951 8.00001C4.03951 8.17334 4.05284 8.35334 4.07951 8.52001L4.12617 8.89334L2.77284 9.94667L3.69284 11.5467L5.28617 10.9067L5.58617 11.14C5.87284 11.36 6.15951 11.5267 6.47284 11.6533L6.82617 11.8L7.07951 13.5ZM10.3328 8.00001C10.3328 9.28867 9.28817 10.3333 7.99951 10.3333C6.71084 10.3333 5.66617 9.28867 5.66617 8.00001C5.66617 6.71134 6.71084 5.66667 7.99951 5.66667C9.28817 5.66667 10.3328 6.71134 10.3328 8.00001Z"></path>
+ </g>
+ <g id="default-theme" viewBox="0 0 18 18">
+ <circle cx="9" cy="9" r="8.26667" fill="#F8F9FA" stroke="#DADCE0" stroke-width="0.533333">
+ </circle>
+ <path d="M9 17C13.4183 17 17 13.4183 17 9C17 4.58172 13.4183 1 9 1V17Z" fill="#202124"></path>
+ </g>
+ <g id="blue-theme" viewBox="0 0 18 18">
+ <circle cx="9" cy="9" r="8.26667" fill="#AECBFA" stroke="#DADCE0" stroke-width="0.533333">
+ </circle>
+ <path d="M6.97119 11.6667L8.84319 6.70248H9.70985L11.5888 11.6667H10.7499L10.2923 10.391H8.26772L7.81012 11.6667H6.97119ZM10.0357 9.68381L9.48799 8.16541L9.30079 7.59688H9.25919L9.07199 8.16541L8.52425 9.68381H10.0357Z" fill="#202124"></path>
+ </g>
+ <g id="yellow-theme" viewBox="0 0 18 18">
+ <circle cx="9" cy="9" r="8.26667" fill="#FDE293" stroke="#DADCE0" stroke-width="0.533333">
+ </circle>
+ <path d="M6.97021 11.6666L8.84221 6.70236H9.70888L11.5878 11.6666H10.7489L10.2913 10.3909H8.26674L7.80914 11.6666H6.97021ZM10.0347 9.68369L9.48701 8.16529L9.29981 7.59676H9.25821L9.07101 8.16529L8.52328 9.68369H10.0347Z" fill="#202124"></path>
+ </g>
+ <g id="dark-theme" viewBox="0 0 18 18">
+ <circle cx="9" cy="9" r="8.26667" fill="#202124" stroke="#DADCE0" stroke-width="0.533333">
+ </circle>
+ <path d="M6.97021 11.6666L8.84221 6.70236H9.70888L11.5878 11.6666H10.7489L10.2913 10.3909H8.26674L7.80914 11.6666H6.97021ZM10.0347 9.68369L9.48701 8.16529L9.29981 7.59676H9.25821L9.07101 8.16529L8.52328 9.68369H10.0347Z" fill="#E8EAED"></path>
+ </g>
+ <g id="light-theme" viewBox="0 0 18 18">
+ <circle cx="9" cy="9" r="8.26667" fill="#F8F9FA" stroke="#DADCE0" stroke-width="0.533333">
+ </circle>
+ <path d="M6.97021 11.6666L8.84221 6.70236H9.70888L11.5878 11.6666H10.7489L10.2913 10.3909H8.26674L7.80914 11.6666H6.97021ZM10.0347 9.68369L9.48701 8.16529L9.29981 7.59676H9.25821L9.07101 8.16529L8.52328 9.68369H10.0347Z" fill="#3C4043"></path>
+ </g>
+ <g id="line-spacing-standard" viewBox="0 0 20 20">
+ <path d="M16.9998 5H2.99951V7.00003H16.9998V5Z"></path>
+ <path d="M16.9998 9H2.99951V11H16.9998V9Z"></path>
+ <path d="M16.9998 13H2.99951V15H16.9998V13Z"></path>
+ </g>
+ <g id="line-spacing-loose" viewBox="0 0 20 20">
+ <path d="M16.9998 4H2.99951V6.00003H16.9998V4Z"></path>
+ <path d="M16.9998 9H2.99951V11H16.9998V9Z"></path>
+ <path d="M16.9998 14H2.99951V16H16.9998V14Z"></path>
+ </g>
+ <g id="line-spacing-very-loose" viewBox="0 0 20 20">
+ <path d="M16.9998 3H2.99951V5.00003H16.9998V3Z"></path>
+ <path d="M16.9998 9H2.99951V11H16.9998V9Z"></path>
+ <path d="M16.9998 15H2.99951V17H16.9998V15Z"></path>
+ </g>
+ <g id="letter-spacing-standard" viewBox="0 0 14 10">
+ <path d="M0.2 9.8V0.199999H1.4V9.8H0.2ZM8.6 9.8V0.199999H9.8V9.8H8.6ZM2.03333 8.2L4.43333 1.8H5.58333L7.98333 8.2H6.88333L6.31667 6.56667H3.73333L3.15 8.2H2.03333ZM4.05 5.63333H5.96667L5.03333 2.98333H4.98333L4.05 5.63333Z"></path>
+ </g>
+ <g id="letter-spacing-wide" viewBox="0 0 14 10">
+ <path d="M0.4 9.8V0.199999H1.6V9.8H0.4ZM10.4 9.8V0.199999H11.6V9.8H10.4ZM3.03333 8.2L5.43333 1.8H6.58333L8.98333 8.2H7.88333L7.31667 6.56667H4.73333L4.15 8.2H3.03333ZM5.05 5.63333H6.96667L6.03333 2.98333H5.98333L5.05 5.63333Z"></path>
+ </g>
+ <g id="letter-spacing-very-wide" viewBox="0 0 14 10">
+ <path d="M0.6 9.8V0.199999H1.8V9.8H0.6ZM12.2 9.8V0.199999H13.4V9.8H12.2ZM4.03333 8.2L6.43333 1.8H7.58333L9.98333 8.2H8.88333L8.31667 6.56667H5.73333L5.15 8.2H4.03333ZM6.05 5.63333H7.96667L7.03333 2.98333H6.98333L6.05 5.63333Z"></path>
+ </g>
+
+ </defs>
+ </svg>
+</iron-iconset-svg>
+<iron-iconset-svg name="read-anything-20" size="20">
+ <svg>
+ <defs>
+ <g id="play" viewBox="0 0 18 18">
+ <path d="M6.95833 12.5625L12.5625 9L6.95833 5.4375V12.5625ZM9 17.1667C7.875 17.1667 6.8125 16.9583 5.8125 16.5417C4.82639 16.1111 3.95833 15.5278 3.20833 14.7917C2.47222 14.0417 1.88889 13.1736 1.45833 12.1875C1.04167 11.1875 0.833333 10.125 0.833333 9C0.833333 7.86111 1.04167 6.79861 1.45833 5.8125C1.88889 4.82639 2.47222 3.96528 3.20833 3.22917C3.95833 2.47917 4.82639 1.89583 5.8125 1.47917C6.8125 1.04861 7.875 0.833333 9 0.833333C10.1389 0.833333 11.2014 1.04861 12.1875 1.47917C13.1736 1.89583 14.0347 2.47917 14.7708 3.22917C15.5208 3.96528 16.1042 4.83333 16.5208 5.83333C16.9514 6.81944 17.1667 7.875 17.1667 9C17.1667 10.125 16.9514 11.1875 16.5208 12.1875C16.1042 13.1736 15.5208 14.0417 14.7708 14.7917C14.0347 15.5278 13.1667 16.1111 12.1667 16.5417C11.1806 16.9583 10.125 17.1667 9 17.1667Z"></path>
+ </g>
+ <g id="pause" viewBox="0 0 18 18">
+ <path d="M6.41667 12.0417H8V5.95833H6.41667V12.0417ZM10 12.0417H11.5833V5.95833H10V12.0417ZM9 17.1667C7.875 17.1667 6.8125 16.9583 5.8125 16.5417C4.82639 16.1111 3.95833 15.5278 3.20833 14.7917C2.47222 14.0417 1.88889 13.1736 1.45833 12.1875C1.04167 11.1875 0.833333 10.125 0.833333 9C0.833333 7.86111 1.04167 6.79861 1.45833 5.8125C1.88889 4.82639 2.47222 3.96528 3.20833 3.22917C3.95833 2.47917 4.82639 1.89583 5.8125 1.47917C6.8125 1.04861 7.875 0.833333 9 0.833333C10.1389 0.833333 11.2014 1.04861 12.1875 1.47917C13.1736 1.89583 14.0347 2.47917 14.7708 3.22917C15.5208 3.96528 16.1042 4.83333 16.5208 5.83333C16.9514 6.81944 17.1667 7.875 17.1667 9C17.1667 10.125 16.9514 11.1875 16.5208 12.1875C16.1042 13.1736 15.5208 14.0417 14.7708 14.7917C14.0347 15.5278 13.1667 16.1111 12.1667 16.5417C11.1806 16.9583 10.125 17.1667 9 17.1667Z"></path>
+ </g>
+ </defs>
+ </svg>
+</iron-iconset-svg>
diff --git a/chromium/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts b/chromium/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
index 74293843559..2f0137cec11 100644
--- a/chromium/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
+++ b/chromium/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
@@ -31,6 +31,32 @@ declare namespace chrome {
let lineSpacing: number;
let letterSpacing: number;
+ // The current color theme value.
+ let colorTheme: number;
+
+ // Enum values for various visual theme changes.
+ let standardLineSpacing: number;
+ let looseLineSpacing: number;
+ let veryLooseLineSpacing: number;
+ let standardLetterSpacing: number;
+ let wideLetterSpacing: number;
+ let veryWideLetterSpacing: number;
+ let defaultTheme: number;
+ let lightTheme: number;
+ let darkTheme: number;
+ let yellowTheme: number;
+ let blueTheme: number;
+
+ // Whether the WebUI toolbar feature flag is enabled.
+ let isWebUIToolbarVisible: boolean;
+
+ // Whether the Read Aloud feature flag is enabled.
+ let isReadAloudEnabled: boolean;
+
+ // Indicates if select-to-distill works on the web page. Used to
+ // determine which empty state to display.
+ let isSelectable: boolean;
+
// Returns a list of AXNodeIDs corresponding to the unignored children of
// the AXNode for the provided AXNodeID. If there is a selection contained
// in this node, only returns children which are partially or entirely
@@ -75,9 +101,37 @@ declare namespace chrome {
// the link's corresponding AXNode in the main pane.
function onLinkClicked(nodeId: number): void;
- // Returns true if select-to-distill works on the web page. Used to
- // determine which empty state to display.
- function isSelectable(): boolean;
+ // Called when the line spacing is changed via the webui toolbar.
+ function onStandardLineSpacing(): void;
+ function onLooseLineSpacing(): void;
+ function onVeryLooseLineSpacing(): void;
+
+ // Called when a user makes a font size change via the webui toolbar.
+ function onFontSizeChanged(increase: boolean): void;
+ function onFontSizeReset(): void;
+
+ // Called when the letter spacing is changed via the webui toolbar.
+ function onStandardLetterSpacing(): void;
+ function onWideLetterSpacing(): void;
+ function onVeryWideLetterSpacing(): void;
+
+ // Called when the color theme is changed via the webui toolbar.
+ function onDefaultTheme(): void;
+ function onLightTheme(): void;
+ function onDarkTheme(): void;
+ function onYellowTheme(): void;
+ function onBlueTheme(): void;
+
+ // Called when the font is changed via the webui toolbar.
+ function onFontChange(font: string): void;
+
+ // Returns the actual spacing value to use based on the given lineSpacing
+ // category.
+ function getLineSpacingValue(lineSpacing: number): number;
+
+ // Returns the actual spacing value to use based on the given letterSpacing
+ // category.
+ function getLetterSpacingValue(letterSpacing: number): number;
// Called when a user makes a selection change. AnchorNodeID and
// focusAXNodeID are AXNodeIDs which identify the anchor and focus AXNodes
@@ -85,6 +139,9 @@ declare namespace chrome {
function onSelectionChange(
anchorNodeId: number, anchorOffset: number, focusNodeId: number,
focusOffset: number): void;
+ // Called when a user collapses the selection. This is usually accomplished
+ // by clicking.
+ function onCollapseSelection(): void;
// Set the content. Used by tests only.
// SnapshotLite is a data structure which resembles an AXTreeUpdate. E.g.:
@@ -132,5 +189,9 @@ declare namespace chrome {
// Ping that the theme choices of the user have been changed using the
// toolbar and are ready to consume.
function updateTheme(): void;
+
+ // Ping that the theme choices of the user have been retrieved from
+ // preferences and can be used to set up the page.
+ function restoreSettingsFromPrefs(): void;
}
}
diff --git a/chromium/chrome/browser/resources/side_panel/read_anything/read_anything.html b/chromium/chrome/browser/resources/side_panel/read_anything/read_anything.html
index 5ba82c08410..24a8d9e560f 100644
--- a/chromium/chrome/browser/resources/side_panel/read_anything/read_anything.html
+++ b/chromium/chrome/browser/resources/side_panel/read_anything/read_anything.html
@@ -1,10 +1,12 @@
<!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<html dir="$i18n{textdirection}" lang="$i18n{language}"
+ $i18n{chromeRefresh2023Attribute}>
<head>
<meta charset="utf-8">
<title>$i18n{readAnythingTabTitle}</title>
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="//resources/css/text_defaults_md.css">
+ <link rel="stylesheet" href="//theme/colors.css?sets=ui,chrome">
<link rel="stylesheet" href="//resources/css/md_colors.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins|Comic+Neue|Lexend+Deca|EB+Garamond|STIX+Two+Text">
<style>
@@ -15,11 +17,13 @@
padding: 0;
width: 100%;
}
-
body {
overflow-wrap: break-word;
overflow-x: hidden;
}
+ html[chrome-refresh-2023] body {
+ background: var(--color-side-panel-content-background);
+ }
</style>
</head>
<body>
diff --git a/chromium/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html b/chromium/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html
new file mode 100644
index 00000000000..b5041d7bbf9
--- /dev/null
+++ b/chromium/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.html
@@ -0,0 +1,183 @@
+<style include="cr-icons">
+ iron-icon {
+ --icon-size: 20px;
+ height: var(--icon-size);
+ width: var(--icon-size);
+ padding: 0 8px 0 0;
+ }
+ cr-icon-button {
+ margin: 10px 4px;
+ }
+ :host-context([chrome-refresh-2023]) #play-pause {
+ --cr-icon-button-icon-size: 20px;
+ --cr-icon-button-size: 28px;
+ color: var(--color-side-panel-entry-icon);
+ }
+ :host-context([chrome-refresh-2023]) cr-icon-button {
+ --cr-icon-button-icon-size: 16px;
+ --cr-icon-button-size: 24px;
+ color: var(--color-sys-on-surface-subtle);
+ }
+ :host-context([chrome-refresh-2023]) .dropdown-item:focus {
+ background-color: var(--cr-hover-background-color);
+ }
+ .dropdown-item {
+ display: block;
+ padding: 6px 0 6px 16px;
+ }
+ /* TODO(b/1465029): Investigate why chrome refresh colors don't always
+ work on first launch. */
+ :host-context([chrome-refresh-2023]) cr-action-menu {
+ --cr-menu-border-radius: 4px;
+ }
+ .back, .section {
+ font-weight: bold;
+ }
+ #font-size-decrease{
+ margin-left: 16px;
+ }
+ .font-size {
+ margin: 0 4px;
+ }
+ .text-button {
+ border: none;
+ }
+</style>
+<div class="toolbar-container">
+ <span id="play-pause-container" hidden="[[!isReadAloudEnabled_]]">
+ <cr-icon-button
+ id="play-pause"
+ iron-icon="read-anything-20:play"
+ on-click="onPlayPauseClick_">
+ </cr-icon-button>
+ </span>
+ <cr-icon-button
+ id="font-size"
+ iron-icon="read-anything:font-size"
+ on-click="onShowFontSizeMenuClick_">
+ </cr-icon-button>
+ <cr-icon-button
+ id="font"
+ iron-icon="read-anything:font"
+ on-click="onShowFontSubMenuClick_">
+ </cr-icon-button>
+ <cr-icon-button
+ id="color"
+ iron-icon="read-anything:color"
+ on-click="onShowColorSubMenuClick_">
+ </cr-icon-button>
+ <cr-icon-button
+ id="line-spacing"
+ iron-icon="read-anything:line-spacing"
+ on-click="onShowLineSpacingSubMenuClick_">
+ </cr-icon-button>
+ <cr-icon-button
+ id="letter-spacing"
+ iron-icon="read-anything:letter-spacing"
+ on-click="onShowLetterSpacingSubMenuClick_">
+ </cr-icon-button>
+
+ <cr-action-menu id="fontSizeMenu">
+ <cr-icon-button
+ class="font-size"
+ id="font-size-decrease"
+ iron-icon="read-anything:font-size-decrease"
+ on-click="onFontSizeDecreaseClick_">
+ </cr-icon-button>
+ <cr-icon-button
+ class="font-size"
+ id="font-size-increase"
+ iron-icon="read-anything:font-size-increase"
+ on-click="onFontSizeIncreaseClick_">
+ </cr-icon-button>
+ <cr-button class="text-button" on-click="onFontResetClick_">
+ Reset
+ </cr-button>
+ </cr-action-menu>
+ <cr-action-menu id="colorSubmenu">
+ <button class="dropdown-item" data$="[[menuStateEnum_.DEFAULT_COLOR]]">
+ <iron-icon class="button-image" icon="read-anything:default-theme"></iron-icon>
+ $i18n{defaultColorTitle}
+ </button>
+ <button class="dropdown-item" data$="[[menuStateEnum_.LIGHT]]">
+ <iron-icon class="button-image" icon="read-anything:light-theme"></iron-icon>
+ $i18n{lightColorTitle}
+ </button>
+ <button class="dropdown-item" data$="[[menuStateEnum_.DARK]]">
+ <iron-icon class="button-image" icon="read-anything:dark-theme"></iron-icon>
+ $i18n{darkColorTitle}
+ </button>
+ <button class="dropdown-item" data$="[[menuStateEnum_.YELLOW]]">
+ <iron-icon class="button-image" icon="read-anything:yellow-theme"></iron-icon>
+ $i18n{yellowColorTitle}
+ </button>
+ <button class="dropdown-item" data$="[[menuStateEnum_.BLUE]]">
+ <iron-icon class="button-image" icon="read-anything:blue-theme"></iron-icon>
+ $i18n{blueColorTitle}
+ </button>
+ </cr-action-menu>
+ <cr-action-menu id="lineSpacingSubmenu">
+ <button class="dropdown-item" data$="[[menuStateEnum_.LINE_STANDARD]]">
+ <iron-icon class="button-image"
+ icon="read-anything:line-spacing-standard"></iron-icon>
+ $i18n{lineSpacingStandardTitle}
+ </button>
+ <button class="dropdown-item" data$="[[menuStateEnum_.LOOSE]]">
+ <iron-icon class="button-image"
+ icon="read-anything:line-spacing-loose"></iron-icon>
+ $i18n{lineSpacingLooseTitle}
+ </button>
+ <button class="dropdown-item" data$="[[menuStateEnum_.VERY_LOOSE]]">
+ <iron-icon class="button-image"
+ icon="read-anything:line-spacing-very-loose"></iron-icon>
+ $i18n{lineSpacingVeryLooseTitle}
+ </button>
+ </cr-action-menu>
+ <cr-action-menu id="letterSpacingSubmenu">
+ <!--TODO(b/1465029): Fix icon that is slightly out of alignment. -->
+ <button class="dropdown-item" data$="[[menuStateEnum_.LETTER_STANDARD]]">
+ <iron-icon class="button-image"
+ icon="read-anything:letter-spacing-standard"></iron-icon>
+ $i18n{letterSpacingStandardTitle}
+ </button>
+ <button class="dropdown-item" data$="[[menuStateEnum_.WIDE]]">
+ <iron-icon class="button-image"
+ icon="read-anything:letter-spacing-wide"></iron-icon>
+ $i18n{letterSpacingWideTitle}
+ </button>
+ <button class="dropdown-item" data$="[[menuStateEnum_.VERY_WIDE]]">
+ <iron-icon class="button-image"
+ icon="read-anything:letter-spacing-very-wide"></iron-icon>
+ $i18n{letterSpacingVeryWideTitle}
+ </button>
+ </cr-action-menu>
+ <!-- TODO(b/1465029): Use the brower's preferred language to hide unsupported
+ fonts.
+ -->
+ <!-- If you change these fonts, please also update
+ read_anything_constants.h -->
+ <cr-action-menu id="fontSubmenu">
+ <!-- TODO(1266555): Each button should be styled in its own font. -->
+ <button class="dropdown-item">
+ Poppins
+ </button>
+ <button class="dropdown-item">
+ Sans-serif
+ </button>
+ <button class="dropdown-item">
+ Serif
+ </button>
+ <button class="dropdown-item">
+ Comic Neue
+ </button>
+ <button class="dropdown-item">
+ Lexend Deca
+ </button>
+ <button class="dropdown-item">
+ EB Garamond
+ </button>
+ <button class="dropdown-item">
+ STIX Two Text
+ </button>
+ </cr-action-menu>
+</div>
diff --git a/chromium/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts b/chromium/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts
new file mode 100644
index 00000000000..f7662fb4faf
--- /dev/null
+++ b/chromium/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.ts
@@ -0,0 +1,377 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '//resources/cr_elements/cr_button/cr_button.js';
+import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import '//resources/cr_elements/cr_icons.css.js';
+import '//resources/cr_elements/icons.html.js';
+import '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import './icons.html.js';
+
+import {AnchorAlignment, CrActionMenuElement, ShowAtPositionConfig} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import {WebUiListenerMixin} from '//resources/cr_elements/web_ui_listener_mixin.js';
+import {assert} from '//resources/js/assert_ts.js';
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './read_anything_toolbar.html.js';
+
+export interface ReadAnythingToolbar {
+ $: {
+ colorSubmenu: CrActionMenuElement,
+ lineSpacingSubmenu: CrActionMenuElement,
+ letterSpacingSubmenu: CrActionMenuElement,
+ fontSubmenu: CrActionMenuElement,
+ fontSizeMenu: CrActionMenuElement,
+ };
+}
+
+enum MenuStateValue {
+ LINE_STANDARD = 0,
+ LOOSE = 1,
+ VERY_LOOSE = 2,
+ DEFAULT_COLOR = 3,
+ LIGHT = 4,
+ DARK = 5,
+ YELLOW = 6,
+ BLUE = 7,
+ LETTER_STANDARD = 8,
+ WIDE = 9,
+ VERY_WIDE = 10,
+}
+
+// Enum for logging when a text style setting is changed.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum ReadAnythingSettingsChange {
+ FONT_CHANGE = 0,
+ FONT_SIZE_CHANGE = 1,
+ THEME_CHANGE = 2,
+ LINE_HEIGHT_CHANGE = 3,
+ LETTER_SPACING_CHANGE = 4,
+
+ // Must be last.
+ COUNT = 5,
+}
+
+const SETTINGS_CHANGE_UMA = 'Accessibility.ReadAnything.SettingsChange';
+
+
+const ReadAnythingToolbarBase = WebUiListenerMixin(PolymerElement);
+export class ReadAnythingToolbar extends ReadAnythingToolbarBase {
+ contentPage = document.querySelector('read-anything-app');
+ static get is() {
+ return 'read-anything-toolbar';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ static get properties() {
+ return {
+ menuStateEnum_: {
+ type: Object,
+ value: MenuStateValue,
+ },
+ };
+ }
+
+ private showAtPositionConfig_: ShowAtPositionConfig = {
+ top: 20,
+ left: 8,
+ anchorAlignmentY: AnchorAlignment.AFTER_END,
+ };
+
+ private isReadAloudEnabled_: boolean;
+
+ // If Read Aloud is in the paused state.
+ isPaused = true;
+
+ // This is needed to keep a reference to any dynamically added callbacks so
+ // that they can be removed with #removeEventListener.
+ private elementCallbackMap = new Map<any, () => void>();
+
+ override connectedCallback() {
+ super.connectedCallback();
+ this.isReadAloudEnabled_ = chrome.readingMode.isReadAloudEnabled;
+
+ const onFontClick = (fontName: string) => {
+ this.onFontClick_(fontName);
+ };
+
+ const fontNodes = Array.from(this.$.fontSubmenu.children);
+ fontNodes.forEach((element) => {
+ if (element instanceof HTMLButtonElement) {
+ if (element.classList.contains('back') || !element.innerText) {
+ return;
+ }
+ // Update the font of each button to be the same as the font text.
+ element.style.fontFamily = element.innerText;
+ // Set the onclick listener for each button so that the content
+ // page font updates when a button is clicked.
+ const callback = () => {
+ onFontClick(element.innerText);
+ };
+ element.addEventListener('click', callback);
+ this.elementCallbackMap.set(element, callback);
+ }
+ });
+
+ // Configure on-click listeners for line spacing.
+ const onLineSpacingClick = (element: number) => {
+ let data: number|undefined;
+
+ switch (element) {
+ case MenuStateValue.LINE_STANDARD:
+ chrome.readingMode.onStandardLineSpacing();
+ data = chrome.readingMode.standardLineSpacing;
+ break;
+ case MenuStateValue.LOOSE:
+ chrome.readingMode.onLooseLineSpacing();
+ data = chrome.readingMode.looseLineSpacing;
+ break;
+ case MenuStateValue.VERY_LOOSE:
+ chrome.readingMode.onVeryLooseLineSpacing();
+ data = chrome.readingMode.veryLooseLineSpacing;
+ break;
+ default:
+ // Do nothing;
+ }
+
+ chrome.metricsPrivate.recordEnumerationValue(
+ SETTINGS_CHANGE_UMA, ReadAnythingSettingsChange.LINE_HEIGHT_CHANGE,
+ ReadAnythingSettingsChange.COUNT);
+ if (this.contentPage && data) {
+ this.contentPage.updateLineSpacing(
+ chrome.readingMode.getLineSpacingValue(data));
+ }
+ this.closeMenus_();
+ };
+
+ // Configure on-click listeners for letter spacing.
+ const onLetterSpacingClick = (element: number) => {
+ let data: number|undefined;
+
+ switch (element) {
+ case MenuStateValue.LETTER_STANDARD:
+ chrome.readingMode.onStandardLetterSpacing();
+ data = chrome.readingMode.standardLetterSpacing;
+ break;
+ case MenuStateValue.WIDE:
+ chrome.readingMode.onWideLetterSpacing();
+ data = chrome.readingMode.wideLetterSpacing;
+ break;
+ case MenuStateValue.VERY_WIDE:
+ chrome.readingMode.onVeryWideLetterSpacing();
+ data = chrome.readingMode.veryWideLetterSpacing;
+ break;
+ default:
+ // Do nothing;
+ }
+
+ chrome.metricsPrivate.recordEnumerationValue(
+ SETTINGS_CHANGE_UMA, ReadAnythingSettingsChange.LETTER_SPACING_CHANGE,
+ ReadAnythingSettingsChange.COUNT);
+ if (this.contentPage && data) {
+ this.contentPage.updateLetterSpacing(
+ chrome.readingMode.getLetterSpacingValue(data));
+ }
+ this.closeMenus_();
+ };
+
+ // Configure on-click listeners for theme.
+ const onThemeClick = (element: number) => {
+ let colorSuffix: string|undefined;
+
+ switch (element) {
+ case MenuStateValue.DEFAULT_COLOR:
+ chrome.readingMode.onDefaultTheme();
+ colorSuffix = '';
+ break;
+ case MenuStateValue.LIGHT:
+ chrome.readingMode.onLightTheme();
+ colorSuffix = '-light';
+ break;
+ case MenuStateValue.DARK:
+ chrome.readingMode.onDarkTheme();
+ colorSuffix = '-dark';
+ break;
+ case MenuStateValue.YELLOW:
+ chrome.readingMode.onYellowTheme();
+ colorSuffix = '-yellow';
+ break;
+ case MenuStateValue.BLUE:
+ chrome.readingMode.onBlueTheme();
+ colorSuffix = '-blue';
+ break;
+ default:
+ // Do nothing;
+ }
+
+ chrome.metricsPrivate.recordEnumerationValue(
+ SETTINGS_CHANGE_UMA, ReadAnythingSettingsChange.THEME_CHANGE,
+ ReadAnythingSettingsChange.COUNT);
+ if (this.contentPage && (colorSuffix !== undefined)) {
+ this.contentPage.updateThemeFromWebUi(colorSuffix);
+ }
+ this.closeMenus_();
+ };
+ this.addOnClickListeners(this.$.lineSpacingSubmenu, onLineSpacingClick);
+ this.addOnClickListeners(this.$.colorSubmenu, onThemeClick);
+ this.addOnClickListeners(this.$.letterSpacingSubmenu, onLetterSpacingClick);
+ }
+
+ override disconnectedCallback() {
+ super.disconnectedCallback();
+ this.removeOnClickListeners(this.$.fontSubmenu);
+ this.removeOnClickListeners(this.$.lineSpacingSubmenu);
+ this.removeOnClickListeners(this.$.colorSubmenu);
+ this.removeOnClickListeners(this.$.letterSpacingSubmenu);
+ }
+
+ updateUiForPlaying() {
+ const shadowRoot = this.shadowRoot;
+ assert(shadowRoot);
+ const button = shadowRoot.getElementById('play-pause');
+ assert(button);
+ button.setAttribute('iron-icon', 'read-anything-20:pause');
+ this.isPaused = false;
+ }
+
+ updateUiForPausing() {
+ const shadowRoot = this.shadowRoot;
+ assert(shadowRoot);
+ const button = shadowRoot.getElementById('play-pause');
+ assert(button);
+ button.setAttribute('iron-icon', 'read-anything-20:play');
+ this.isPaused = true;
+ }
+
+ private removeOnClickListeners(menu: CrActionMenuElement) {
+ const nodes = Array.from(menu.children);
+ nodes.forEach((element) => {
+ if ((element instanceof HTMLButtonElement) &&
+ !element.classList.contains('back') &&
+ (element.className === 'dropdown-item')) {
+ const callback = this.elementCallbackMap.get(element);
+ if (callback) {
+ element.removeEventListener('click', callback);
+ }
+ this.elementCallbackMap.delete(element);
+ }
+ });
+ }
+
+ private addOnClickListeners(
+ menu: CrActionMenuElement,
+ onMenuElementClick: (element: number) => void) {
+ const nodes = Array.from(menu.children);
+ nodes.forEach((element) => {
+ if ((element instanceof HTMLButtonElement) &&
+ !element.classList.contains('back') && element.hasAttribute('data')) {
+ const callback = () => {
+ onMenuElementClick(parseInt(element.getAttribute('data')!));
+ };
+ this.elementCallbackMap.set(element, callback);
+ element.addEventListener('click', callback);
+ }
+ });
+ }
+
+ private closeMenus_() {
+ this.$.colorSubmenu.close();
+ this.$.lineSpacingSubmenu.close();
+ this.$.letterSpacingSubmenu.close();
+ this.$.fontSubmenu.close();
+ }
+
+ private onShowColorSubMenuClick_(event: MouseEvent) {
+ this.openMenu_(this.$.colorSubmenu, event.target as HTMLElement);
+ }
+
+ private onShowLineSpacingSubMenuClick_(event: MouseEvent) {
+ this.openMenu_(this.$.lineSpacingSubmenu, event.target as HTMLElement);
+ }
+
+ private onShowLetterSpacingSubMenuClick_(event: MouseEvent) {
+ this.openMenu_(this.$.letterSpacingSubmenu, event.target as HTMLElement);
+ }
+
+ private onShowFontSubMenuClick_(event: MouseEvent) {
+ this.openMenu_(this.$.fontSubmenu, event.target as HTMLElement);
+ }
+
+ private onShowFontSizeMenuClick_(event: MouseEvent) {
+ this.openMenu_(this.$.fontSizeMenu, event.target as HTMLElement);
+ }
+
+ private openMenu_(menuToOpen: CrActionMenuElement, target: HTMLElement) {
+ this.closeMenus_();
+
+ const shadowRoot = this.shadowRoot;
+ assert(shadowRoot);
+ menuToOpen.showAt(target, {
+ anchorAlignmentX: AnchorAlignment.AFTER_START,
+ anchorAlignmentY: AnchorAlignment.AFTER_END,
+ noOffset: true,
+ });
+ }
+
+ private onFontClick_(fontName: string) {
+ chrome.metricsPrivate.recordEnumerationValue(
+ SETTINGS_CHANGE_UMA, ReadAnythingSettingsChange.FONT_CHANGE,
+ ReadAnythingSettingsChange.COUNT);
+ chrome.readingMode.onFontChange(fontName);
+ if (this.contentPage) {
+ this.contentPage.updateFont(fontName);
+ }
+
+ this.closeMenus_();
+ }
+
+ private onFontSizeIncreaseClick_() {
+ this.updateFontSize_(true);
+ }
+
+ private onFontSizeDecreaseClick_() {
+ this.updateFontSize_(false);
+ }
+
+ private updateFontSize_(increase: boolean) {
+ chrome.metricsPrivate.recordEnumerationValue(
+ SETTINGS_CHANGE_UMA, ReadAnythingSettingsChange.FONT_SIZE_CHANGE,
+ ReadAnythingSettingsChange.COUNT);
+ chrome.readingMode.onFontSizeChanged(increase);
+ if (this.contentPage) {
+ this.contentPage.updateFontSize();
+ }
+ // Don't close the menu
+ }
+
+ private onFontResetClick_() {
+ chrome.metricsPrivate.recordEnumerationValue(
+ SETTINGS_CHANGE_UMA, ReadAnythingSettingsChange.FONT_SIZE_CHANGE,
+ ReadAnythingSettingsChange.COUNT);
+ chrome.readingMode.onFontSizeReset();
+ if (this.contentPage) {
+ this.contentPage.updateFontSize();
+ }
+ }
+
+ private onPlayPauseClick_() {
+ if (this.isPaused) {
+ this.updateUiForPlaying();
+ if (this.contentPage) {
+ this.contentPage.playSpeech();
+ }
+ } else {
+ this.updateUiForPausing();
+ if (this.contentPage) {
+ this.contentPage.stopSpeech();
+ }
+ }
+ }
+}
+
+customElements.define('read-anything-toolbar', ReadAnythingToolbar);
diff --git a/chromium/chrome/browser/resources/side_panel/reading_list/app.html b/chromium/chrome/browser/resources/side_panel/reading_list/app.html
index 9f7e2f47bc5..625d88d0e1b 100644
--- a/chromium/chrome/browser/resources/side_panel/reading_list/app.html
+++ b/chromium/chrome/browser/resources/side_panel/reading_list/app.html
@@ -33,7 +33,7 @@
}
sp-heading {
- margin: 8px var(--mwb-list-item-horizontal-margin);
+ margin: 8px 16px;
}
.hr {
@@ -56,7 +56,8 @@
heading="$i18n{emptyStateHeader}"
body="[[getEmptyStateSubheaderText_()]]">
</sp-empty-state>
- <div id="readingListList" class="sp-scroller" on-keydown="onItemKeyDown_"
+ <div id="readingListList" class="sp-scroller sp-scroller-top-of-page"
+ on-keydown="onItemKeyDown_"
hidden$="[[!shouldShowList_(unreadItems_, readItems_)]]">
<div class="sp-card" hidden="[[!unreadItems_.length]]">
<sp-heading compact hide-back-button>
diff --git a/chromium/chrome/browser/resources/side_panel/reading_list/reading_list_item.html b/chromium/chrome/browser/resources/side_panel/reading_list/reading_list_item.html
index 6cf97bd3a45..5d0f0bd85b8 100644
--- a/chromium/chrome/browser/resources/side_panel/reading_list/reading_list_item.html
+++ b/chromium/chrome/browser/resources/side_panel/reading_list/reading_list_item.html
@@ -9,7 +9,7 @@
title="[[data.title]]"
description="[[data.displayUrl]]"
reverse-elide-description
- timestamp="[[data.displayTimeSinceUpdate]]"
+ description-meta="[[data.displayTimeSinceUpdate]]"
url="[[data.url.url]]">
<cr-icon-button slot="suffix-icon" id="updateStatusButton" disable-ripple
aria-label="[[getUpdateStatusButtonTooltip_('$i18n{tooltipMarkAsUnread}',
diff --git a/chromium/chrome/browser/resources/side_panel/shared/sp_shared_style.css b/chromium/chrome/browser/resources/side_panel/shared/sp_shared_style.css
index 65546409b5a..b4a88d7728c 100644
--- a/chromium/chrome/browser/resources/side_panel/shared/sp_shared_style.css
+++ b/chromium/chrome/browser/resources/side_panel/shared/sp_shared_style.css
@@ -44,20 +44,18 @@
:host-context([chrome-refresh-2023]) .sp-scroller::-webkit-scrollbar-thumb {
background: var(--color-side-panel-scrollbar-thumb);
background-clip: content-box;
- border: solid 2px transparent;
+ border: solid 1.5px transparent;
border-radius: 100px;
}
-:host-context([chrome-refresh-2023]) .sp-card:has(.sp-scroller) {
- padding-block-end: 0;
+:host-context([chrome-refresh-2023])
+ .sp-scroller-top-of-page::-webkit-scrollbar-track {
+ margin-block-start: var(--sp-body-padding);
}
-:host-context([chrome-refresh-2023]) .sp-card .sp-scroller {
- border-bottom-left-radius: 12px;
- border-bottom-right-radius: 12px;
- box-sizing: border-box;
- margin-inline-end: calc(-1 * var(--sp-body-padding));
- padding-block-end: var(--sp-card-block-padding);
+:host-context([chrome-refresh-2023])
+ .sp-scroller-bottom-of-page::-webkit-scrollbar-track {
+ margin-block-end: var(--sp-body-padding);
}
:host-context([chrome-refresh-2023]) .sp-scroller .sp-card,
@@ -141,13 +139,3 @@
border-radius: 12px;
box-shadow: var(--cr-elevation-3);
}
-
-:host-context([chrome-refresh-2023]) cr-action-menu {
- --cr-hairline: 1px solid var(--color-side-panel-menu-divider);
- --cr-action-menu-disabled-item-color: var(--color-side-panel-menu-disabled);
- --cr-action-menu-disabled-item-opacity: 1;
- --cr-menu-background-color: var(--color-side-panel-menu-background);
- --cr-menu-background-focus-color: var(--cr-hover-background-color);
- --cr-menu-shadow: var(--cr-elevation-2);
- --cr-primary-text-color: var(--color-side-panel-menu-foreground);
-}
diff --git a/chromium/chrome/browser/resources/signin/BUILD.gn b/chromium/chrome/browser/resources/signin/BUILD.gn
index 8348fe4d2da..e0f26ff37a7 100644
--- a/chromium/chrome/browser/resources/signin/BUILD.gn
+++ b/chromium/chrome/browser/resources/signin/BUILD.gn
@@ -33,15 +33,15 @@ build_webui("build") {
"profile_customization/profile_customization.html",
"signin_email_confirmation/signin_email_confirmation.html",
"signin_error/signin_error.html",
+ "signin_reauth/images/account_passwords_reauth_illustration_dark.svg",
+ "signin_reauth/images/account_passwords_reauth_illustration.svg",
+ "signin_reauth/signin_reauth.html",
]
}
if (enable_dice_support) {
static_files += [
"dice_web_signin_intercept/dice_web_signin_intercept.html",
"dice_web_signin_intercept/images/split_header.svg",
- "signin_reauth/images/account_passwords_reauth_illustration_dark.svg",
- "signin_reauth/images/account_passwords_reauth_illustration.svg",
- "signin_reauth/signin_reauth.html",
]
}
@@ -56,13 +56,12 @@ build_webui("build") {
"profile_customization/profile_customization_app.ts",
"signin_email_confirmation/signin_email_confirmation_app.ts",
"signin_error/signin_error_app.ts",
+ "signin_reauth/signin_reauth_app.ts",
]
}
if (enable_dice_support) {
- web_component_files += [
- "dice_web_signin_intercept/dice_web_signin_intercept_app.ts",
- "signin_reauth/signin_reauth_app.ts",
- ]
+ web_component_files +=
+ [ "dice_web_signin_intercept/dice_web_signin_intercept_app.ts" ]
}
non_web_component_files = [
@@ -75,12 +74,12 @@ build_webui("build") {
"enterprise_profile_welcome/enterprise_profile_welcome_browser_proxy.ts",
"profile_customization/profile_customization_browser_proxy.ts",
"signin_error/signin_error.ts",
+ "signin_reauth/signin_reauth_browser_proxy.ts",
]
}
if (enable_dice_support) {
non_web_component_files += [
"dice_web_signin_intercept/dice_web_signin_intercept_browser_proxy.ts",
- "signin_reauth/signin_reauth_browser_proxy.ts",
]
}
@@ -97,6 +96,7 @@ build_webui("build") {
ts_deps = [
"//third_party/polymer/v3_0:library",
"//ui/webui/resources/cr_components/customize_themes:build_ts",
+ "//ui/webui/resources/cr_components/theme_color_picker:build_ts",
"//ui/webui/resources/cr_elements:build_ts",
"//ui/webui/resources/js:build_ts",
]
diff --git a/chromium/chrome/browser/resources/signin/dice_web_signin_intercept/DIR_METADATA b/chromium/chrome/browser/resources/signin/dice_web_signin_intercept/DIR_METADATA
new file mode 100644
index 00000000000..3a626e17937
--- /dev/null
+++ b/chromium/chrome/browser/resources/signin/dice_web_signin_intercept/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chrome/browser/profiles/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/signin/dice_web_signin_intercept/OWNERS b/chromium/chrome/browser/resources/signin/dice_web_signin_intercept/OWNERS
new file mode 100644
index 00000000000..5fd4d46edf4
--- /dev/null
+++ b/chromium/chrome/browser/resources/signin/dice_web_signin_intercept/OWNERS
@@ -0,0 +1,2 @@
+file://components/signin/OWNERS
+gabolvr@google.com
diff --git a/chromium/chrome/browser/resources/signin/enterprise_profile_welcome/DIR_METADATA b/chromium/chrome/browser/resources/signin/enterprise_profile_welcome/DIR_METADATA
new file mode 100644
index 00000000000..aa6b82f423e
--- /dev/null
+++ b/chromium/chrome/browser/resources/signin/enterprise_profile_welcome/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//components/policy/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/signin/enterprise_profile_welcome/enterprise_profile_welcome_app.html b/chromium/chrome/browser/resources/signin/enterprise_profile_welcome/enterprise_profile_welcome_app.html
index 415361b3cbc..a3a99e26845 100644
--- a/chromium/chrome/browser/resources/signin/enterprise_profile_welcome/enterprise_profile_welcome_app.html
+++ b/chromium/chrome/browser/resources/signin/enterprise_profile_welcome/enterprise_profile_welcome_app.html
@@ -40,6 +40,14 @@
box-sizing: border-box;
position: absolute;
width: 100%;
+ /* Hotfix for cases where the linkData checkbox may be pushed under the action container
+ so that it remains clickable. */
+ pointer-events: none;
+ }
+
+ .action-container cr-button {
+ /* Reverts inherited pointer-events value from .action-container. */
+ pointer-events: all;
}
.action-container.dialog {
diff --git a/chromium/chrome/browser/resources/signin/profile_customization/DIR_METADATA b/chromium/chrome/browser/resources/signin/profile_customization/DIR_METADATA
new file mode 100644
index 00000000000..3a626e17937
--- /dev/null
+++ b/chromium/chrome/browser/resources/signin/profile_customization/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chrome/browser/profiles/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/signin/profile_customization/OWNERS b/chromium/chrome/browser/resources/signin/profile_customization/OWNERS
new file mode 100644
index 00000000000..5fd4d46edf4
--- /dev/null
+++ b/chromium/chrome/browser/resources/signin/profile_customization/OWNERS
@@ -0,0 +1,2 @@
+file://components/signin/OWNERS
+gabolvr@google.com
diff --git a/chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.html b/chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.html
index 2fd6e2c4df6..662454bbbb3 100644
--- a/chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.html
+++ b/chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.html
@@ -132,6 +132,13 @@
--cr-customize-themes-icon-size: 36px;
}
+ cr-theme-color-picker {
+ --cr-theme-color-padding: 6px;
+ --cr-theme-color-picker-column-width: 50px;
+ --cr-theme-color-picker-grid-gap: 7px;
+ --cr-theme-color-picker-grid-padding: 0;
+ }
+
.action-container {
padding: 0 16px 16px;
}
@@ -225,9 +232,8 @@
}
</style>
-<cr-view-manager role="dialog" id="viewManager" aria-labelledby="title"
- aria-describedby="content"
- class$="[[getDialogDesignClass_(profileCustomizationInDialogDesign_)]]">
+<cr-view-manager role="dialog" id="viewManager" class="in-dialog-design"
+ aria-labelledby="title" aria-describedby="content">
<div id="customizeDialog" slot="view" class="active">
<!-- An extra wrapper is needed because cr-view-manager incorrectly
displays views with display: flex -->
@@ -256,9 +262,6 @@
<div id="title">
[[welcomeTitle_]]
</div>
- <template is="dom-if" if="[[!profileCustomizationInDialogDesign_]]">
- <div id="content">$i18n{profileCustomizationText}</div>
- </template>
</div>
<cr-input id="nameInput" pattern=".*\\S.*" value="{{profileName_}}"
@@ -269,8 +272,13 @@
</cr-input>
<div id="pickThemeContainer">
- <cr-customize-themes id="themeSelector">
- </cr-customize-themes>
+ <template is="dom-if" if="[[isChromeRefresh2023_]]">
+ <cr-theme-color-picker columns="6"></cr-theme-color-picker>
+ </template>
+ <template is="dom-if" if="[[!isChromeRefresh2023_]]">
+ <cr-customize-themes id="themeSelector">
+ </cr-customize-themes>
+ </template>
</div>
<div class="action-container">
diff --git a/chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.ts b/chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.ts
index 6a2576a3072..3546cf3acdb 100644
--- a/chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.ts
+++ b/chromium/chrome/browser/resources/signin/profile_customization/profile_customization_app.ts
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'chrome://resources/cr_components/customize_themes/customize_themes.js';
+import 'chrome://resources/cr_components/theme_color_picker/theme_color_picker.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_input/cr_input.js';
import 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js';
@@ -19,10 +20,10 @@ import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_butto
import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
import {AvatarIcon} from 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js';
import {CrViewManagerElement} from 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js';
-import {assert} from 'chrome://resources/js/assert_ts.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './profile_customization_app.html.js';
@@ -33,8 +34,8 @@ export interface ProfileCustomizationAppElement {
$: {
doneButton: CrButtonElement,
nameInput: CrInputElement,
+ pickThemeContainer: HTMLElement,
title: HTMLElement,
- themeSelector: CustomizeThemesElement,
viewManager: CrViewManagerElement,
};
}
@@ -83,15 +84,15 @@ export class ProfileCustomizationAppElement extends
/** The currently selected profile avatar, if any. */
selectedAvatar_: Object,
- profileCustomizationInDialogDesign_: {
+ isLocalProfileCreation_: {
type: Boolean,
- value: () =>
- loadTimeData.getBoolean('profileCustomizationInDialogDesign'),
+ value: () => loadTimeData.getBoolean('isLocalProfileCreation'),
},
- isLocalProfileCreation_: {
+ isChromeRefresh2023_: {
type: Boolean,
- value: () => loadTimeData.getBoolean('isLocalProfileCreation'),
+ value: () =>
+ document.documentElement.hasAttribute('chrome-refresh-2023'),
},
};
}
@@ -103,8 +104,8 @@ export class ProfileCustomizationAppElement extends
private availableIcons_: AvatarIcon[];
private selectedAvatar_: AvatarIcon;
private confirmedAvatar_: AvatarIcon;
- private profileCustomizationInDialogDesign_: boolean;
private isLocalProfileCreation_: boolean;
+ private isChromeRefresh2023_: boolean;
private profileCustomizationBrowserProxy_: ProfileCustomizationBrowserProxy =
ProfileCustomizationBrowserProxyImpl.getInstance();
@@ -135,7 +136,11 @@ export class ProfileCustomizationAppElement extends
* native.
*/
private onDoneCustomizationClicked_() {
- this.$.themeSelector.confirmThemeChanges();
+ if (!this.isChromeRefresh2023_) {
+ const themeSelector = this.$.pickThemeContainer.querySelector(
+ '#themeSelector')! as CustomizeThemesElement;
+ themeSelector.confirmThemeChanges();
+ }
this.profileCustomizationBrowserProxy_.done(this.profileName_);
}
@@ -148,18 +153,13 @@ export class ProfileCustomizationAppElement extends
'--header-background-color', profileInfo.backgroundColor);
this.pictureUrl_ = profileInfo.pictureUrl;
this.isManaged_ = profileInfo.isManaged;
- if (this.profileCustomizationInDialogDesign_) {
- this.welcomeTitle_ = this.isLocalProfileCreation_ ?
- this.i18n('localProfileCreationTitle') :
- this.i18n('profileCustomizationTitle');
- } else {
- this.welcomeTitle_ = profileInfo.welcomeTitle;
- }
+ this.welcomeTitle_ = this.isLocalProfileCreation_ ?
+ this.i18n('localProfileCreationTitle') :
+ this.i18n('profileCustomizationTitle');
}
private shouldShowCancelButton_(): boolean {
- return this.profileCustomizationInDialogDesign_ &&
- !this.isLocalProfileCreation_;
+ return !this.isLocalProfileCreation_;
}
private onSkipCustomizationClicked_() {
@@ -169,14 +169,14 @@ export class ProfileCustomizationAppElement extends
private onDeleteProfileClicked_() {
// Unsaved theme color changes cause an error in `ProfileCustomizationUI`
// destructor when deleting the profile.
- this.$.themeSelector.confirmThemeChanges();
+ if (!this.isChromeRefresh2023_) {
+ const themeSelector = this.$.pickThemeContainer.querySelector(
+ '#themeSelector')! as CustomizeThemesElement;
+ themeSelector.confirmThemeChanges();
+ }
this.profileCustomizationBrowserProxy_.deleteProfile();
}
- private getDialogDesignClass_(inDialogDesign: boolean): string {
- return inDialogDesign ? 'in-dialog-design' : '';
- }
-
private onCustomizeAvatarClick_() {
assert(this.isLocalProfileCreation_);
this.$.viewManager.switchView('selectAvatarDialog', 'fade-in', 'fade-out');
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/BUILD.gn b/chromium/chrome/browser/resources/signin/profile_picker/BUILD.gn
index 2a142714487..5d02bc8593b 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/BUILD.gn
+++ b/chromium/chrome/browser/resources/signin/profile_picker/BUILD.gn
@@ -16,7 +16,6 @@ build_webui("build") {
web_component_files = [
"profile_card_menu.ts",
"profile_card.ts",
- "profile_creation_flow/local_profile_customization.ts",
"profile_creation_flow/profile_type_choice.ts",
"profile_picker_app.ts",
"profile_picker_main_view.ts",
@@ -29,6 +28,7 @@ build_webui("build") {
}
non_web_component_files = [
+ "drag_drop_reorder_tile_list_delegate.ts",
"ensure_lazy_loaded.ts",
"lazy_load.ts",
"manage_profiles_browser_proxy.ts",
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/DIR_METADATA b/chromium/chrome/browser/resources/signin/profile_picker/DIR_METADATA
new file mode 100644
index 00000000000..3a626e17937
--- /dev/null
+++ b/chromium/chrome/browser/resources/signin/profile_picker/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chrome/browser/profiles/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/drag_drop_reorder_tile_list_delegate.ts b/chromium/chrome/browser/resources/signin/profile_picker/drag_drop_reorder_tile_list_delegate.ts
new file mode 100644
index 00000000000..0c96bc5e5b8
--- /dev/null
+++ b/chromium/chrome/browser/resources/signin/profile_picker/drag_drop_reorder_tile_list_delegate.ts
@@ -0,0 +1,491 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {EventTracker} from 'chrome://resources/js/event_tracker.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+function isIndexInBetweenStartEnd(
+ index: number, start: number, end: number): boolean {
+ if (start === -1 || end === -1) {
+ return false;
+ }
+
+ const lower = Math.min(start, end);
+ const higher = Math.max(start, end);
+
+ return lower <= index && index <= higher;
+}
+
+// Class tags to use throughout the drag events.
+//
+// Tag for the tile that is being dragged in the drag cycle event.
+const DRAGGING_TAG: string = 'dragging';
+// Tag for a tile that is done shifting during a drag cycle event.
+const SHIFTED_TAG: string = 'shifted';
+// Tag for a tile while it is shifting (applying the transition effect).
+// This could mean either shifting in (to new position) or shifting back (to
+// initial position).
+const SHIFTING_TAG: string = 'shifting';
+
+// Interface to interact with the real underlying tile list that expects to have
+// the Drag and Drop functionality.
+export interface DraggableTileListInterface {
+ // Given an index, returns the HTMLElement corresponding to the draggable
+ // tile.
+ getDraggableTile(index: number): HTMLElement;
+ // Given a draggable tile, return its corresponding index in the tile list.
+ getDraggableTileIndex(tile: HTMLElement): number;
+
+ // On drag end, send back the indices of the tiles that were affected.
+ // initialIndex: is the index of the tile that was dragged.
+ // finalIndex: is the index of the tile that was targeted, the location at
+ // which the dragging tile was dropped.
+ onDradEnd(initialIndex: number, finalIndex: number): void;
+}
+
+// This delegate class allows any Polymer list container of tiles to add the
+// drag and drop with reordering functionality.
+//
+// The events that will be redirected to this delegate are:
+// - 'dragstart': triggered once when a tile is initially being dragged.
+// - 'dragenter': triggered whenever hovering over a tile with the dragging
+// tile.
+// - 'dragover': triggered continuously as long as the dragging tile is over
+// another tile.
+// - 'dragend': triggered once when a tile drag stops, after a drop.
+// A full drag event cycle starts with 'dragstart' and ends with 'dragend'.
+//
+// To activate the drag and drop functionality, a call to
+// `initializeListeners()` (only once) will attach all necessary 'drag-' event
+// listeners to the proper tiles. This method must be called once the HTML
+// tiles, that are intended to be drag and dropped, are properly rendered.
+export class DragDropReorderTileListDelegate {
+ private polymerElement_: PolymerElement;
+ private tileListInterface_: DraggableTileListInterface;
+ private tileListName_: string;
+ private tileCount_: number;
+ private transitionDuration_: number; // Unit: ms.
+
+ private initialized_ = false;
+ private isDragEnabled_ = true;
+
+ private isDragging_: boolean = false;
+ private draggingTile_: HTMLElement|null = null;
+ private dragStartIndex_: number = -1;
+ private dropTargetIndex_: number = -1;
+
+ private eventTracker_: EventTracker;
+
+ // ---------------------------------------------------------------------------
+ // public section:
+
+ constructor(
+ polymer: PolymerElement, tileListInterface: DraggableTileListInterface,
+ tileListName: string, tileCount: number,
+ transitionDuration: number = 300) {
+ this.polymerElement_ = polymer;
+ this.tileListInterface_ = tileListInterface;
+ this.tileListName_ = tileListName;
+ this.tileCount_ = tileCount;
+ this.transitionDuration_ = transitionDuration;
+
+ this.eventTracker_ = new EventTracker();
+ }
+
+ // Initialize tiles to be able to react to drag events and shift with
+ // transition effect based on the 'transform' property.
+ // Expected to be called once so that a single event of each type is added to
+ // the tiles.
+ initializeListeners() {
+ assert(!this.initialized_);
+ this.initialized_ = true;
+
+ for (let i = 0; i < this.tileCount_; ++i) {
+ const tile = this.getDraggableTile_(i);
+ tile.draggable = true;
+
+ this.eventTracker_.add(tile, 'dragstart', (event: DragEvent) => {
+ this.onDragStart_(event);
+ });
+
+ this.eventTracker_.add(tile, 'dragenter', (event: DragEvent) => {
+ this.onDragEnter_(event);
+ });
+
+ this.eventTracker_.add(tile, 'dragover', (event: DragEvent) => {
+ this.onDragOver_(event);
+ }, false);
+
+ // TODO(http://crbug/1466146): check if this event delay can be removed
+ // for MacOS. It is making the drop have an awkward movement.
+ this.eventTracker_.add(tile, 'dragend', (event: DragEvent) => {
+ this.onDragEnd_(event);
+ });
+ }
+ }
+
+ // Clear all drag events listeners and reset tiles drag state.
+ clearListeners() {
+ if (!this.initialized_) {
+ return;
+ }
+
+ for (let i = 0; i < this.tileCount_; ++i) {
+ const tile = this.getDraggableTile_(i);
+ tile.draggable = false;
+ }
+
+ this.eventTracker_.removeAll();
+ }
+
+ // Toggle the dragging properties of the tiles on or off.
+ // This could be useful to temporarily turning off the functionality (e.g.
+ // when hovering over some editable elements that are part of a draggable
+ // tile).
+ toggleDrag(toggle: boolean) {
+ assert(this.initialized_);
+ this.isDragEnabled_ = toggle;
+ }
+
+ // ---------------------------------------------------------------------------
+ // private section
+
+ // Event 'dragstart' is applied on the tile that will be dragged. We store the
+ // tile being dragged in temporary member variables that will be used
+ // throughout a single drag event cycle. We need to store information in
+ // member variables since future events will be triggered in different stack
+ // calls.
+ private onDragStart_(event: DragEvent) {
+ if (!this.isDragEnabled_) {
+ event.preventDefault();
+ return;
+ }
+
+ this.isDragging_ = true;
+ // 'event.target' corresponds to the tile being dragged. Implicit cast to
+ // an HTMLElement.
+ const tile = event.target as HTMLElement;
+ this.markDraggingTile_(tile);
+
+ // Prepare all tiles transition effects at the beginning of the drag event
+ // cycle.
+ this.setAllTilesTransitions_();
+
+ // `event.dataTransfer` is null in tests.
+ if (event.dataTransfer) {
+ const pos = tile.getBoundingClientRect();
+ // Make the dragging image the tile itself so that reaction to any sub
+ // element that is also draggable shows the whole tile being dragged and
+ // not the sub element only (e.g. images).
+ event.dataTransfer.setDragImage(
+ tile, event.x - pos.left, event.y - pos.top);
+ }
+ }
+
+ // Event 'dragenter' is applied on the tile that is being hovered over.
+ // We shift all tiles between the initial dragging tile and the one that we
+ // just entered which will create the reordering functionality.
+ private onDragEnter_(event: DragEvent) {
+ // Check that this event was triggered as part of the tile drag event cycle
+ // otherwise we discard this event.
+ if (!this.isDragging_) {
+ return;
+ }
+
+ event.preventDefault();
+
+ // Tile that the dragging tile entered.
+ const enteredTile = event.target as HTMLElement;
+
+ // Do not react to shifting or dragging tile.
+ if (enteredTile.classList.contains(SHIFTING_TAG) ||
+ enteredTile.classList.contains(DRAGGING_TAG)) {
+ return;
+ }
+
+ const newDragTargetIndex = this.computeNewTargetIndex_(enteredTile);
+
+ // Reset any tile that shifted to its initial position, except the tiles
+ // that do not need to be shifted back based on the new drag target index.
+ this.resetShiftedTiles_(newDragTargetIndex);
+
+ // Set the new drag target index for future drag enter events.
+ this.dropTargetIndex_ = newDragTargetIndex;
+
+ // Increment of +/-1 depending on the direction of the dragging event.
+ const indexIncrement =
+ Math.sign(this.dragStartIndex_ - this.dropTargetIndex_);
+ // Loop from target to start with the direction increment.
+ // Shift all tiles by 1 spot based on the direction.
+ for (let i = this.dropTargetIndex_; i !== this.dragStartIndex_;
+ i += indexIncrement) {
+ const tileToShift = this.getDraggableTile_(i);
+ // No need to shift tiles that are already shifted.
+ if (tileToShift.classList.contains(SHIFTED_TAG)) {
+ continue;
+ }
+
+ const tileAtTargetLocation = this.getDraggableTile_(i + indexIncrement);
+ this.shiftTile_(tileToShift, tileAtTargetLocation);
+ }
+ }
+
+ // Event 'dragover' is applied on the tile that is being hovered over, it will
+ // be periodically triggered as long as the dragging tile is over a specific
+ // tile. We use this event to make sure we do not miss any drag enter event
+ // that might have happened while tiles are shifting.
+ private onDragOver_(event: DragEvent) {
+ // Check that this event was triggered as part of the tile drag event cycle
+ // otherwise we discard this event.
+ if (!this.isDragging_) {
+ return;
+ }
+
+ event.preventDefault();
+
+ const overTile = event.target as HTMLElement;
+ // Do not react to shifting or dragging tiles.
+ if (overTile.classList.contains(SHIFTING_TAG) ||
+ overTile.classList.contains(DRAGGING_TAG)) {
+ return;
+ }
+
+ // If the dragging tile stays over a shifting tile while it is shifting no
+ // drag enter event will be called, or a drag enter event can be missed
+ // while an element is shifting, so we simulate another drag enter event
+ // after the shifting is done.
+ this.onDragEnter_(event);
+ }
+
+ // Event 'dragend` is applied on the tile that was dragged and now dropped. We
+ // restore all the temporary member variables to their original state. It is
+ // the end of the drag event cycle.
+ // If a valid target index results from the drag events, perform a reordering
+ // on the underlying list. Then notify the changes so that they are rendered.
+ // Transition effects are disabled at this point not to have back and forth
+ // animations.
+ private onDragEnd_(event: DragEvent) {
+ // The 'event.target' of the 'dragend' event is expected to be the same as
+ // the one that started the drag event cycle.
+ assert(this.draggingTile_);
+ assert(this.draggingTile_ === event.target as HTMLElement);
+
+ this.isDragging_ = false;
+
+ // Reset all the tiles that shifted during the drag events.
+ // Disable transition so that the change is instant and re-alligned by the
+ // data change.
+ this.resetAllTilesWithoutTransition_();
+
+ if (this.dropTargetIndex_ !== -1) {
+ // In case a reorder should happen:
+ // - Apply the changes on the original list.
+ // - The changes will cause a re-rendering of the polymer element which
+ // will take into account the changes and have all the tiles at their
+ // right place.
+ this.applyChanges_();
+
+ // Notfiy the list of the changes.
+ this.tileListInterface_.onDradEnd(
+ this.dragStartIndex_, this.dropTargetIndex_);
+ }
+
+ this.dropTargetIndex_ = -1;
+ this.resetDraggingTile_();
+ }
+
+ // Tile 'tileToShift' will shift to the position of 'tileAtTargetLocation'.
+ // The shift happens by applying a transform on the tile. The transition
+ // effect is set to 'transform' in the 'setAllTilesTransitions()'.
+ //
+ // Shifting a tile steps:
+ // - mark the tile as `SHIFTING_TAG`.
+ // - apply the corresponding transform.
+ // - transition effect happening with a duration of
+ // 'this.transitionDuration_'.
+ // - delayed function call to switch the tag from `SHIFTING_TAG` to
+ // `SHIFTED_TAG`. after the transition effect is done.
+ private shiftTile_(
+ tileToShift: HTMLElement, tileAtTargetLocation: HTMLElement) {
+ // Tag tile as shifted.
+ tileToShift.classList.add(SHIFTING_TAG);
+
+ // Increase the 'zIndex' property of SHIFTING and SHIFTED tiles in order to
+ // give them priority for drag enter/over events over other elements.
+ tileToShift.style.zIndex = '2';
+
+ // Compute relative positions to apply to transform with XY Translation.
+ const diffx = tileToShift.offsetLeft - tileAtTargetLocation.offsetLeft;
+ const diffy = tileToShift.offsetTop - tileAtTargetLocation.offsetTop;
+ tileToShift.style.transform =
+ `translateX(${- diffx}px) translateY(${- diffy}px)`;
+
+ const onShiftTransitionEnd = () => {
+ tileToShift.ontransitionend = null;
+
+ tileToShift.classList.remove(SHIFTING_TAG);
+ // In case the dragging has stopped before the delayed function, we do
+ // not want to tag this tile.
+ if (this.isDragging_) {
+ tileToShift.classList.add(SHIFTED_TAG);
+ }
+ };
+
+ if (this.transitionDuration_ !== 0) {
+ tileToShift.ontransitionend = onShiftTransitionEnd;
+ } else {
+ // If `this.transitionDuration_` is 0, then we need to perform the ending
+ // function directly, as there is no transition happening. This could
+ // happen in tests or if the usage requires no transition.
+ onShiftTransitionEnd();
+ }
+ }
+
+ // Reset elements from start index until the end index.
+ // If some indices are between the start index and the new end index, do not
+ // reset these elements as their shifted position should not be modified.
+ private resetShiftedTiles_(newDragTargetIndex: number) {
+ if (this.dropTargetIndex_ === -1) {
+ return;
+ }
+
+ // Increment of +/-1 depending on the direction of the dragging event.
+ const indexIncrement =
+ Math.sign(this.dragStartIndex_ - this.dropTargetIndex_);
+ // Loop from target to start with the direction increment.
+ for (let i = this.dropTargetIndex_; i !== this.dragStartIndex_;
+ i += indexIncrement) {
+ // Do not reset tiles that have indices between the start and new drag
+ // target index since their shift should be kept.
+ if (!isIndexInBetweenStartEnd(
+ i, this.dragStartIndex_, newDragTargetIndex)) {
+ this.resetShiftedTile_(this.getDraggableTile_(i));
+ }
+ }
+ }
+
+ // Resetting a tile that was shifted to it's initial state by clearing the
+ // transform.
+ // - Transition effect happening while the tile is shifting back.
+ // - delayed function call to remove the `SHIFTING_TAG` tag after the
+ // transition is done.
+ private resetShiftedTile_(tileToShiftBack: HTMLElement) {
+ tileToShiftBack.style.transform = '';
+ tileToShiftBack.classList.remove(SHIFTED_TAG);
+ tileToShiftBack.classList.add(SHIFTING_TAG);
+
+ const onShiftBackTransitionEnd = () => {
+ tileToShiftBack.ontransitionend = null;
+
+ // Reset previously increased 'zIndex'.
+ tileToShiftBack.style.removeProperty('z-index');
+
+ tileToShiftBack.classList.remove(SHIFTING_TAG);
+ // Can potentially be added if the shift back happens before the end
+ // of the initial shift.
+ tileToShiftBack.classList.remove(SHIFTED_TAG);
+ };
+
+ if (this.transitionDuration_ !== 0) {
+ tileToShiftBack.ontransitionend = onShiftBackTransitionEnd;
+ } else {
+ // If `this.transitionDuration_` is 0, then we need to perform the ending
+ // function directly, as there is no transition happening. This could
+ // happen in tests or if the usage requires no transition.
+ onShiftBackTransitionEnd();
+ }
+ }
+
+ // Apply changes on the underlying tile list through the polymer element by
+ // performing two splices, the changes applied will cause a re-rendering of
+ // the polymer element.
+ private applyChanges_() {
+ // Remove the dragging tile from its original index.
+ const [draggingTile] = this.polymerElement_.splice(
+ this.tileListName_, this.dragStartIndex_, 1);
+ // Place it on the target index.
+ this.polymerElement_.splice(
+ this.tileListName_, this.dropTargetIndex_, 0, draggingTile);
+ }
+
+ // Compute the new drag target index based on the tile that is being hovered
+ // over.
+ // In case a tile is shifted by a previous reoredering, it's index is not
+ // adapted, therefore we should offset the new target index.
+ private computeNewTargetIndex_(enteredTile: HTMLElement): number {
+ let newTargetIndex = this.getDraggableTileIndex_(enteredTile);
+
+ // If the tile being dragged over was shifted by a previous reordering,
+ // it's index will be shifted by 1, so we need to offset it to get
+ // the right index.
+ if (enteredTile.classList.contains(SHIFTED_TAG)) {
+ const indexIncrement =
+ Math.sign(this.dragStartIndex_ - this.dropTargetIndex_);
+ newTargetIndex += indexIncrement;
+ }
+
+ return newTargetIndex;
+ }
+
+ // Prepare 'this.draggingTile_' member variable as the dragging tile.
+ // It will used throughout each drag event cycle and reset in the
+ // `resetDraggingTile_()` method which restore the tile to it's initial state.
+ private markDraggingTile_(element: HTMLElement) {
+ this.draggingTile_ = element;
+ this.draggingTile_.classList.add(DRAGGING_TAG);
+ this.dragStartIndex_ = this.getDraggableTileIndex_(this.draggingTile_);
+
+ // Apply specific style to hide the tile that is being dragged, making sure
+ // only the image that sticks on the mouse pointer to be displayed while
+ // dragging. A very low value different than 0 is needed, otherwise the
+ // element would be considered invisible and would not react to drag events
+ // anymore. A value of '0.001' is enough to simulate the 'invisible' effect.
+ this.draggingTile_.style.opacity = '0.001';
+ }
+
+ // Restores `this.draggingTile_` to it's initial state.
+ private resetDraggingTile_() {
+ this.draggingTile_!.style.removeProperty('opacity');
+
+ this.dragStartIndex_ = -1;
+ this.draggingTile_!.classList.remove(DRAGGING_TAG);
+ this.draggingTile_ = null;
+ }
+
+ // Clear all tiles transition effects, and remove the temporary transforms on
+ // all tiles that shifted or are shifting.
+ private resetAllTilesWithoutTransition_() {
+ // Reset all tiles potential transform values or shited/shifting values.
+ // Also clear all tiles transition effects so that the repositioning doesn't
+ // animate.
+ for (let i = 0; i < this.tileCount_; ++i) {
+ const tile = this.getDraggableTile_(i);
+ tile.classList.remove(SHIFTED_TAG);
+ tile.classList.remove(SHIFTING_TAG);
+ tile.style.removeProperty('transition');
+ tile.style.removeProperty('transform');
+ tile.style.removeProperty('z-index');
+ }
+ }
+
+ // Set all the tiles transition values. Transition will happen when the
+ // transform property is changed using the `this.transitionDuration_` value
+ // set at construction.
+ private setAllTilesTransitions_() {
+ for (let i = 0; i < this.tileCount_; ++i) {
+ const tile = this.getDraggableTile_(i);
+ tile.style.transition =
+ `transform ease-in-out ${this.transitionDuration_}ms`;
+ }
+ }
+
+ private getDraggableTile_(index: number) {
+ return this.tileListInterface_.getDraggableTile(index);
+ }
+
+ private getDraggableTileIndex_(tile: HTMLElement): number {
+ return this.tileListInterface_.getDraggableTileIndex(tile);
+ }
+}
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/ensure_lazy_loaded.ts b/chromium/chrome/browser/resources/signin/profile_picker/ensure_lazy_loaded.ts
index 9a970dce8a4..4220d5996e6 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/ensure_lazy_loaded.ts
+++ b/chromium/chrome/browser/resources/signin/profile_picker/ensure_lazy_loaded.ts
@@ -18,8 +18,7 @@ export function ensureLazyLoaded(): Promise<void> {
lazyLoadPromise = Promise
.all([
- 'profile-type-choice',
- 'local-profile-customization', 'profile-switch',
+ 'profile-type-choice', 'profile-switch',
// <if expr="chromeos_lacros">
'account-selection-lacros',
// </if>
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/lazy_load.ts b/chromium/chrome/browser/resources/signin/profile_picker/lazy_load.ts
index 225a40b875d..e1e1d9b0956 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/lazy_load.ts
+++ b/chromium/chrome/browser/resources/signin/profile_picker/lazy_load.ts
@@ -5,6 +5,5 @@
// <if expr="chromeos_lacros">
export {AccountSelectionLacrosElement} from './profile_creation_flow/account_selection_lacros.js';
// </if>
-export {LocalProfileCustomizationElement} from './profile_creation_flow/local_profile_customization.js';
export {ProfileTypeChoiceElement} from './profile_creation_flow/profile_type_choice.js';
export {ProfileSwitchElement} from './profile_switch.js';
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.ts b/chromium/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.ts
index e77326cead7..98d815ace7c 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.ts
+++ b/chromium/chrome/browser/resources/signin/profile_picker/manage_profiles_browser_proxy.ts
@@ -133,13 +133,6 @@ export interface ManageProfilesBrowserProxy {
getAvailableIcons(): Promise<AvatarIcon[]>;
/**
- * Creates local profile.
- */
- createProfile(
- profileName: string, profileColor: number, avatarIndex: number,
- createShortcut: boolean): void;
-
- /**
* Creates local profile and opens a profile customization modal dialog on a
* browser window.
* TODO(https://crbug.com/1282157): Add createShortcut parameter.
@@ -170,6 +163,15 @@ export interface ManageProfilesBrowserProxy {
*/
cancelProfileSwitch(): void;
+ /**
+ * Sends the profile order changes
+ * @param fromIndex the initial index of the tile that was dragged.
+ * @param toIndex the index to which the profile has been moved/dropped.
+ * All other profiles between `fromIndex` and `toIndex` +/-1 should be shifted
+ * by +/-1 depending on the change direction.
+ */
+ updateProfileOrder(fromIndex: number, toIndex: number): void;
+
// <if expr="chromeos_lacros">
/**
* Gets the available accounts, through WebUIListener.
@@ -246,14 +248,6 @@ export class ManageProfilesBrowserProxyImpl {
return sendWithPromise('getAvailableIcons');
}
- createProfile(
- profileName: string, profileColor: number, avatarIndex: number,
- createShortcut: boolean) {
- chrome.send(
- 'createProfile',
- [profileName, profileColor, avatarIndex, createShortcut]);
- }
-
createProfileAndOpenCustomizationDialog(profileColor: number) {
chrome.send('createProfileAndOpenCustomizationDialog', [profileColor]);
}
@@ -278,6 +272,10 @@ export class ManageProfilesBrowserProxyImpl {
chrome.send('cancelProfileSwitch');
}
+ updateProfileOrder(fromIndex: number, toIndex: number) {
+ chrome.send('updateProfileOrder', [fromIndex, toIndex]);
+ }
+
// <if expr="chromeos_lacros">
getAvailableAccounts() {
chrome.send('getAvailableAccounts');
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/profile_card.html b/chromium/chrome/browser/resources/signin/profile_picker/profile_card.html
index 71cfcdc8ea1..2c239a2a774 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/profile_card.html
+++ b/chromium/chrome/browser/resources/signin/profile_picker/profile_card.html
@@ -159,7 +159,9 @@
value="[[profileState.localProfileName]]"
on-change="onProfileNameChanged_" on-keydown="onProfileNameKeydown_"
on-blur="onProfileNameInputBlur_" pattern="[[pattern_]]"
- auto-validate spellcheck="false" required>
+ auto-validate spellcheck="false"
+ on-pointerenter="onNameInputPointerEnter_"
+ on-pointerleave="onNameInputPointerLeave_" required>
</cr-input>
<div id="hoverUnderline"></div>
</div>
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/profile_card.ts b/chromium/chrome/browser/resources/signin/profile_picker/profile_card.ts
index 995a3100fed..f035ee1ad29 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/profile_card.ts
+++ b/chromium/chrome/browser/resources/signin/profile_picker/profile_card.ts
@@ -110,6 +110,16 @@ export class ProfileCardElement extends ProfileCardElementBase {
this.profileState.profilePath);
}
+ private onNameInputPointerEnter_() {
+ this.dispatchEvent(new CustomEvent(
+ 'toggle-drag', {composed: true, detail: {toggle: false}}));
+ }
+
+ private onNameInputPointerLeave_() {
+ this.dispatchEvent(new CustomEvent(
+ 'toggle-drag', {composed: true, detail: {toggle: true}}));
+ }
+
/**
* Handler for when the profile name field is changed, then blurred.
*/
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.html b/chromium/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.html
deleted file mode 100644
index a376837bf66..00000000000
--- a/chromium/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.html
+++ /dev/null
@@ -1,260 +0,0 @@
-<style include="profile-creation-shared profile-picker-shared cr-shared-style
- cr-hidden-style">
- :host {
- --vertical-gap: 24px;
- }
-
- #title {
- color: var(--theme-text-color);
- font-weight: normal;
- padding-top: 84px;
- position: relative;
- text-align: center;
- }
-
- #avatarContainer {
- --edit-avatar-border: 3px;
- --edit-avatar-size : 36px;
- }
-
- #headerContainer {
- background-color: var(--theme-frame-color);
- }
-
- .banner {
- background-size: 100% 100%;
- height: 100%;
- position: absolute;
- top: 0;
- width: 100%;
- z-index: 0;
- }
-
- #customizeAvatarEllipse {
- background-color: var(--md-background-color);
- border-radius: 50%;
- bottom: 0;
- height: var(--edit-avatar-size);
- margin: auto;
- position: absolute;
- right: 0;
- width : var(--edit-avatar-size);
- z-index: 2;
- }
-
- #customizeAvatarIcon {
- --cr-icon-button-icon-size: 18px;
- --cr-icon-button-size: calc(var(--edit-avatar-size) -
- 2 * var(--edit-avatar-border));
- background-color: var(--md-background-color);
- border-radius: 50%;
- bottom: var(--edit-avatar-border);
- box-sizing: border-box;
- margin: auto;
- position: absolute;
- right: var(--edit-avatar-border);
- z-index: 3;
- }
-
- #customizeAvatarIcon:not(:focus-visible:focus) {
- box-shadow: 0 0 2px rgba(60, 64, 67, 0.12), 0 0 6px rgba(60, 64, 67, 0.15);
- }
-
- :host-context([dir='rtl']) #customizeAvatarEllipse {
- left: 0;
- right: initial;
- }
-
- :host-context([dir='rtl']) #customizeAvatarIcon {
- left: var(--edit-avatar-border);
- right: initial;
- }
-
- #wrapperContainer {
- display: flex;
- height: calc(max(100vh, var(--view-min-size)) -
- (var(--banner-height) + var(--avatar-size)/2 + var(--cr-button-height) +
- 2 * var(--footer-spacing) + 4px));
- justify-content: center;
- margin-inline-end: 16px;
- margin-inline-start: 16px;
- margin-top: calc(var(--avatar-size)/2);
- overflow: auto;
- }
-
- #wrapper > * {
- flex-grow: 0;
- flex-shrink: 0;
- margin-top: var(--vertical-gap);
- }
-
- #wrapper {
- align-items: center;
- display: flex;
- flex-direction: column;
- max-height: 100%;
- width: 100%;
- }
-
- #nameInput {
- --cr-input-placeholder-color: rgba(var(--google-grey-900-rgb), .5);
- --cr-input-border-bottom: 1px solid var(--cr-secondary-text-color);
- height: 32px;
- width: 320px;
- }
-
- #colorPickerContainer {
- border: 1px solid var(--google-grey-300);
- border-radius: 4px;
- display: flex;
- flex-direction: column;
- padding: 15px 36px 23px;
- /* Positions tooltips relatively to this container.
- Fix for https://crbug.com/1182108 */
- position: relative;
- }
-
- #colorPickerHeader {
- color: var(--cr-primary-text-color);
- padding-bottom: 16px;
- }
-
- #colorPicker {
- --cr-customize-themes-grid-gap: 8px;
- --cr-customize-themes-icon-size: 44px;
- align-self: center;
- }
-
- cr-checkbox {
- --cr-checkbox-label-color: var(--cr-secondary-text-color);
- --cr-checkbox-label-padding-start: 8px;
- height: 20px;
- left: 0;
- margin-inline-end: auto;
- margin-inline-start: auto;
- position: absolute;
- right: 0;
- width: fit-content;
- }
-
- #save {
- display: flex;
- margin-inline-end: var(--footer-spacing);
- margin-inline-start: auto;
- width: 111px;
- }
-
- cr-profile-avatar-selector {
- --avatar-size: 72px;
- --avatar-spacing: 18px;
- --avatar-grid-columns: 5;
- height: fit-content;
- padding-bottom: 15px;
- padding-inline-start: 15px;
- padding-top: 15px;
- width: fit-content;
- }
-
- #selectAvatarWrapper {
- height: 394px;
- overflow-x: hidden;
- overflow-y: auto;
- }
-
- #buttonContainer {
- display: flex;
- justify-content: flex-end;
- }
-
- #doneButton {
- width : 32px;
- }
-
- @media (forced-colors: none) {
- #backButton {
- --cr-icon-button-fill-color: var(--theme-text-color);
- }
- }
-
- @media (prefers-color-scheme: dark) {
- #nameInput {
- --cr-input-placeholder-color: rgba(var(--google-grey-200-rgb), .5);
- }
-
- #customizeAvatarIcon {
- border: 1px solid var(--google-grey-500);
- }
-
- #colorPickerContainer {
- border-color: var(--google-grey-700);
- }
- }
-</style>
-
-<div id="headerContainer"
- style$="--theme-frame-color:[[profileThemeInfo.themeFrameColor]];
- --theme-text-color:[[profileThemeInfo.themeFrameTextColor]];
- --theme-shape-color:[[profileThemeInfo.themeShapeColor]]">
- <iron-icon class="banner" icon="profiles:customize-banner"></iron-icon>
- <cr-icon-button id="backButton" iron-icon="cr:arrow-back"
- on-click="onBackClick_" aria-label$="[[getBackButtonAriaLabel_()]]"
- title$="[[getBackButtonAriaLabel_()]]">
- </cr-icon-button>
- <h1 class="title" id="title">$i18n{localProfileCreationTitle}</h1>
- <div id="avatarContainer">
- <img class="avatar" alt="" src$="[[selectedAvatar_.url]]">
- <div id="customizeAvatarEllipse"></div>
- <cr-icon-button id="customizeAvatarIcon"
- iron-icon="cr:create" on-click="onCustomizeAvatarClick_"
- aria-label="$i18n{localProfileCreationCustomizeAvatarLabel}"
- title="$i18n{localProfileCreationCustomizeAvatarLabel}">
- </cr-icon-button>
- </div>
-</div>
-
-<div id="wrapperContainer" class="custom-scrollbar">
- <div id="wrapper">
- <cr-input id="nameInput" value="{{profileName_}}" pattern="[[pattern_]]"
- placeholder="$i18n{createProfileNamePlaceholder}"
- on-blur="onProfileNameInputBlur_"
- auto-validate spellcheck="false" required>
- </cr-input>
-
- <div id="colorPickerContainer">
- <div id="colorPickerHeader">
- $i18n{localProfileCreationThemeText}
- </div>
- <cr-customize-themes id="colorPicker" selected-theme="{{selectedTheme_}}">
- </cr-customize-themes>
- </div>
- </div>
-</div>
-
-<div class="footer">
- <cr-checkbox checked="{{createShortcut_}}"
- hidden="[[!isProfileShortcutsEnabled_]]">
- $i18n{createDesktopShortcutLabel}
- </cr-checkbox>
-
- <cr-button id="save" class="action-button" on-click="onSaveClick_"
- disabled="[[isSaveDisabled_(profileCreationInProgress, profileName_)]]">
- $i18n{createProfileConfirm}
- </cr-button>
-</div>
-
-<cr-dialog id="selectAvatarDialog">
- <div slot="title">$i18n{selectAnAvatarDialogTitle}</div>
- <div slot="body">
- <div id="selectAvatarWrapper" class="custom-scrollbar">
- <cr-profile-avatar-selector avatars="[[availableIcons_]]"
- selected-avatar="{{selectedAvatar_}}">
- </cr-profile-avatar-selector>
- </div>
- </div>
- <div id="buttonContainer" slot="footer">
- <cr-button id="doneButton" class="action-button"
- on-click="onDoneSelectAvatarClick_">
- $i18n{selectAvatarDoneButtonLabel}
- </cr-button>
- </div>
-</cr-dialog>
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.ts b/chromium/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.ts
deleted file mode 100644
index b9de2b3f5bb..00000000000
--- a/chromium/chrome/browser/resources/signin/profile_picker/profile_creation_flow/local_profile_customization.ts
+++ /dev/null
@@ -1,345 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
-import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
-import 'chrome://resources/cr_elements/cr_input/cr_input.js';
-import 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js';
-import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
-import 'chrome://resources/cr_elements/cr_shared_style.css.js';
-import 'chrome://resources/cr_components/customize_themes/customize_themes.js';
-import './profile_creation_shared.css.js';
-import '../icons.html.js';
-import '../profile_picker_shared.css.js';
-
-import {Theme, ThemeInfo, ThemeType} from 'chrome://resources/cr_components/customize_themes/customize_themes.mojom-webui.js';
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
-import {AvatarIcon} from 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js';
-import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
-import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {AutogeneratedThemeColorInfo, ManageProfilesBrowserProxy, ManageProfilesBrowserProxyImpl, UserThemeChoice} from '../manage_profiles_browser_proxy.js';
-import {navigateToPreviousRoute} from '../navigation_mixin.js';
-import {isProfileCreationAllowed} from '../policy_helper.js';
-
-import {getTemplate} from './local_profile_customization.html.js';
-
-export interface LocalProfileCustomizationElement {
- $: {
- backButton: HTMLElement,
- doneButton: CrButtonElement,
- nameInput: CrInputElement,
- save: CrButtonElement,
- selectAvatarDialog: CrDialogElement,
- wrapperContainer: HTMLElement,
- wrapper: HTMLElement,
- };
-}
-
-const LocalProfileCustomizationElementBase =
- WebUiListenerMixin(I18nMixin(PolymerElement));
-
-export class LocalProfileCustomizationElement extends
- LocalProfileCustomizationElementBase {
- static get is() {
- return 'local-profile-customization';
- }
-
- static get template() {
- return getTemplate();
- }
-
- static get properties() {
- return {
- /**
- * The theme info used to display colored UI elements.
- */
- profileThemeInfo: {
- type: Object,
- observer: 'onProfileThemeInfoChange_',
- notify: true,
- },
-
- /**
- * True if a new profile (local or signed-in) is being created, all
- * buttons that create a new profile are then disabled (to avoid creating
- * two profiles).
- */
- profileCreationInProgress: {
- type: Boolean,
- notify: true,
- },
-
- /**
- * The currently selected theme in the color picker.
- */
- selectedTheme_: {
- type: Object,
- observer: 'onSelectedThemeChange_',
- },
-
- /**
- * True if `selectedTheme_` doesn't need to be updated when
- * `profileThemeInfo` changes.
- */
- disableSelectedThemeUpdates_: {
- type: Boolean,
- value: false,
- },
-
- /**
- * Colored default generic avatar in the format expected by
- * 'cr-profile-avatar-selector'
- */
- genericDefaultAvatar_: {
- type: Object,
- computed:
- 'getGenericDefaultAvatar_(profileThemeInfo.themeGenericAvatar)',
- observer: 'onGenericDefaultAvatarChange_',
- },
-
- /**
- * List of available profile icon Urls and labels.
- */
- availableIcons_: {
- type: Array,
- value() {
- return [];
- },
- },
-
- /**
- * The currently selected profile avatar, if any.
- */
- selectedAvatar_: Object,
-
- /**
- * The current profile name.
- */
- profileName_: {
- type: String,
- value: '',
- },
-
- /**
- * if true, a desktop shortcut will be created for the new profile.
- */
- createShortcut_: {
- type: Boolean,
- value: true,
- },
-
- /**
- * True if the profile shortcuts feature is enabled.
- */
- isProfileShortcutsEnabled_: {
- type: Boolean,
- value: () => loadTimeData.getBoolean('profileShortcutsEnabled'),
- },
-
- pattern_: {
- type: String,
- value: '.*\\S.*',
- },
-
- defaultAvatarIndex_: {
- type: Number,
- value: () => loadTimeData.getInteger('placeholderAvatarIndex'),
- },
- };
- }
-
- profileThemeInfo: AutogeneratedThemeColorInfo;
- profileCreationInProgress: boolean;
- private selectedTheme_: Theme;
- private disableSelectedThemeUpdates_: boolean;
- private genericDefaultAvatar_: AvatarIcon;
- private availableIcons_: AvatarIcon[];
- private selectedAvatar_: AvatarIcon|null;
- private profileName_: string;
- private createShortcut_: boolean;
- private isProfileShortcutsEnabled_: boolean;
- private pattern_: string;
- private defaultAvatarIndex_: number;
- private manageProfilesBrowserProxy_: ManageProfilesBrowserProxy =
- ManageProfilesBrowserProxyImpl.getInstance();
- private resizeObserver_: ResizeObserver|null = null;
-
- override ready() {
- super.ready();
- this.sanityCheck_();
- this.manageProfilesBrowserProxy_.getAvailableIcons().then(
- icons => this.setAvailableIcons_(icons));
-
- this.addEventListener('view-enter-start', this.onViewEnterStart_);
- }
-
- override connectedCallback() {
- super.connectedCallback();
- this.addResizeObserver_();
- }
-
- override disconnectedCallback() {
- super.disconnectedCallback();
- this.resizeObserver_!.disconnect();
- }
-
- private addResizeObserver_() {
- const wrapperContainer = this.$.wrapperContainer;
- this.resizeObserver_ = new ResizeObserver(() => {
- this.shadowRoot!.querySelector('.footer')!.classList.toggle(
- 'division-line',
- wrapperContainer.scrollHeight > wrapperContainer.clientHeight);
- });
- this.resizeObserver_.observe(wrapperContainer);
- }
-
- private onViewEnterStart_() {
- if (this.profileName_.length === 0) {
- this.$.nameInput.invalid = false;
- }
- this.$.nameInput.focusInput();
- this.$.wrapper.scrollTop = 0;
- }
-
- private getBackButtonAriaLabel_(): string {
- return this.i18n(
- 'backButtonAriaLabel', this.i18n('localProfileCreationTitle'));
- }
-
- private sanityCheck_(): boolean {
- if (!isProfileCreationAllowed()) {
- this.onBackClick_();
- return false;
- }
- return true;
- }
-
- /**
- * Handler for profile name blur.
- */
- private onProfileNameInputBlur_() {
- this.$.nameInput.validate();
- }
-
- /**
- * Determining whether 'Save' button is disabled.
- */
- private isSaveDisabled_(): boolean {
- return this.profileCreationInProgress || !this.profileName_ ||
- !this.$.nameInput.validate();
- }
-
- /**
- * Handler for the 'Save' button click event.
- */
- private onSaveClick_() {
- if (!this.sanityCheck_()) {
- return;
- }
-
- if (this.profileCreationInProgress) {
- return;
- }
- this.profileCreationInProgress = true;
- const createShortcut =
- this.isProfileShortcutsEnabled_ && this.createShortcut_;
- this.manageProfilesBrowserProxy_.createProfile(
- this.profileName_, this.profileThemeInfo.color,
- this.selectedAvatar_!.index, createShortcut);
- }
-
- private onBackClick_() {
- navigateToPreviousRoute();
- }
-
- private onCustomizeAvatarClick_() {
- this.$.selectAvatarDialog.showModal();
- }
-
- private onDoneSelectAvatarClick_() {
- this.$.selectAvatarDialog.close();
- }
-
- private getGenericDefaultAvatar_(): AvatarIcon {
- return {
- url: this.profileThemeInfo.themeGenericAvatar,
- label: this.i18n('defaultAvatarLabel'),
- index: this.defaultAvatarIndex_,
- isGaiaAvatar: false,
- selected: false,
- };
- }
-
- private onGenericDefaultAvatarChange_() {
- this.setAvailableIcons_([...this.availableIcons_]);
- if (!this.selectedAvatar_ ||
- this.selectedAvatar_.index === this.defaultAvatarIndex_) {
- this.selectedAvatar_ = this.genericDefaultAvatar_;
- }
- }
-
- private setAvailableIcons_(icons: AvatarIcon[]) {
- if (!this.genericDefaultAvatar_) {
- this.availableIcons_ = icons;
- return;
- }
- const offset =
- icons.length > 0 && icons[0].index === this.defaultAvatarIndex_ ? 1 : 0;
- this.availableIcons_ = [this.genericDefaultAvatar_, ...icons.slice(offset)];
- }
-
- private onProfileThemeInfoChange_() {
- if (this.disableSelectedThemeUpdates_) {
- return;
- }
-
- this.selectedTheme_ = {
- type: ThemeType.kChrome,
- info: {
- chromeThemeId: this.profileThemeInfo.colorId,
- } as ThemeInfo,
- isForced: false,
- };
- }
-
- private async onSelectedThemeChange_() {
- let theme: UserThemeChoice|null = null;
- if (this.selectedTheme_.type === ThemeType.kAutogenerated) {
- theme = {
- colorId: 0,
- color: this.selectedTheme_.info.autogeneratedThemeColors!.frame.value,
- };
- } else if (this.selectedTheme_.type === ThemeType.kChrome) {
- theme = {
- colorId: this.selectedTheme_.info.chromeThemeId!,
- };
- } else if (this.selectedTheme_.type === ThemeType.kDefault) {
- theme = {
- colorId: -1,
- };
- }
-
- const newThemeInfo =
- await this.manageProfilesBrowserProxy_.getProfileThemeInfo(theme!);
- this.disableSelectedThemeUpdates_ = true;
- this.profileThemeInfo = newThemeInfo;
- this.disableSelectedThemeUpdates_ = false;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'local-profile-customization': LocalProfileCustomizationElement;
- }
-}
-
-customElements.define(
- LocalProfileCustomizationElement.is, LocalProfileCustomizationElement);
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.html b/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.html
index 1b414eba7ac..0b8f70dacc2 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.html
+++ b/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.html
@@ -64,15 +64,6 @@
</template>
</cr-lazy-render>
- <cr-lazy-render id="localProfileCustomization">
- <template>
- <local-profile-customization slot="view"
- profile-theme-info="{{newProfileThemeInfo}}"
- profile-creation-in-progress="{{profileCreationInProgress}}">
- </local-profile-customization>
- </template>
- </cr-lazy-render>
-
<cr-lazy-render id="profileSwitch">
<template>
<profile-switch slot="view">
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.ts b/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.ts
index 58d6be2d9f7..986e3f1f2a7 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.ts
+++ b/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_app.ts
@@ -60,16 +60,6 @@ export class ProfilePickerAppElement extends ProfilePickerAppElementBase {
notify: true,
},
- /**
- * Feature to open a profile customization dialog in a browser window for
- * the local profile created.
- */
- isLocalProfileCreationDialogEnabled_: {
- type: Boolean,
- value: () =>
- loadTimeData.getBoolean('isLocalProfileCreationDialogEnabled'),
- },
-
shouldDisplayVerticalBanners_: {
type: Boolean,
value: false,
@@ -79,7 +69,6 @@ export class ProfilePickerAppElement extends ProfilePickerAppElementBase {
newProfileThemeInfo: AutogeneratedThemeColorInfo;
profileCreationInProgress: boolean;
- private isLocalProfileCreationDialogEnabled_: boolean;
private shouldDisplayVerticalBanners_: boolean;
private currentRoute_: Routes|null = null;
private manageProfilesBrowserProxy_: ManageProfilesBrowserProxy =
@@ -129,8 +118,7 @@ export class ProfilePickerAppElement extends ProfilePickerAppElementBase {
step !== ProfileCreationSteps.LOAD_SIGNIN,
'LOAD_SIGNIN should not appear in navigation (only used for metrics)');
- if (step === ProfileCreationSteps.LOCAL_PROFILE_CUSTOMIZATION &&
- this.isLocalProfileCreationDialogEnabled_) {
+ if (step === ProfileCreationSteps.LOCAL_PROFILE_CUSTOMIZATION) {
if (this.profileCreationInProgress) {
return;
}
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html b/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html
index 1f248aa7f26..3334945e7cd 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html
+++ b/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html
@@ -152,8 +152,10 @@
<div id="wrapper">
<div id="profilesContainer" class="custom-scrollbar"
hidden$="[[!profilesListLoaded_]]">
- <template is="dom-repeat" items="[[profilesList_]]">
+ <template id="profiles" is="dom-repeat" items="[[profilesList_]]"
+ index-as="idx">
<profile-card
+ id="profile-[[idx]]"
class="profile-item" profile-state="[[item]]">
</profile-card>
</template>
diff --git a/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.ts b/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.ts
index 0f0d5484537..55a1def4837 100644
--- a/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.ts
+++ b/chromium/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.ts
@@ -13,11 +13,13 @@ import './profile_card.js';
import './profile_picker_shared.css.js';
import './strings.m.js';
+import {listenOnce} from '//resources/js/util_ts.js';
import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {afterNextRender, DomRepeat, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {DragDropReorderTileListDelegate, DraggableTileListInterface} from './drag_drop_reorder_tile_list_delegate.js';
import {ManageProfilesBrowserProxy, ManageProfilesBrowserProxyImpl, ProfileState} from './manage_profiles_browser_proxy.js';
import {navigateTo, NavigationMixin, Routes} from './navigation_mixin.js';
import {isAskOnStartupAllowed, isGuestModeEnabled, isProfileCreationAllowed} from './policy_helper.js';
@@ -31,6 +33,7 @@ export interface ProfilePickerMainViewElement {
browseAsGuestButton: HTMLElement,
profilesContainer: HTMLElement,
wrapper: HTMLElement,
+ profiles: DomRepeat,
};
}
@@ -38,7 +41,7 @@ const ProfilePickerMainViewElementBase =
WebUiListenerMixin(NavigationMixin(PolymerElement));
export class ProfilePickerMainViewElement extends
- ProfilePickerMainViewElementBase {
+ ProfilePickerMainViewElementBase implements DraggableTileListInterface {
static get is() {
return 'profile-picker-main-view';
}
@@ -86,6 +89,9 @@ export class ProfilePickerMainViewElement extends
private resizeObserver_: ResizeObserver|null = null;
private previousRoute_: Routes|null = null;
+ private dragDelegate_: DragDropReorderTileListDelegate|null = null;
+ private dragDuration_: number = 300;
+
override ready() {
super.ready();
if (!isGuestModeEnabled()) {
@@ -97,6 +103,8 @@ export class ProfilePickerMainViewElement extends
}
this.addEventListener('view-enter-finish', this.onViewEnterFinish_);
+
+ this.addEventListener('toggle-drag', this.toggleDrag_);
}
override connectedCallback() {
@@ -112,6 +120,10 @@ export class ProfilePickerMainViewElement extends
override disconnectedCallback() {
super.disconnectedCallback();
this.resizeObserver_!.disconnect();
+
+ if (this.dragDelegate_) {
+ this.dragDelegate_.clearListeners();
+ }
}
override onRouteChange(route: Routes) {
@@ -156,6 +168,22 @@ export class ProfilePickerMainViewElement extends
private handleProfilesListChanged_(profilesList: ProfileState[]) {
this.profilesListLoaded_ = true;
this.profilesList_ = profilesList;
+
+ if (loadTimeData.getBoolean('profilesReorderingEnabled')) {
+ if (this.dragDelegate_) {
+ this.dragDelegate_.clearListeners();
+ }
+
+ this.dragDelegate_ = new DragDropReorderTileListDelegate(
+ this, this, 'profilesList_', this.profilesList_.length,
+ this.dragDuration_);
+
+ listenOnce(this, 'dom-change', () => {
+ afterNextRender(this, () => {
+ this.dragDelegate_!.initializeListeners();
+ });
+ });
+ }
}
/**
@@ -198,6 +226,39 @@ export class ProfilePickerMainViewElement extends
return !isAskOnStartupAllowed() || !this.profilesList_ ||
this.profilesList_.length < 2;
}
+
+ private toggleDrag_(e: Event) {
+ if (!this.dragDelegate_) {
+ return;
+ }
+
+ const customEvent = e as CustomEvent;
+ this.dragDelegate_.toggleDrag(customEvent.detail.toggle);
+ }
+
+ // @override
+ onDradEnd(initialIndex: number, finalIndex: number): void {
+ this.manageProfilesBrowserProxy_.updateProfileOrder(
+ initialIndex, finalIndex);
+ }
+
+ // @override
+ getDraggableTile(index: number): HTMLElement {
+ return this.shadowRoot!.querySelector<HTMLElement>('#profile-' + index)!;
+ }
+
+ // @override
+ getDraggableTileIndex(tile: HTMLElement): number {
+ return this.$.profiles.indexForElement(tile) as number;
+ }
+
+ setDraggingTransitionDurationForTesting(duration: number) {
+ this.dragDuration_ = duration;
+ }
+
+ getProfileListForTesting(): ProfileState[] {
+ return this.profilesList_;
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/signin/sync_confirmation/OWNERS b/chromium/chrome/browser/resources/signin/sync_confirmation/OWNERS
index f599b2b5f01..a1d70042fa6 100644
--- a/chromium/chrome/browser/resources/signin/sync_confirmation/OWNERS
+++ b/chromium/chrome/browser/resources/signin/sync_confirmation/OWNERS
@@ -1,2 +1,3 @@
file://components/signin/OWNERS
jyammine@google.com
+gabolvr@google.com
diff --git a/chromium/chrome/browser/resources/suggest_internals/app.ts b/chromium/chrome/browser/resources/suggest_internals/app.ts
index bf2de990352..460ebc72813 100644
--- a/chromium/chrome/browser/resources/suggest_internals/app.ts
+++ b/chromium/chrome/browser/resources/suggest_internals/app.ts
@@ -64,8 +64,10 @@ class SuggestInternalsAppElement extends PolymerElement {
private callbackRouter_: PageCallbackRouter;
private pageHandler_: PageHandlerInterface;
private suggestionsRequestCompletedListenerId_: number|null = null;
+ private suggestionsRequestCreatedListenerId_: number|null = null;
private suggestionsRequestStartedListenerId_: number|null = null;
+
constructor() {
super();
this.pageHandler_ = PageHandler.getRemote();
@@ -76,9 +78,12 @@ class SuggestInternalsAppElement extends PolymerElement {
override connectedCallback() {
super.connectedCallback();
+ this.suggestionsRequestCreatedListenerId_ =
+ this.callbackRouter_.onSuggestRequestCreated.addListener(
+ this.onSuggestRequestCreated_.bind(this));
this.suggestionsRequestStartedListenerId_ =
- this.callbackRouter_.onSuggestRequestStarting.addListener(
- this.onSuggestRequestStarting_.bind(this));
+ this.callbackRouter_.onSuggestRequestStarted.addListener(
+ this.onSuggestRequestStarted_.bind(this));
this.suggestionsRequestCompletedListenerId_ =
this.callbackRouter_.onSuggestRequestCompleted.addListener(
this.onSuggestRequestCompleted_.bind(this));
@@ -86,6 +91,9 @@ class SuggestInternalsAppElement extends PolymerElement {
override disconnectedCallback() {
super.disconnectedCallback();
+ assert(this.suggestionsRequestCreatedListenerId_);
+ this.callbackRouter_.removeListener(
+ this.suggestionsRequestCreatedListenerId_);
assert(this.suggestionsRequestStartedListenerId_);
this.callbackRouter_.removeListener(
this.suggestionsRequestStartedListenerId_);
@@ -181,11 +189,26 @@ class SuggestInternalsAppElement extends PolymerElement {
this.$.toast.show();
}
- private onSuggestRequestStarting_(request: Request) {
+ private onSuggestRequestCreated_(request: Request) {
// Add the request to the start of the list of known requests.
this.unshift('requests_', request);
}
+ private onSuggestRequestStarted_(request: Request) {
+ const index = this.requests_.findIndex((element: Request) => {
+ return request.id.high === element.id.high &&
+ request.id.low === element.id.low;
+ });
+ // If the request is known, update it with the additional information.
+ if (index !== -1) {
+ this.set(`requests_.${index}.status`, request.status);
+ this.set(
+ `requests_.${index}.data`,
+ Object.assign({}, this.requests_[index].data, request.data));
+ this.set(`requests_.${index}.startTime`, request.startTime);
+ }
+ }
+
private onSuggestRequestCompleted_(request: Request) {
const index = this.requests_.findIndex((element: Request) => {
return request.id.high === element.id.high &&
@@ -194,6 +217,9 @@ class SuggestInternalsAppElement extends PolymerElement {
// If the request is known, update it with the additional information.
if (index !== -1) {
this.set(`requests_.${index}.status`, request.status);
+ this.set(
+ `requests_.${index}.data`,
+ Object.assign({}, this.requests_[index].data, request.data));
this.set(`requests_.${index}.endTime`, request.endTime);
this.set(`requests_.${index}.response`, request.response);
}
diff --git a/chromium/chrome/browser/resources/suggest_internals/request.html b/chromium/chrome/browser/resources/suggest_internals/request.html
index 71f80182a39..9093cdfc044 100644
--- a/chromium/chrome/browser/resources/suggest_internals/request.html
+++ b/chromium/chrome/browser/resources/suggest_internals/request.html
@@ -48,8 +48,10 @@
<span class="label">[[getRequestPath_(request)]]</span>
</cr-expand-button>
<iron-collapse opened="[[request.expanded]]">
- <div class="cr-row" hidden$="[[!getRequestData_(request)]]">
- <div class="content">[[getRequestData_(request)]]</div>
+ <div class="cr-row" hidden$="[[!requestDataJson_]]">
+ <div class="content"
+ inner-h-t-m-l="[[getRequestDataHtml_(requestDataJson_)]]">
+ </div>
<div class="actions">
<cr-icon-button class="icon-copy-content" title="copy to clipboard"
on-click="onCopyRequestClick_">
@@ -60,8 +62,7 @@
</div>
</div>
<div class="cr-row" hidden$="[[!responseJson_]]">
- <div class="content"
- inner-h-t-m-l="[[getResponseHtml_(responseJson_)]]">
+ <div class="content" inner-h-t-m-l="[[getResponseHtml_(responseJson_)]]">
</div>
<div class="actions">
<cr-icon-button class="icon-copy-content" title="copy to clipboard"
diff --git a/chromium/chrome/browser/resources/suggest_internals/request.ts b/chromium/chrome/browser/resources/suggest_internals/request.ts
index e5accb8f404..c96e698ec37 100644
--- a/chromium/chrome/browser/resources/suggest_internals/request.ts
+++ b/chromium/chrome/browser/resources/suggest_internals/request.ts
@@ -30,6 +30,11 @@ export class SuggestRequestElement extends PolymerElement {
return {
request: Object,
+ requestDataJson_: {
+ type: String,
+ computed: `computeRquestDataJson_(request.data)`,
+ },
+
responseJson_: {
type: String,
computed: `computeResponseJson_(request.response)`,
@@ -38,13 +43,25 @@ export class SuggestRequestElement extends PolymerElement {
}
request: Request;
+ private requestDataJson_: string = '';
private responseJson_: string = '';
+ private computeRquestDataJson_(): string {
+ try {
+ // Try to parse the request body, if any.
+ this.request.data['Request-Body'] =
+ JSON.parse(this.request.data['Request-Body']);
+ } finally {
+ // Pretty-print the parsed JSON.
+ return JSON.stringify(this.request.data, null, 2);
+ }
+ }
+
private computeResponseJson_(): string {
try {
// Remove the magic XSSI guard prefix, if any, to get a valid JSON.
const validJson = this.request.response.replace(')]}\'', '').trim();
- // Try to parse and pretty-print the valid JSON.
+ // Try to parse the valid JSON.
const parsedJson = JSON.parse(validJson);
// Pretty-print the parsed JSON.
return JSON.stringify(parsedJson, null, 2);
@@ -53,9 +70,8 @@ export class SuggestRequestElement extends PolymerElement {
}
}
- private getRequestData_(): string {
- const requestData = JSON.stringify(this.request.data, null, 2);
- return requestData === '{}' ? '' : requestData;
+ private getRequestDataHtml_(): TrustedHTML {
+ return sanitizeInnerHtml(this.requestDataJson_);
}
private getRequestPath_(): string {
@@ -76,6 +92,8 @@ export class SuggestRequestElement extends PolymerElement {
switch (this.request.status) {
case RequestStatus.kHardcoded:
return 'suggest:lock';
+ case RequestStatus.kCreated:
+ return 'cr:create';
case RequestStatus.kSent:
return 'cr:schedule';
case RequestStatus.kSucceeded:
@@ -91,6 +109,8 @@ export class SuggestRequestElement extends PolymerElement {
switch (this.request.status) {
case RequestStatus.kHardcoded:
return 'hardcoded';
+ case RequestStatus.kCreated:
+ return 'created';
case RequestStatus.kSent:
return 'pending';
case RequestStatus.kSucceeded:
@@ -120,7 +140,7 @@ export class SuggestRequestElement extends PolymerElement {
}
private onCopyRequestClick_() {
- navigator.clipboard.writeText(this.getRequestData_());
+ navigator.clipboard.writeText(this.requestDataJson_);
this.dispatchEvent(new CustomEvent('show-toast', {
bubbles: true,
diff --git a/chromium/chrome/browser/resources/support_tool/data_collectors.html b/chromium/chrome/browser/resources/support_tool/data_collectors.html
index 2b779323aab..0ea552fad73 100644
--- a/chromium/chrome/browser/resources/support_tool/data_collectors.html
+++ b/chromium/chrome/browser/resources/support_tool/data_collectors.html
@@ -12,7 +12,7 @@ found in the LICENSE file.
}
</style>
-<h1 tabindex="0">Step 2 of 4: Select diagnostics data to export</h1>
+<h1 tabindex="0">$i18n{dataSelectionPageTitle}</h1>
<iron-list class="data-collector-list" items="[[dataCollectors_]]">
<template>
<cr-checkbox class="data-collector-checkbox" checked="{{item.isIncluded}}"
diff --git a/chromium/chrome/browser/resources/support_tool/data_export_done.html b/chromium/chrome/browser/resources/support_tool/data_export_done.html
index 67f21cf80ed..b230d431d13 100644
--- a/chromium/chrome/browser/resources/support_tool/data_export_done.html
+++ b/chromium/chrome/browser/resources/support_tool/data_export_done.html
@@ -17,9 +17,9 @@ found in the LICENSE file.
</style>
-<h1 tabindex="0">Step 4 of 4: Diagnostics data is exported</h1>
+<h1 tabindex="0">$i18n{dataExportDonePageTitle}</h1>
<div class="support-tool-title" tabindex="0">
- Data has been exported into the selected location
+ $i18n{dataExportedText}
</div>
<div>
<iron-icon id="check-icon" icon="cr:check-circle"></iron-icon>
diff --git a/chromium/chrome/browser/resources/support_tool/issue_details.html b/chromium/chrome/browser/resources/support_tool/issue_details.html
index 076f3ab43b8..3f99d1b8ac9 100644
--- a/chromium/chrome/browser/resources/support_tool/issue_details.html
+++ b/chromium/chrome/browser/resources/support_tool/issue_details.html
@@ -31,12 +31,14 @@ found in the LICENSE file.
}
</style>
-<h1 tabindex="0">Step 1 of 4: Describe the issue</h1>
-<div class="support-tool-title">Support Case ID</div>
+<h1 tabindex="0">$i18n{issueDetailsPageTitle}</h1>
+<div class="support-tool-title">$i18n{supportCaseId}</div>
<cr-input class="support-case-id" value="{{caseId_}}"
- spellcheck="false" maxlength="20" aria-label="Support Case ID">
+ spellcheck="false" maxlength="20" aria-label="$i18n{supportCaseId}">
</cr-input>
-<div id="email-title" class="support-tool-title" aria-hidden="true">Email</div>
+<div id="email-title" class="support-tool-title" aria-hidden="true">
+ $i18n{email}
+</div>
<select class="md-select" value="{{selectedEmail_::change}}"
aria-labelledby="email-title">
<template is="dom-repeat" items="[[emails_]]">
@@ -44,9 +46,9 @@ found in the LICENSE file.
</template>
</select>
<div id="description-title" class="support-tool-title" aria-hidden="true">
- Describe the issue
+ $i18n{describeIssueText}
</div>
<textarea id="description" class="support-tool-text" spellcheck="true"
value="{{issueDescription_::input}}" aria-labelledby="description-title"
- placeholder="Provide a clear desciption of the issue and steps to reproduce the issue (if possible)">
+ placeholder="$i18n{issueDescriptionPlaceholder}">
</textarea> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/support_tool/issue_details.ts b/chromium/chrome/browser/resources/support_tool/issue_details.ts
index 2d307ba2bf3..919d085eed4 100644
--- a/chromium/chrome/browser/resources/support_tool/issue_details.ts
+++ b/chromium/chrome/browser/resources/support_tool/issue_details.ts
@@ -15,8 +15,6 @@ import {BrowserProxy, BrowserProxyImpl, IssueDetails} from './browser_proxy.js';
import {getTemplate} from './issue_details.html.js';
import {SupportToolPageMixin} from './support_tool_page_mixin.js';
-const DONT_INCLUDE_EMAIL: string = 'Do not include email address';
-
const IssueDetailsElementBase = SupportToolPageMixin(PolymerElement);
export class IssueDetailsElement extends IssueDetailsElementBase {
@@ -36,7 +34,7 @@ export class IssueDetailsElement extends IssueDetailsElementBase {
},
emails_: {
type: Array,
- value: () => [DONT_INCLUDE_EMAIL],
+ value: () => [],
},
issueDescription_: {
type: String,
@@ -50,11 +48,12 @@ export class IssueDetailsElement extends IssueDetailsElementBase {
}
private caseId_: string;
- private emails_: string[];
+ private emails_: string[] = [this.i18n('dontIncludeEmailAddress')];
private issueDescription_: string;
private selectedEmail_: string;
private browserProxy_: BrowserProxy = BrowserProxyImpl.getInstance();
+
override connectedCallback() {
super.connectedCallback();
@@ -62,7 +61,7 @@ export class IssueDetailsElement extends IssueDetailsElementBase {
this.emails_ = emails;
// Add default email at the end of emails list for user to be able to
// choose to not include email address.
- this.emails_.push(DONT_INCLUDE_EMAIL);
+ this.emails_.push(this.i18n('dontIncludeEmailAddress'));
});
}
@@ -71,7 +70,8 @@ export class IssueDetailsElement extends IssueDetailsElementBase {
caseId: this.caseId_,
// Set emailAddress field to empty string if user selected to not include
// email address.
- emailAddress: (this.selectedEmail_ === DONT_INCLUDE_EMAIL) ?
+ emailAddress:
+ (this.selectedEmail_ === this.i18n('dontIncludeEmailAddress')) ?
'' :
this.selectedEmail_,
issueDescription: this.issueDescription_,
diff --git a/chromium/chrome/browser/resources/support_tool/pii_selection.html b/chromium/chrome/browser/resources/support_tool/pii_selection.html
index 99206f1308e..d1e76313357 100644
--- a/chromium/chrome/browser/resources/support_tool/pii_selection.html
+++ b/chromium/chrome/browser/resources/support_tool/pii_selection.html
@@ -40,14 +40,9 @@ found in the LICENSE file.
}
cr-expand-button {
- background-color: var(--cr-card-background-color);
height: var(--cr-expand-button-icon-size, 20px);
}
- cr-checkbox {
- background-color: var(--cr-card-background-color);
- }
-
.pii-item-collapse {
color: var(--cr-title-text-color);
margin-bottom: 4px;
@@ -68,30 +63,28 @@ found in the LICENSE file.
</style>
<h1 tabindex="0">
- Step 3 of 4: Review personally identifiable information
+ $i18n{reviewPiiPageTitle}
</h1>
<div id="pii-warning-text" class="support-tool-title" tabindex="0">
- Diagnostic data collection is complete. Some of your personal information is
- included in this data.
+ $i18n{piiWarningText}
</div>
<div id="radio-group">
<cr-radio-group selected="[[selectedRadioButton_]]"
on-selected-changed="onSelectedRadioButtonChanged_">
<cr-radio-button name="[[piiRadioButtonsEnum_.INCLUDE_ALL]]" tabindex="0">
- Include all personal information
+ $i18n{includeAllPiiRadioButton}
</cr-radio-button>
<cr-radio-button name="[[piiRadioButtonsEnum_.INCLUDE_NONE]]" tabindex="0">
- Automatically remove most personal information
+ $i18n{removePiiRadioButton}
</cr-radio-button>
<iron-collapse id="privacy-disclaimer"
opened$="[[showDisclaimer_(selectedRadioButton_)]]">
<div tabindex="0">
- Some personal information may still be included in the data. Make sure
- to review the exported files.
+ $i18n{piiRemovalDisclaimer}
</div>
</iron-collapse>
<cr-radio-button name="[[piiRadioButtonsEnum_.INCLUDE_SOME]]" tabindex="0">
- Manually select personal information you want to include
+ $i18n{manuallySelectPiiRadioButton}
</cr-radio-button>
</cr-radio-group>
</div>
@@ -116,9 +109,11 @@ found in the LICENSE file.
</div>
</iron-collapse>
<div class="navigation-buttons">
- <cr-button id="cancelButton" on-click="onCancelClick_">Cancel</cr-button>
+ <cr-button id="cancelButton" on-click="onCancelClick_">
+ $i18n{cancelButtonText}
+ </cr-button>
<cr-button id="exportButton" class="action-button"
on-click="onExportClick_">
- Export
+ $i18n{exportButtonText}
</cr-button>
</div>
diff --git a/chromium/chrome/browser/resources/support_tool/spinner_page.html b/chromium/chrome/browser/resources/support_tool/spinner_page.html
index 7918fb87a57..679e39180e3 100644
--- a/chromium/chrome/browser/resources/support_tool/spinner_page.html
+++ b/chromium/chrome/browser/resources/support_tool/spinner_page.html
@@ -16,5 +16,7 @@ found in the LICENSE file.
<paper-spinner-lite active>
</paper-spinner-lite>
<div class="navigation-buttons">
- <cr-button id="cancelButton" on-click="onCancelClick_">Cancel</cr-button>
+ <cr-button id="cancelButton" on-click="onCancelClick_">
+ $i18n{cancelButtonText}
+ </cr-button>
</div>
diff --git a/chromium/chrome/browser/resources/support_tool/support_tool.html b/chromium/chrome/browser/resources/support_tool/support_tool.html
index b283298c466..2d2c95b17f5 100644
--- a/chromium/chrome/browser/resources/support_tool/support_tool.html
+++ b/chromium/chrome/browser/resources/support_tool/support_tool.html
@@ -26,13 +26,13 @@ found in the LICENSE file.
<data-collectors id="dataCollectors"
page-index="[[supportToolPageIndex_.DATA_COLLECTOR_SELECTION]]">
</data-collectors>
- <spinner-page id="spinnerPage" page-title="Collecting diagnostic data"
+ <spinner-page id="spinnerPage" page-title="$i18n{dataCollectionSpinner}"
page-index="[[supportToolPageIndex_.SPINNER]]">
</spinner-page>
<pii-selection id="piiSelection"
page-index="[[supportToolPageIndex_.PII_SELECTION]]">
</pii-selection>
- <spinner-page id="exportSpinner" page-title="Exporting diagnostic data"
+ <spinner-page id="exportSpinner" page-title="$i18n{dataExportSpinner}"
page-index="[[supportToolPageIndex_.EXPORT_SPINNER]]">
</spinner-page>
<data-export-done id="dataExportDone"
@@ -44,17 +44,17 @@ found in the LICENSE file.
hidden$="[[shouldHideContinueButtonContainer_(selectedPage_)]]">
<cr-button id="backButton" hidden$="[[shouldHideBackButton_(selectedPage_)]]"
on-click="onBackClick_">
- Back
+ $i18n{backButtonText}
</cr-button>
<cr-button id="continueButton" class="action-button"
on-click="onContinueClick_">
- Continue
+ $i18n{continueButtonText}
</cr-button>
</div>
<cr-toast id="errorMessageToast" duration="0" tabindex="0"
aria-labelledby="error-message">
<span id="error-message">[[errorMessage_]]</span>
<cr-button on-click="onErrorMessageToastCloseClicked_">
- Dismiss
+ $i18n{dismissButtonText}
</cr-button>
</cr-toast>
diff --git a/chromium/chrome/browser/resources/support_tool/support_tool_container.html b/chromium/chrome/browser/resources/support_tool/support_tool_container.html
index e42a69f611b..4e627897e36 100644
--- a/chromium/chrome/browser/resources/support_tool/support_tool_container.html
+++ b/chromium/chrome/browser/resources/support_tool/support_tool_container.html
@@ -8,7 +8,7 @@ found in the LICENSE file.
<html>
<head>
<meta charset="utf-8">
- <title>Support Tool</title>
+ <title>$i18n{supportToolTabTitle}</title>
<link rel="import" href="chrome://resources/html/dark_mode.html">
<link rel="stylesheet" href="chrome://resources/css/md_colors.css">
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
diff --git a/chromium/chrome/browser/resources/support_tool/support_tool_page_mixin.ts b/chromium/chrome/browser/resources/support_tool/support_tool_page_mixin.ts
index 2bba0c70135..a1b987e7063 100644
--- a/chromium/chrome/browser/resources/support_tool/support_tool_page_mixin.ts
+++ b/chromium/chrome/browser/resources/support_tool/support_tool_page_mixin.ts
@@ -7,6 +7,7 @@
* pages.
*/
+import {I18nMixin, I18nMixinInterface} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
type Constructor<T> = new (...args: any[]) => T;
@@ -14,7 +15,9 @@ type Constructor<T> = new (...args: any[]) => T;
export const SupportToolPageMixin = dedupingMixin(
<T extends Constructor<PolymerElement>>(superClass: T): T&
Constructor<SupportToolPageMixinInterface> => {
- class SupportToolPageMixin extends superClass implements
+ const superClassBase = I18nMixin(superClass);
+
+ class SupportToolPageMixin extends superClassBase implements
SupportToolPageMixinInterface {
$$<E extends Element = Element>(query: string) {
return this.shadowRoot!.querySelector<E>(query);
@@ -30,8 +33,8 @@ export const SupportToolPageMixin = dedupingMixin(
return SupportToolPageMixin;
});
-export interface SupportToolPageMixinInterface {
+export interface SupportToolPageMixinInterface extends I18nMixinInterface {
$$<E extends Element = Element>(query: string): E|null;
ensureFocusOnPageHeader(): void;
-} \ No newline at end of file
+}
diff --git a/chromium/chrome/browser/resources/support_tool/url_generator.html b/chromium/chrome/browser/resources/support_tool/url_generator.html
index d6337ec80bd..4f6aa6103b3 100644
--- a/chromium/chrome/browser/resources/support_tool/url_generator.html
+++ b/chromium/chrome/browser/resources/support_tool/url_generator.html
@@ -30,13 +30,13 @@ found in the LICENSE file.
}
</style>
-<h1 tabindex="0">Get a Support Tool link</h1>
-<div class="support-tool-title">Support Case ID</div>
+<h1 tabindex="0">$i18n{urlGeneratorPageTitle}</h1>
+<div class="support-tool-title">$i18n{supportCaseId}</div>
<cr-input id="caseIdInput" class="support-case-id" value="{{caseId_}}"
- spellcheck="false" maxlength="20" aria-label="Support Case ID">
+ spellcheck="false" maxlength="20" aria-label="$i18n{supportCaseId}">
</cr-input>
<div id="data-sources-title" class="support-tool-title" tabindex="0">
- Data sources to collect
+ $i18n{dataCollectorListTitle}
</div>
<div class="data-collector-list" aria-labelledby="data-sources-title">
<template is="dom-repeat" items="[[dataCollectors_]]">
@@ -47,19 +47,19 @@ found in the LICENSE file.
</template>
</div>
-<div class="support-tool-title" tabindex="0">Get a link</div>
+<div class="support-tool-title" tabindex="0">$i18n{getLinkText}</div>
<div>
<p id="info-text">
- Copy and send this link to the user to collect the logs.
+ $i18n{copyLinkDescription}
</p>
<cr-button id="copyURLButton" class="navigation-buttons action-button"
on-click="onCopyUrlClick_" disabled="[[buttonDisabled_]]">
- Copy link
+ $i18n{copyLinkButtonText}
</cr-button>
<cr-button id="copyTokenButton" class="navigation-buttons action-button"
on-click="onCopyTokenClick_" disabled="[[buttonDisabled_]]"
hidden="[[hideTokenButton_]]">
- Copy token
+ $i18n{copyTokenButtonText}
</cr-button>
</div>
<cr-toast id="copyToast" duration="5000" tabindex="0"
@@ -70,6 +70,6 @@ found in the LICENSE file.
aria-labelledby="error-message">
<span id="error-message">[[errorMessage_]]</span>
<cr-button on-click="onErrorMessageToastCloseClicked_">
- Dismiss
+ $i18n{dismissButtonText}
</cr-button>
</cr-toast>
diff --git a/chromium/chrome/browser/resources/support_tool/url_generator.ts b/chromium/chrome/browser/resources/support_tool/url_generator.ts
index 4c3d8263a91..fb0f6103920 100644
--- a/chromium/chrome/browser/resources/support_tool/url_generator.ts
+++ b/chromium/chrome/browser/resources/support_tool/url_generator.ts
@@ -12,15 +12,13 @@ import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {BrowserProxy, BrowserProxyImpl, DataCollectorItem, SupportTokenGenerationResult} from './browser_proxy.js';
import {getTemplate} from './url_generator.html.js';
-const LINK_COPIED_TOAST: string = 'Link copied';
-const TOKEN_COPIED_TOAST: string = 'Token copied';
-
export interface UrlGeneratorElement {
$: {
copyToast: CrToastElement,
@@ -28,7 +26,9 @@ export interface UrlGeneratorElement {
};
}
-export class UrlGeneratorElement extends PolymerElement {
+const UrlGeneratorElementBase = I18nMixin(PolymerElement);
+
+export class UrlGeneratorElement extends UrlGeneratorElementBase {
static get is() {
return 'url-generator';
}
@@ -111,7 +111,7 @@ export class UrlGeneratorElement extends PolymerElement {
result: SupportTokenGenerationResult, toastMessage: string) {
if (result.success) {
this.generatedResult_ = result.token;
- navigator.clipboard.writeText(this.generatedResult_.toString());
+ navigator.clipboard.writeText(this.generatedResult_);
this.copiedToastMessage_ = toastMessage;
this.$.copyToast.show();
this.$.copyToast.focus();
@@ -121,11 +121,11 @@ export class UrlGeneratorElement extends PolymerElement {
}
private onUrlGenerationResult_(result: SupportTokenGenerationResult) {
- this.showGenerationResult(result, LINK_COPIED_TOAST);
+ this.showGenerationResult(result, this.i18n('linkCopied'));
}
private onTokenGenerationResult_(result: SupportTokenGenerationResult) {
- this.showGenerationResult(result, TOKEN_COPIED_TOAST);
+ this.showGenerationResult(result, this.i18n('tokenCopied'));
}
private onCopyUrlClick_() {
diff --git a/chromium/chrome/browser/resources/tab_search/BUILD.gn b/chromium/chrome/browser/resources/tab_search/BUILD.gn
index 41b53e68f9f..539b909ee31 100644
--- a/chromium/chrome/browser/resources/tab_search/BUILD.gn
+++ b/chromium/chrome/browser/resources/tab_search/BUILD.gn
@@ -26,7 +26,6 @@ build_webui("build") {
"infinite_list.ts",
"tab_search_group_item.ts",
"tab_search_item.ts",
- "tab_search_search_field.ts",
]
non_web_component_files = [
diff --git a/chromium/chrome/browser/resources/tab_search/app.html b/chromium/chrome/browser/resources/tab_search/app.html
index ad87ffa6408..5e8aa90ecd8 100644
--- a/chromium/chrome/browser/resources/tab_search/app.html
+++ b/chromium/chrome/browser/resources/tab_search/app.html
@@ -16,6 +16,72 @@
--mwb-scrollbar-track-color: transparent;
}
+ #searchField {
+ align-items: center;
+ background-color: var(--mwb-background-color);
+ display: flex;
+ height: 60px;
+ padding: 0 var(--mwb-list-item-horizontal-margin);
+ user-select: none;
+ }
+
+ #searchIcon {
+ color: var(--cr-secondary-text-color);
+ height: var(--mwb-icon-size);
+ padding-inline-end: 8px;
+ width: var(--mwb-icon-size);
+ }
+
+ #searchWrapper {
+ display: flex;
+ flex: 1;
+ height: 100%;
+ position: relative;
+ }
+
+ :host([has-search-text]) #searchField label {
+ visibility: hidden;
+ }
+
+ #searchLabel {
+ align-items: center;
+ color: var(--cr-secondary-text-color);
+ cursor: text;
+ display: flex;
+ font-size: var(--mwb-primary-text-font-size);
+ font-weight: var(--mwb-secondary-text-font-weight);
+ height: 100%;
+ justify-content: space-between;
+ position: absolute;
+ width: 100%;
+ }
+
+ #searchResultText {
+ clip: rect(0,0,0,0);
+ display: inline-block;
+ position: fixed;
+ }
+
+ #searchInput {
+ background-color: transparent;
+ border: none;
+ border-radius: 0;
+ color: var(--cr-primary-text-color);
+ flex: 1;
+ font-family: inherit;
+ font-size: var(--mwb-primary-text-font-size);
+ font-style: inherit;
+ font-weight: var(--mwb-secondary-text-font-weight);
+ outline: none;
+ padding: 0;
+ text-overflow: ellipsis;
+ }
+
+
+ #searchInput::-webkit-search-cancel-button {
+ display: none;
+ }
+
#no-results {
color: var(--cr-primary-text-color);
font-size: var(--mwb-primary-text-font-size);
@@ -62,8 +128,8 @@
font-size: var(--mwb-list-section-title-font-size);
font-weight: bolder;
height: var(--mwb-list-section-title-height);
- padding-inline-end: 24px;
- padding-inline-start: 16px;
+ padding-inline-end: 28px;
+ padding-inline-start: var(--mwb-list-item-horizontal-margin);
position: sticky;
text-transform: uppercase;
top: 0;
@@ -91,17 +157,30 @@
height: 1px;
background-color: var(--cr-separator-color);
}
+
</style>
-<tab-search-search-field id="searchField" autofocus
- clear-label="$i18n{clearSearch}" label="$i18n{searchTabs}"
- on-keydown="onSearchKeyDown_"
- on-search-changed="onSearchChanged_"
- search-result-text="[[searchResultText_]]">
-</tab-search-search-field>
+<div id="searchField" on-keydown="onSearchKeyDown_"
+ clear-label="$i18n{clearSearch}" label="$i18n{searchTabs}">
+ <iron-icon id="searchIcon" icon="mwb16:search"></iron-icon>
+ <div id="searchWrapper">
+ <label id="searchLabel" for="searchInput" aria-hidden="true">
+ <span>[[label]]</span>
+ <span>[[shortcut_]]</span>
+ <span id="searchResultText">[[searchResultText_]]</span>
+ </label>
+ <input id="searchInput" aria-labelledby="searchLabel"
+ autofocus autocomplete="off"
+ on-search="onSearchTermSearch" on-input="onSearchTermInput"
+ type="search" spellcheck="false" role="combobox"
+ aria-activedescendant$="[[activeSelectionId_]]"
+ aria-controls="tabsList" aria-owns="tabsList">
+ </div>
+</div>
<div id="divider"></div>
<div hidden="[[!filteredItems_.length]]">
<infinite-list id="tabsList" max-height="[[listMaxHeight_(availableHeight_)]]"
- items="[[filteredItems_]]">
+ items="[[filteredItems_]]" on-selected-item-changed="onSelectedItemChanged_"
+ role="listbox">
<template data-type="TitleItem">
<div class="list-section-title">
<div>[[item.title]]</div>
@@ -125,8 +204,8 @@
</tab-search-item>
</template>
<template data-type="TabGroupData" data-selectable>
- <tab-search-group-item class="mwb-list-item" index="[[index]]"
- data="[[item]]" aria-label="[[ariaLabel_(item)]]"
+ <tab-search-group-item id="[[item.tabGroup.id]]" class="mwb-list-item"
+ index="[[index]]" data="[[item]]" aria-label="[[ariaLabel_(item)]]"
on-click="onItemClick_" on-focus="onItemFocus_"
on-keydown="onItemKeyDown_" role="option" tabindex="0">
</tab-search-group-item>
diff --git a/chromium/chrome/browser/resources/tab_search/app.ts b/chromium/chrome/browser/resources/tab_search/app.ts
index d5291f03b39..a89418367c8 100644
--- a/chromium/chrome/browser/resources/tab_search/app.ts
+++ b/chromium/chrome/browser/resources/tab_search/app.ts
@@ -12,11 +12,11 @@ import 'chrome://resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
import './infinite_list.js';
import './tab_search_group_item.js';
import './tab_search_item.js';
-import './tab_search_search_field.js';
import './title_item.js';
import './strings.m.js';
import {ColorChangeUpdater} from '//resources/cr_components/color_change_listener/colors_css_updater.js';
+import {CrSearchFieldMixin} from 'chrome://resources/cr_elements/cr_search_field/cr_search_field_mixin.js';
import {assert} from 'chrome://resources/js/assert_ts.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {MetricsReporter, MetricsReporterImpl} from 'chrome://resources/js/metrics_reporter/metrics_reporter.js';
@@ -31,7 +31,6 @@ import {InfiniteList, NO_SELECTION, selectorNavigationKeys} from './infinite_lis
import {ariaLabel, ItemData, TabData, TabGroupData, TabItemType, tokenEquals, tokenToString} from './tab_data.js';
import {ProfileData, RecentlyClosedTab, RecentlyClosedTabGroup, Tab, TabGroup, TabsRemovedInfo, TabUpdateInfo} from './tab_search.mojom-webui.js';
import {TabSearchApiProxy, TabSearchApiProxyImpl} from './tab_search_api_proxy.js';
-import {TabSearchSearchField} from './tab_search_search_field.js';
import {tabHasMediaAlerts} from './tab_search_utils.js';
import {TitleItem} from './title_item.js';
@@ -39,6 +38,8 @@ import {TitleItem} from './title_item.js';
// height. Includes a half row that hints to the user the capability to scroll.
const MINIMUM_AVAILABLE_HEIGHT_LIST_ITEM_COUNT: number = 5.5;
+const TabSearchSearchFieldBase = CrSearchFieldMixin(PolymerElement);
+
/**
* These values are persisted to logs and should not be renumbered or re-used.
* See tools/metrics/histograms/enums.xml.
@@ -50,18 +51,33 @@ export enum TabSwitchAction {
export interface TabSearchAppElement {
$: {
- searchField: TabSearchSearchField,
+ searchField: HTMLElement,
+ searchInput: HTMLInputElement,
+ searchWrapper: HTMLElement,
tabsList: InfiniteList,
};
}
-export class TabSearchAppElement extends PolymerElement {
+export class TabSearchAppElement extends TabSearchSearchFieldBase {
static get is() {
return 'tab-search-app';
}
static get properties() {
return {
+ /**
+ * Text that describes the resulting tabs currently present in the list.
+ */
+ searchResultText_: {
+ type: String,
+ value: '',
+ },
+
+ shortcut_: {
+ type: String,
+ value: () => loadTimeData.getString('shortcutText'),
+ },
+
searchText_: {
type: String,
value: '',
@@ -113,8 +129,6 @@ export class TabSearchAppElement extends PolymerElement {
value: () =>
loadTimeData.getValue('recentlyClosedDefaultItemDisplayCount'),
},
-
- searchResultText_: {type: String, value: ''},
};
}
@@ -125,6 +139,9 @@ export class TabSearchAppElement extends PolymerElement {
private moveActiveTabToBottom_: boolean;
private recentlyClosedDefaultItemDisplayCount_: number;
private searchResultText_: string;
+ private activeSelectionId_: string;
+ private shortcut_: string;
+ override autofocus: boolean;
private apiProxy_: TabSearchApiProxy = TabSearchApiProxyImpl.getInstance();
private metricsReporter_: MetricsReporter|null;
@@ -230,6 +247,30 @@ export class TabSearchAppElement extends PolymerElement {
'visibilitychange', this.visibilityChangedListener_);
}
+ override getSearchInput(): HTMLInputElement {
+ return this.$.searchInput;
+ }
+
+ /**
+ * Do not schedule the timer from CrSearchFieldMixin to make search more
+ * responsive.
+ */
+ override onSearchTermInput() {
+ this.hasSearchText = this.getSearchInput().value !== '';
+ this.searchText_ = this.getSearchInput().value;
+ // Reset the selected item whenever a search query is provided.
+ // updateFilteredTabs_ will set the correct tab index for initial selection.
+ const tabsList = this.$.tabsList;
+ tabsList.selected = NO_SELECTION;
+
+ this.updateFilteredTabs_();
+
+ // http://crbug.com/1481787: Dispatch the search event to update the
+ // internal value to make CrSearchFieldMixin function correctly.
+ this.getSearchInput().dispatchEvent(
+ new CustomEvent('search', {composed: true, detail: this.searchText_}));
+ }
+
/**
* @param name A property whose value is specified in pixels.
*/
@@ -255,8 +296,8 @@ export class TabSearchAppElement extends PolymerElement {
private onDocumentHidden_() {
this.filteredItems_ = [];
- this.$.searchField.setValue('');
- this.$.searchField.getSearchInput().focus();
+ this.setValue('');
+ this.$.searchInput.focus();
}
private updateTabs_() {
@@ -368,17 +409,6 @@ export class TabSearchAppElement extends PolymerElement {
return this.$.tabsList.selected;
}
- private onSearchChanged_(e: CustomEvent<string>) {
- this.searchText_ = e.detail;
- // Reset the selected item whenever a search query is provided.
- // updateFilteredTabs_ will set the correct tab index for initial selection.
- const tabsList = this.$.tabsList;
- tabsList.selected = NO_SELECTION;
-
- this.updateFilteredTabs_();
- this.$.searchField.announce(this.getA11ySearchResultText_());
- }
-
private getA11ySearchResultText_(): string {
// TODO(romanarora): Screen readers' list item number announcement will
// not match as it counts the title items too. Investigate how to
@@ -517,6 +547,9 @@ export class TabSearchAppElement extends PolymerElement {
this.recentlyClosedTitleItem_.expanded =
profileData.recentlyClosedSectionExpanded;
+ this.$.tabsList.setAttribute(
+ 'expanded-list', profileData.recentlyClosedSectionExpanded.toString());
+
this.updateFilteredTabs_();
}
@@ -538,6 +571,8 @@ export class TabSearchAppElement extends PolymerElement {
titleItem.expanded = expanded;
this.apiProxy_.saveRecentlyClosedExpandedPref(expanded);
+ this.$.tabsList.setAttribute('expanded-list', expanded.toString());
+
this.updateFilteredTabs_();
// If a section's title item is the last visible element in the list and the
@@ -581,10 +616,6 @@ export class TabSearchAppElement extends PolymerElement {
e.stopPropagation();
e.preventDefault();
- // TODO(tluk): Fix this to use aria-activedescendant when it's updated to
- // work with Shadow DOM elements.
- this.$.searchField.announce(
- ariaLabel(this.$.tabsList.selectedItem as ItemData));
} else if (e.key === 'Enter') {
const itemData = this.$.tabsList.selectedItem as ItemData;
this.tabItemAction_(itemData, this.getSelectedIndex());
@@ -771,6 +802,12 @@ export class TabSearchAppElement extends PolymerElement {
static get template() {
return getTemplate();
}
+
+ private onSelectedItemChanged_() {
+ const item = this.$.tabsList.selectedItem;
+
+ this.activeSelectionId_ = item ? (item as any)?.tab?.tabId : null;
+ }
}
declare global {
diff --git a/chromium/chrome/browser/resources/tab_search/infinite_list.html b/chromium/chrome/browser/resources/tab_search/infinite_list.html
index 518643be171..93660730526 100644
--- a/chromium/chrome/browser/resources/tab_search/infinite_list.html
+++ b/chromium/chrome/browser/resources/tab_search/infinite_list.html
@@ -5,11 +5,16 @@
overflow-y: auto;
position: relative;
}
+
+ :host-context([expanded-list="true"]) {
+ padding-bottom: 16px;
+ }
</style>
<div id="container">
<iron-selector id="selector" on-keydown="onKeyDown_"
- on-iron-select="onSelectedChanged_" role="listbox"
- selectable="[[selectableSelector_()]]" selected-class="selected">
+ on-iron-select="onSelectedChanged_"
+ selectable="[[selectableSelector_()]]" selected-class="selected"
+ on-selected-item-changed="onSelectedItemChanged_">
<slot></slot>
</iron-selector>
</div>
diff --git a/chromium/chrome/browser/resources/tab_search/infinite_list.ts b/chromium/chrome/browser/resources/tab_search/infinite_list.ts
index 407f305e171..5a73b9a782b 100644
--- a/chromium/chrome/browser/resources/tab_search/infinite_list.ts
+++ b/chromium/chrome/browser/resources/tab_search/infinite_list.ts
@@ -64,11 +64,18 @@ export class InfiniteList extends PolymerElement {
observer: 'onItemsChanged_',
value: [],
},
+
+ selectedItem: {
+ type: Object,
+ readonly: true,
+ notify: true,
+ },
};
}
maxHeight: number;
items: Object[];
+ selectedItem: Object|null;
private instanceConstructors_:
Map<string,
new(args: {item: Object, index?: number}) =>
@@ -597,13 +604,8 @@ export class InfiniteList extends PolymerElement {
NO_SELECTION;
}
- get selectedItem(): Object|null {
- if (this.$.selector.selected === undefined) {
- return null;
- }
-
- return this.items[this.selectableIndexToItemIndex_!.get(
- this.$.selector.selected as number)!]!;
+ private onSelectedItemChanged_() {
+ this.selectedItem = (this.$.selector.selectedItem as any)?.data;
}
}
diff --git a/chromium/chrome/browser/resources/tab_search/tab_search.ts b/chromium/chrome/browser/resources/tab_search/tab_search.ts
index b9bcedf78af..5ce4e0f4997 100644
--- a/chromium/chrome/browser/resources/tab_search/tab_search.ts
+++ b/chromium/chrome/browser/resources/tab_search/tab_search.ts
@@ -14,6 +14,5 @@ export {PageCallbackRouter, PageRemote, ProfileData, RecentlyClosedTab, Recently
export {TabSearchApiProxy, TabSearchApiProxyImpl} from './tab_search_api_proxy.js';
export {TabSearchGroupItem} from './tab_search_group_item.js';
export {TabSearchItem} from './tab_search_item.js';
-export {TabSearchSearchField} from './tab_search_search_field.js';
export {TabAlertState} from './tabs.mojom-webui.js';
export {TitleItem} from './title_item.js';
diff --git a/chromium/chrome/browser/resources/tab_search/tab_search_search_field.html b/chromium/chrome/browser/resources/tab_search/tab_search_search_field.html
deleted file mode 100644
index 16875b0a64b..00000000000
--- a/chromium/chrome/browser/resources/tab_search/tab_search_search_field.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<style include="cr-icons">
- :host {
- align-items: center;
- background-color: var(--mwb-background-color);
- display: flex;
- height: 60px;
- padding: 0 var(--mwb-list-item-horizontal-margin);
- user-select: none;
- }
-
- :host([has-search-text]) label {
- visibility: hidden;
- }
-
- #searchIcon {
- color: var(--cr-secondary-text-color);
- height: var(--mwb-icon-size);
- padding-inline-end: 8px;
- width: var(--mwb-icon-size);
- }
-
- #searchWrapper {
- display: flex;
- flex: 1;
- height: 100%;
- position: relative;
- }
-
- #searchLabel {
- align-items: center;
- color: var(--cr-secondary-text-color);
- cursor: text;
- display: flex;
- font-size: var(--mwb-primary-text-font-size);
- font-weight: var(--mwb-secondary-text-font-weight);
- height: 100%;
- justify-content: space-between;
- position: absolute;
- width: 100%;
- }
-
- #searchInput {
- background-color: transparent;
- border: none;
- border-radius: 0;
- color: var(--cr-primary-text-color);
- flex: 1;
- font-family: inherit;
- font-size: var(--mwb-primary-text-font-size);
- font-style: inherit;
- font-weight: var(--mwb-secondary-text-font-weight);
- outline: none;
- padding: 0;
- text-overflow: ellipsis;
- }
-
- #searchInput::-webkit-search-cancel-button {
- display: none;
- }
-
- #inputAnnounce,
- #searchResultText {
- clip: rect(0,0,0,0);
- display: inline-block;
- position: fixed;
- }
-</style>
-
-<iron-icon id="searchIcon" icon="mwb16:search"></iron-icon>
-<div id="searchWrapper">
- <label id="searchLabel" for="searchInput" aria-hidden="true">
- <span>[[label]]</span>
- <span>[[shortcut_]]</span>
- <span id="searchResultText">[[searchResultText]]</span>
- </label>
- <input id="searchInput" aria-labelledby="searchLabel"
- autofocus="[[autofocus]]" autocomplete="off"
- on-search="onSearchTermSearch" on-input="onSearchTermInput" type="search"
- spellcheck="false" aria-describedby="inputAnnounce"
- on-blur="onInputBlur_">
- <span id="inputAnnounce" aria-live="assertive">
- [[announceText_]]
- </span>
-</div>
diff --git a/chromium/chrome/browser/resources/tab_search/tab_search_search_field.ts b/chromium/chrome/browser/resources/tab_search/tab_search_search_field.ts
deleted file mode 100644
index c483ded4ed8..00000000000
--- a/chromium/chrome/browser/resources/tab_search/tab_search_search_field.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/cr_elements/mwb_shared_icons.html.js';
-import 'chrome://resources/cr_elements/mwb_shared_vars.css.js';
-import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
-import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
-
-import {CrSearchFieldMixin} from 'chrome://resources/cr_elements/cr_search_field/cr_search_field_mixin.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {getTemplate} from './tab_search_search_field.html.js';
-
-const TabSearchSearchFieldBase = CrSearchFieldMixin(PolymerElement);
-
-export interface TabSearchSearchField {
- $: {
- inputAnnounce: HTMLElement,
- searchInput: HTMLInputElement,
- searchWrapper: HTMLElement,
- };
-}
-
-export class TabSearchSearchField extends TabSearchSearchFieldBase {
- static get is() {
- return 'tab-search-search-field';
- }
-
- static get template() {
- return getTemplate();
- }
-
- static get properties() {
- return {
- /**
- * Controls autofocus for the search field.
- */
- autofocus: {
- type: Boolean,
- value: false,
- },
-
- /**
- * Text that describes the resulting tabs currently present in the list.
- */
- searchResultText: {
- type: String,
- value: '',
- },
-
- shortcut_: {
- type: String,
- value: () => loadTimeData.getString('shortcutText'),
- },
-
- announceText_: {
- type: String,
- value: '',
- },
- };
- }
-
- override autofocus: boolean;
- searchResultText: string;
- private shortcut_: string;
- private announceText_: string;
-
- override getSearchInput(): HTMLInputElement {
- return this.$.searchInput;
- }
-
- /**
- * Cause a text string to be announced by screen readers. Used for announcing
- * when the input field has focus for better compatibility with screen
- * readers.
- * @param text The text that should be announced.
- */
- announce(text: string) {
- this.$.searchWrapper.append(
- this.$.searchWrapper.removeChild(this.$.inputAnnounce));
- if (this.announceText_ === text) {
- // A timeout is required when announcing duplicate text for certain
- // screen readers.
- this.announceText_ = '';
- setTimeout(() => {
- this.announceText_ = text;
- }, 100);
- } else {
- this.announceText_ = text;
- }
- }
-
- /**
- * Clear |announceText_| when focus leaves the input field to ensure the text
- * is not re-announced when focus returns to the input field.
- */
- private onInputBlur_() {
- this.announceText_ = '';
- }
-
- /**
- * Do not schedule the timer from CrSearchFieldMixin to make search more
- * responsive.
- */
- override onSearchTermInput() {
- this.hasSearchText = this.$.searchInput.value !== '';
- this.getSearchInput().dispatchEvent(
- new CustomEvent('search', {composed: true, detail: this.getValue()}));
- }
-}
-
-customElements.define(TabSearchSearchField.is, TabSearchSearchField);
diff --git a/chromium/chrome/browser/resources/waffle/DIR_METADATA b/chromium/chrome/browser/resources/waffle/DIR_METADATA
deleted file mode 100644
index 6cfbfc28de5..00000000000
--- a/chromium/chrome/browser/resources/waffle/DIR_METADATA
+++ /dev/null
@@ -1 +0,0 @@
-mixins: "//chrome/browser/ui/waffle/COMMON_METADATA"
diff --git a/chromium/chrome/browser/resources/waffle/OWNERS b/chromium/chrome/browser/resources/waffle/OWNERS
deleted file mode 100644
index 55c6bfbde5e..00000000000
--- a/chromium/chrome/browser/resources/waffle/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://chrome/browser/ui/waffle/OWNERS
diff --git a/chromium/chrome/browser/resources/waffle/app.html b/chromium/chrome/browser/resources/waffle/app.html
deleted file mode 100644
index 632d9c1989a..00000000000
--- a/chromium/chrome/browser/resources/waffle/app.html
+++ /dev/null
@@ -1,141 +0,0 @@
-<style>
- :host {
- color: var(--cr-primary-text-color);
- }
-
- .left-illustration,
- .right-illustration {
- position: absolute;
- top:0;
- width: 20vw;
- }
-
- .left-illustration {
- content: url(images/left_illustration.svg);
- left: 0;
- }
-
- .right-illustration {
- content: url(images/right_illustration.svg);
- right: 0;
- }
-
- .button-container {
- bottom: 0;
- display: flex;
- position: absolute;
- width: 100%;
- }
-
- cr-button {
- margin: 25px;
- }
-
- .first-button {
- margin-inline-end: auto;
- }
-
- .second-button {
- margin-inline-start: auto;
- }
-
- .content-container {
- align-items: center;
- display: flex;
- flex-direction: column;
- margin: auto;
- max-width: 472px;
- text-align: center;
- }
-
- .title {
- font-weight: 400;
- margin-top: 68px;
- }
-
- .subtitle {
- color: var(--cr-seconday-text-color);
- font-size: 0.88rem;
- line-height: 22px;
- }
-
- .image-placeholder {
- background-color: blue;
- height: 70px;
- width: 250px;
- }
-
- .choice-list {
- display: flex;
- flex-direction: column;
- gap: 1px;
- }
-
- .choice {
- align-items: center;
- background-color: var(--google-grey-200);
- display: flex;
- flex-direction: row;
- gap: 16px;
- height: 24px;
- padding: 16px 24px;
- width: 424px;
- }
-
- .choice:first-child {
- border-top-left-radius: 24px;
- border-top-right-radius: 24px;
- }
-
- div.choice:last-of-type {
- border-bottom-left-radius: 24px;
- border-bottom-right-radius: 24px;
- }
-
- .choice-icon {
- background-color: green;
- height: 24px;
- width: 24px;
- }
-
- .choice-text {
- font-size: 0.88rem;
- line-height: 20px;
- }
-
- cr-checkbox {
- margin-inline-start: auto;
- }
-
- @media (prefers-color-scheme: dark) {
- .left-illustration {
- content: url(images/left_illustration_dark.svg);
- }
-
- .right-illustration {
- content: url(images/right_illustration_dark.svg);
- }
- }
-</style>
-
-<img class="left-illustration" alt="">
-<img class="right-illustration" alt="">
-<div class="content-container">
- <h1 class="title">$i18n{title}</h1>
- <div class="image-placeholder"></div>
- <p class="subtitle">$i18n{subtitle}</p>
- <div class="choice-list">
- <template is="dom-repeat" items="[[choiceList_]]">
- <div class="choice">
- <div class="choice-icon"></div>
- <div class="choice-text">[[item.name]]</div>
- <cr-checkbox></cr-checkbox>
- </div>
- </template>
- </div>
-</div>
-<div class="button-container">
- <cr-button class="first-button">$i18n{firstButton}</cr-button>
- <cr-button class="second-button action-button" disabled>
- $i18n{secondButton}</cr-button>
-</div>
diff --git a/chromium/chrome/browser/resources/waffle/app.ts b/chromium/chrome/browser/resources/waffle/app.ts
deleted file mode 100644
index 41f3ca64413..00000000000
--- a/chromium/chrome/browser/resources/waffle/app.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
-import 'chrome://resources/cr_elements/cr_button/cr_button.js';
-import 'chrome://resources/cr_components/localized_link/localized_link.js';
-import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
-import './strings.m.js';
-
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {getTemplate} from './app.html.js';
-import {WaffleChoice} from './browser_proxy';
-import {WaffleBrowserProxy} from './browser_proxy.js';
-
-export class WaffleAppElement extends PolymerElement {
- static get is() {
- return 'waffle-app';
- }
-
- static get template() {
- return getTemplate();
- }
-
- static get properties() {
- return {
- /**
- * We pass the choice list as JSON because it doesn't change
- * dynamically, so it would be better to have it available as loadtime
- * data.
- */
- choiceList_: {
- type: Array,
- value() {
- return JSON.parse(loadTimeData.getString('choiceList'));
- },
- },
- };
- }
-
- private choiceList_: WaffleChoice[];
-
- override connectedCallback() {
- super.connectedCallback();
-
- afterNextRender(this, () => {
- WaffleBrowserProxy.getInstance().handler.displayDialog();
- });
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'waffle-app': WaffleAppElement;
- }
-}
-
-customElements.define(WaffleAppElement.is, WaffleAppElement);
diff --git a/chromium/chrome/browser/resources/waffle/browser_proxy.ts b/chromium/chrome/browser/resources/waffle/browser_proxy.ts
deleted file mode 100644
index 37a272f5986..00000000000
--- a/chromium/chrome/browser/resources/waffle/browser_proxy.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview A helper object used by the chrome://waffle page to
- * interact with the browser.
- */
-
-import {PageHandlerFactory, PageHandlerInterface, PageHandlerRemote} from './waffle.mojom-webui.js';
-
-export interface WaffleChoice {
- name: string;
-}
-
-export class WaffleBrowserProxy {
- handler: PageHandlerInterface;
-
- constructor() {
- this.handler = new PageHandlerRemote();
-
- const factory = PageHandlerFactory.getRemote();
- factory.createPageHandler(
- (this.handler as PageHandlerRemote).$.bindNewPipeAndPassReceiver());
- }
-
- static getInstance(): WaffleBrowserProxy {
- return instance || (instance = new WaffleBrowserProxy());
- }
-
- static setInstance(obj: WaffleBrowserProxy) {
- instance = obj;
- }
-}
-
-let instance: WaffleBrowserProxy|null = null;
diff --git a/chromium/chrome/browser/resources/web_app_internals/web_app_internals.html b/chromium/chrome/browser/resources/web_app_internals/web_app_internals.html
index 0093c8e96eb..807486635dd 100644
--- a/chromium/chrome/browser/resources/web_app_internals/web_app_internals.html
+++ b/chromium/chrome/browser/resources/web_app_internals/web_app_internals.html
@@ -16,6 +16,17 @@
<hr>
+ <div id="iwa-install-div" style="display: none;">
+ <p>Isolated Web Apps</p>
+ <p>Install IWA via Dev Mode Proxy:
+ <input type="url" id="iwa-install-url" size="30" required
+ placeholder="http://localhost:8000/">
+ <button id="iwa-install-button" type="submit">Install</button>
+ </p>
+ <div id="iwa-install-message-div"></div>
+ <hr>
+ </div>
+
<pre id="json"></pre>
</body>
diff --git a/chromium/chrome/browser/resources/web_app_internals/web_app_internals.ts b/chromium/chrome/browser/resources/web_app_internals/web_app_internals.ts
index 20aa077b17a..cae8b7d05d2 100644
--- a/chromium/chrome/browser/resources/web_app_internals/web_app_internals.ts
+++ b/chromium/chrome/browser/resources/web_app_internals/web_app_internals.ts
@@ -2,7 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import './strings.m.js';
+
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {getRequiredElement} from 'chrome://resources/js/util_ts.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
import {WebAppInternalsHandler} from './web_app_internals.mojom-webui.js';
@@ -12,6 +16,10 @@ const debugInfoAsJsonString: Promise<string> =
webAppInternalsHandler.getDebugInfoAsJsonString().then(
response => response.result);
+const iwaInstallButton =
+ getRequiredElement('iwa-install-button') as HTMLButtonElement;
+const iwaInstallUrl = getRequiredElement('iwa-install-url') as HTMLInputElement;
+
getRequiredElement('copy-button').addEventListener('click', async () => {
navigator.clipboard.writeText(await debugInfoAsJsonString);
});
@@ -34,6 +42,75 @@ getRequiredElement('download-button').addEventListener('click', async () => {
URL.revokeObjectURL(url);
});
+function iwaInstallStateUpdate() {
+ iwaInstallButton.disabled = (iwaInstallUrl.value.length === 0);
+}
+
+async function iwaInstallSubmit() {
+ iwaInstallButton.disabled = true;
+
+ const iwaInstallMessageDiv = getRequiredElement('iwa-install-message-div');
+
+ // Validate the provided URL.
+ let valid = false;
+ try {
+ // We don't need the result of this, only to verify it doesn't throw an
+ // exception.
+ new URL(iwaInstallUrl.value);
+ valid =
+ (iwaInstallUrl.value.startsWith('http:') ||
+ iwaInstallUrl.value.startsWith('https:'));
+ } catch (_) {
+ // Fall-through.
+ }
+ if (!valid) {
+ iwaInstallMessageDiv.innerText =
+ `Installing IWA: ${iwaInstallUrl.value} is not a valid URL`;
+ iwaInstallStateUpdate();
+ return;
+ }
+
+ iwaInstallMessageDiv.innerText = `Installing IWA: ${iwaInstallUrl.value}...`;
+
+ const location = new Url();
+ location.url = iwaInstallUrl.value;
+
+ const installFromDevProxy =
+ await webAppInternalsHandler.installIsolatedWebAppFromDevProxy(location);
+ if (installFromDevProxy.result.success) {
+ iwaInstallMessageDiv.innerText =
+ `Installing IWA: ${iwaInstallUrl.value} successfully installed.`;
+ iwaInstallUrl.value = '';
+ iwaInstallStateUpdate();
+ return;
+ }
+
+ iwaInstallMessageDiv.innerText =
+ `Installing IWA: ${iwaInstallUrl.value} failed to install: ${
+ installFromDevProxy.result.error}`;
+ iwaInstallStateUpdate();
+}
+
+iwaInstallUrl.addEventListener('enter', iwaInstallSubmit);
+iwaInstallButton.addEventListener('click', iwaInstallSubmit);
+
+function updateIwaInstallButtonState(event: KeyboardEvent) {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ iwaInstallSubmit();
+ return;
+ }
+ iwaInstallStateUpdate();
+}
+iwaInstallUrl.addEventListener('keyup', updateIwaInstallButtonState);
+iwaInstallStateUpdate();
+
document.addEventListener('DOMContentLoaded', async () => {
getRequiredElement('json').innerText = await debugInfoAsJsonString;
+
+ if (loadTimeData.getBoolean('experimentalIsIwaDevModeEnabled')) {
+ // Unhide the IWA install div.
+ getRequiredElement('iwa-install-div').style.display = '';
+ return;
+ }
});
diff --git a/chromium/chrome/browser/resources/webapks/about_webapks.ts b/chromium/chrome/browser/resources/webapks/about_webapks.ts
index c091d4671c4..4fda385621b 100644
--- a/chromium/chrome/browser/resources/webapks/about_webapks.ts
+++ b/chromium/chrome/browser/resources/webapks/about_webapks.ts
@@ -21,6 +21,8 @@ interface WebApkInfo {
orientation: string;
themeColor: string;
backgroundColor: string;
+ darkThemeColor: string;
+ darkBackgroundColor: string;
lastUpdateCheckTimeMs: number;
lastUpdateCompletionTimeMs: number;
relaxUpdates: boolean;
@@ -104,6 +106,9 @@ function addWebApk(webApkInfo: WebApkInfo) {
addWebApkField(webApkList, 'Orientation: ', webApkInfo.orientation);
addWebApkField(webApkList, 'Theme color: ', webApkInfo.themeColor);
addWebApkField(webApkList, 'Background color: ', webApkInfo.backgroundColor);
+ addWebApkField(webApkList, 'Dark theme color: ', webApkInfo.darkThemeColor);
+ addWebApkField(
+ webApkList, 'Dark background color: ', webApkInfo.darkBackgroundColor);
addWebApkField(
webApkList, 'Last Update Check Time: ',
new Date(webApkInfo.lastUpdateCheckTimeMs).toString());
diff --git a/chromium/chrome/browser/resources/webui_gallery/BUILD.gn b/chromium/chrome/browser/resources/webui_gallery/BUILD.gn
index d482eddf1ba..7d7e5c51e06 100644
--- a/chromium/chrome/browser/resources/webui_gallery/BUILD.gn
+++ b/chromium/chrome/browser/resources/webui_gallery/BUILD.gn
@@ -26,6 +26,7 @@ build_webui("build") {
"demos/cr_a11y_announcer/cr_a11y_announcer_demo.ts",
"demos/cr_action_menu/cr_action_menu_demo.ts",
"demos/cr_checkbox/cr_checkbox_demo.ts",
+ "demos/cr_chip/cr_chip_demo.ts",
"demos/cr_dialog/cr_dialog_demo.ts",
"demos/cr_icons/cr_icons_demo.ts",
"demos/cr_input/cr_input_demo.ts",
diff --git a/chromium/chrome/browser/resources/webui_gallery/app.ts b/chromium/chrome/browser/resources/webui_gallery/app.ts
index d1700480de3..d291a0efce7 100644
--- a/chromium/chrome/browser/resources/webui_gallery/app.ts
+++ b/chromium/chrome/browser/resources/webui_gallery/app.ts
@@ -69,6 +69,11 @@ export class WebuiGalleryAppElement extends PolymerElement {
src: 'cr_checkbox/cr_checkbox_demo.js',
},
{
+ name: 'Chips',
+ path: 'chips',
+ src: 'cr_chip/cr_chip_demo.js',
+ },
+ {
name: 'Dialogs',
path: 'dialogs',
src: 'cr_dialog/cr_dialog_demo.js',
diff --git a/chromium/chrome/browser/resources/webui_gallery/demos/cr_chip/cr_chip_demo.html b/chromium/chrome/browser/resources/webui_gallery/demos/cr_chip/cr_chip_demo.html
new file mode 100644
index 00000000000..55c2293152c
--- /dev/null
+++ b/chromium/chrome/browser/resources/webui_gallery/demos/cr_chip/cr_chip_demo.html
@@ -0,0 +1,30 @@
+<style include="demo">
+</style>
+
+<h1>cr-chip</h1>
+<div class="demos">
+ <cr-chip>
+ <iron-icon icon="cr:print"></iron-icon>
+ Action
+ </cr-chip>
+
+ <cr-chip chip-role="link">
+ <iron-icon icon="cr:print"></iron-icon>
+ Action Link
+ </cr-chip>
+
+ <cr-chip>
+ <iron-icon icon="cr:add"></iron-icon>
+ Filter
+ </cr-chip>
+
+ <cr-chip selected>
+ <iron-icon icon="cr:check"></iron-icon>
+ Selected filter
+ </cr-chip>
+
+ <cr-chip disabled>
+ <iron-icon icon="cr:clear"></iron-icon>
+ Disabled filter
+ </cr-chip>
+</div>
diff --git a/chromium/chrome/browser/resources/webui_gallery/demos/cr_chip/cr_chip_demo.ts b/chromium/chrome/browser/resources/webui_gallery/demos/cr_chip/cr_chip_demo.ts
new file mode 100644
index 00000000000..2d8cfd69d43
--- /dev/null
+++ b/chromium/chrome/browser/resources/webui_gallery/demos/cr_chip/cr_chip_demo.ts
@@ -0,0 +1,26 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '//resources/cr_elements/cr_chip/cr_chip.js';
+import '//resources/cr_elements/icons.html.js';
+import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '../demo.css.js';
+
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './cr_chip_demo.html.js';
+
+class CrChipDemoElement extends PolymerElement {
+ static get is() {
+ return 'cr-chip-demo';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+}
+
+export const tagName = CrChipDemoElement.is;
+
+customElements.define(CrChipDemoElement.is, CrChipDemoElement);
diff --git a/chromium/chrome/browser/resources/webui_gallery/demos/cr_icons/cr_icons_demo.ts b/chromium/chrome/browser/resources/webui_gallery/demos/cr_icons/cr_icons_demo.ts
index 85013322c16..a140b9203b7 100644
--- a/chromium/chrome/browser/resources/webui_gallery/demos/cr_icons/cr_icons_demo.ts
+++ b/chromium/chrome/browser/resources/webui_gallery/demos/cr_icons/cr_icons_demo.ts
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/chromium/chrome/browser/resources/webui_gallery/demos/cr_input/cr_input_demo.html b/chromium/chrome/browser/resources/webui_gallery/demos/cr_input/cr_input_demo.html
index c77997a338c..dab379be3b4 100644
--- a/chromium/chrome/browser/resources/webui_gallery/demos/cr_input/cr_input_demo.html
+++ b/chromium/chrome/browser/resources/webui_gallery/demos/cr_input/cr_input_demo.html
@@ -5,20 +5,10 @@
width: 100%;
}
- .cr-icon {
- --cr-icon-button-margin-start: 0;
- }
-
.no-error {
--cr-input-error-display: none;
}
- .icon-cancel {
- --cr-icon-button-icon-size: 16px;
- --cr-icon-button-margin-end: 6px;
- --cr-icon-button-size: 24px;
- }
-
.domain-name {
padding-inline-end: 8px;
}
diff --git a/chromium/chrome/browser/resources/webui_gallery/demos/cr_radio/cr_radio_demo.html b/chromium/chrome/browser/resources/webui_gallery/demos/cr_radio/cr_radio_demo.html
index f038615e130..512da99b4dc 100644
--- a/chromium/chrome/browser/resources/webui_gallery/demos/cr_radio/cr_radio_demo.html
+++ b/chromium/chrome/browser/resources/webui_gallery/demos/cr_radio/cr_radio_demo.html
@@ -16,7 +16,10 @@
<cr-radio-button name="option3" label="Option 3">
<div>With slotted content</div>
</cr-radio-button>
- <cr-radio-button name="option4" label="Option 4" disabled>
+ <cr-radio-button name="option4" label="Option 4" class="label-first">
+ <div>Radio button with the label showing first</div>
+ </cr-radio-button>
+ <cr-radio-button name="option5" label="Option 5" disabled>
<div>Disabled</div>
</cr-radio-button>
</cr-radio-group>
diff --git a/chromium/chrome/browser/resources/webui_gallery/demos/cr_url_list_item/cr_url_list_item_demo.html b/chromium/chrome/browser/resources/webui_gallery/demos/cr_url_list_item/cr_url_list_item_demo.html
index b10f7238cc8..1c85661663d 100644
--- a/chromium/chrome/browser/resources/webui_gallery/demos/cr_url_list_item/cr_url_list_item_demo.html
+++ b/chromium/chrome/browser/resources/webui_gallery/demos/cr_url_list_item/cr_url_list_item_demo.html
@@ -46,7 +46,7 @@
<cr-url-list-item url="http://www.google.com"
title="Google"
description="google.com"
- timestamp="3 days ago">
+ description-meta="3 days ago">
<cr-icon-button iron-icon="cr:check-circle" slot="suffix-icon">
</cr-icon-button>
<cr-icon-button iron-icon="cr:more-vert" slot="suffix-icon">
@@ -95,7 +95,7 @@
really long title"
description="aurlthatisreallyreallyreallyreallyreallyreallylong.com"
reverse-elide-description
- timestamp="2 hours ago">
+ description-meta="2 hours ago">
</cr-url-list-item>
<cr-url-list-item count="23" size="compact"
@@ -108,7 +108,7 @@
<h2>Large</h2>
<div class="demos">
<cr-url-list-item url="http://www.google.com" size="large"
- title="Google" description="google.com" timestamp="2 mins">
+ title="Google" description="google.com" description-meta="2 mins">
<div class="badge" slot="badges">
<iron-icon icon="cr:error-outline"></iron-icon> Badge 1
</div>
diff --git a/chromium/chrome/browser/resources/webui_gallery/demos/md_select/md_select_demo.ts b/chromium/chrome/browser/resources/webui_gallery/demos/md_select/md_select_demo.ts
index c7450dd4d65..abe546b9198 100644
--- a/chromium/chrome/browser/resources/webui_gallery/demos/md_select/md_select_demo.ts
+++ b/chromium/chrome/browser/resources/webui_gallery/demos/md_select/md_select_demo.ts
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/chromium/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.html b/chromium/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.html
index 4a68e09d3a4..7a7e4f1ddb3 100644
--- a/chromium/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.html
+++ b/chromium/chrome/browser/resources/webui_gallery/demos/side_panel/sp_components_demo.html
@@ -29,18 +29,14 @@
padding: 0;
}
- #singleCard .sp-card,
- #multipleCards {
+ #scroller {
display: flex;
flex-direction: column;
- min-height: 0;
- }
-
- #multipleCards {
height: 200px;
+ min-height: 0;
}
- .multiple-card-content {
+ .card-content {
align-items: center;
display: flex;
height: 300px;
@@ -85,61 +81,33 @@
</div>
</div>
-<h2>With singular card</h2>
-<div id="singleCard" class="side-panel-demo">
+<h2>Scrollable container with cards</h2>
+<div id="scroller" class="side-panel-demo sp-scroller">
<div class="sp-card">
<sp-heading hide-back-button="[[hideBackButton_]]">
<h3 slot="heading">Heading</h3>
-
- <span slot="metadata">Metadata</span>
- <cr-icon-button slot="buttons" iron-icon="sp:filter-list"
- aria-label="Filter">
- </cr-icon-button>
- <cr-icon-button slot="buttons" iron-icon="cr:sync"
- aria-label="Sync">
- </cr-icon-button>
- <cr-icon-button slot="buttons" iron-icon="cr:create"
- aria-label="Create">
- </cr-icon-button>
</sp-heading>
- <div class="sp-scroller">
- <template is="dom-repeat" items="[[urls_]]">
- <cr-url-list-item title="[[item.title]]"
- description="[[item.url]]"
- url="[[item.url]]"
- size="[[itemSize_]]">
- <template is="dom-if" if="[[showBadges_]]" restamp>
- <sp-list-item-badge slot="badges">
- <iron-icon icon="cr:info-outline"></iron-icon>
- <span>2 Notes</span>
- </sp-list-item-badge>
- </template>
- </cr-url-list-item>
- </template>
- </div>
- </div>
- <sp-footer pinned>
- <cr-button class="floating-button">
- <iron-icon slot="prefix-icon" icon="cr:add"></iron-icon>
- Floating button
- </cr-button>
- </sp-footer>
-</div>
-<h2>Multiple cards</h2>
-<div id="multipleCards" class="side-panel-demo sp-scroller">
- <div class="sp-card">
- <sp-heading hide-back-button>
- <h3 slot="heading">Heading</h3>
- </sp-heading>
- <div class="multiple-card-content">Some content</div>
+ <template is="dom-repeat" items="[[urls_]]">
+ <cr-url-list-item title="[[item.title]]"
+ description="[[item.url]]"
+ url="[[item.url]]"
+ size="[[itemSize_]]">
+ <template is="dom-if" if="[[showBadges_]]" restamp>
+ <sp-list-item-badge slot="badges">
+ <iron-icon icon="cr:info-outline"></iron-icon>
+ <span>2 Notes</span>
+ </sp-list-item-badge>
+ </template>
+ </cr-url-list-item>
+ </template>
</div>
<hr class="sp-cards-separator">
<div class="sp-card">
<sp-heading hide-back-button>
<h3 slot="heading">Heading</h3>
</sp-heading>
- <div class="multiple-card-content">Some content</div>
+ <div class="card-content">Some content</div>
</div>
</div>
diff --git a/chromium/chrome/browser/safe_browsing/BUILD.gn b/chromium/chrome/browser/safe_browsing/BUILD.gn
index 408c5117c7e..0f823ed1e70 100644
--- a/chromium/chrome/browser/safe_browsing/BUILD.gn
+++ b/chromium/chrome/browser/safe_browsing/BUILD.gn
@@ -68,10 +68,6 @@ static_library("safe_browsing") {
"//services/preferences/public/cpp",
]
- if (is_apple) {
- configs += [ "//build/config/compiler:enable_arc" ]
- }
-
if (enable_extensions) {
sources += [
"settings_reset_prompt/default_settings_fetcher.cc",
@@ -226,6 +222,8 @@ static_library("safe_browsing") {
"chrome_enterprise_url_lookup_service.h",
"chrome_enterprise_url_lookup_service_factory.cc",
"chrome_enterprise_url_lookup_service_factory.h",
+ "chrome_safe_browsing_hats_delegate.cc",
+ "chrome_safe_browsing_hats_delegate.h",
"cloud_content_scanning/binary_fcm_service.cc",
"cloud_content_scanning/binary_fcm_service.h",
"cloud_content_scanning/binary_upload_service.cc",
@@ -300,6 +298,8 @@ static_library("safe_browsing") {
"extension_telemetry/extension_telemetry_service_factory.h",
"extension_telemetry/extension_telemetry_uploader.cc",
"extension_telemetry/extension_telemetry_uploader.h",
+ "extension_telemetry/extension_web_request_reporter_impl.cc",
+ "extension_telemetry/extension_web_request_reporter_impl.h",
"extension_telemetry/password_reuse_signal.cc",
"extension_telemetry/password_reuse_signal.h",
"extension_telemetry/potential_password_theft_signal_processor.cc",
@@ -308,6 +308,10 @@ static_library("safe_browsing") {
"extension_telemetry/remote_host_contacted_signal.h",
"extension_telemetry/remote_host_contacted_signal_processor.cc",
"extension_telemetry/remote_host_contacted_signal_processor.h",
+ "extension_telemetry/tabs_api_signal.cc",
+ "extension_telemetry/tabs_api_signal.h",
+ "extension_telemetry/tabs_api_signal_processor.cc",
+ "extension_telemetry/tabs_api_signal_processor.h",
"extension_telemetry/tabs_execute_script_signal.cc",
"extension_telemetry/tabs_execute_script_signal.h",
"extension_telemetry/tabs_execute_script_signal_processor.cc",
@@ -402,6 +406,7 @@ static_library("safe_browsing") {
"//components/language/core/common",
"//components/policy/core/browser",
"//components/prefs",
+ "//components/safe_browsing/content/common:interfaces",
"//components/safe_browsing/core/browser:verdict_cache_manager",
"//components/safe_browsing/core/browser/db",
"//components/safe_browsing/core/browser/db:v4_store",
diff --git a/chromium/chrome/browser/safe_browsing/android/BUILD.gn b/chromium/chrome/browser/safe_browsing/android/BUILD.gn
index 7869e67c597..c8d862b531c 100644
--- a/chromium/chrome/browser/safe_browsing/android/BUILD.gn
+++ b/chromium/chrome/browser/safe_browsing/android/BUILD.gn
@@ -21,6 +21,7 @@ source_set("android") {
"//components/password_manager/core/browser/leak_detection",
"//components/password_manager/core/common",
"//components/prefs",
+ "//components/safe_browsing/core/browser/hashprefix_realtime:hash_realtime_utils",
"//components/safe_browsing/core/common:safe_browsing_prefs",
"//components/signin/public/identity_manager",
@@ -46,6 +47,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/safe_browsing/settings/RadioButtonGroupSafeBrowsingPreference.java",
"java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragment.java",
"java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragmentBase.java",
+ "java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragmentHelper.java",
"java/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragment.java",
]
deps = [
@@ -64,7 +66,9 @@ android_library("java") {
"//components/browser_ui/widget/android:java",
"//components/prefs/android:java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_browser_browser_java",
"//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
@@ -84,6 +88,7 @@ android_library("javatests") {
testonly = true
resources_package = "org.chromium.chrome.browser.safe_browsing.settings"
sources = [
+ "javatests/src/org/chromium/chrome/browser/safe_browsing/settings/EnhancedProtectionSettingsFragmentTest.java",
"javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragmentTest.java",
"javatests/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragmentTest.java",
]
@@ -91,6 +96,7 @@ android_library("javatests") {
":java",
":java_resources",
"//base:base_java_test_support",
+ "//build/android:build_java",
"//chrome/android:chrome_java",
"//chrome/browser/feedback/android:java",
"//chrome/browser/flags:java",
@@ -103,13 +109,17 @@ android_library("javatests") {
"//components/browser_ui/widget/android:java",
"//components/policy/android:policy_java_test_support",
"//components/prefs/android:java",
+ "//components/signin/public/android:java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
]
@@ -119,6 +129,7 @@ android_resources("java_resources") {
sources = [
"java/res/layout/radio_button_group_safe_browsing_preference.xml",
"java/res/xml/enhanced_protection_preferences.xml",
+ "java/res/xml/enhanced_protection_preferences_updated.xml",
"java/res/xml/safe_browsing_preferences.xml",
"java/res/xml/standard_protection_preferences.xml",
]
diff --git a/chromium/chrome/browser/safety_check/android/BUILD.gn b/chromium/chrome/browser/safety_check/android/BUILD.gn
index a6feb554e3e..10388187570 100644
--- a/chromium/chrome/browser/safety_check/android/BUILD.gn
+++ b/chromium/chrome/browser/safety_check/android/BUILD.gn
@@ -81,15 +81,18 @@ android_library("javatests") {
":java_resources",
"//base:base_java_test_support",
"//base/test:test_support_java",
+ "//chrome/android:chrome_java",
"//chrome/browser/password_check:public_java",
"//chrome/browser/preferences:java",
"//chrome/browser/settings:test_support_java",
"//chrome/test/android:chrome_java_integration_test_support",
"//content/public/test/android:content_java_test_support",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
@@ -115,6 +118,7 @@ robolectric_library("junit") {
"//components/user_prefs/android:java",
# Robolectric needs the full PasswordCheckFactory implementation.
+ "//base:jni_java",
"//chrome/browser/password_check/android/internal:internal_factory_java",
"//chrome/browser/password_manager/android:java",
"//chrome/browser/password_manager/android:settings_interface_java",
diff --git a/chromium/chrome/browser/screen_ai/BUILD.gn b/chromium/chrome/browser/screen_ai/BUILD.gn
index 44e3380257f..c39fe4823ca 100644
--- a/chromium/chrome/browser/screen_ai/BUILD.gn
+++ b/chromium/chrome/browser/screen_ai/BUILD.gn
@@ -5,10 +5,10 @@
import("//build/config/ui.gni")
if (is_chromeos_ash) {
- source_set("screen_ai_chromeos_installer") {
+ source_set("screen_ai_dlc_installer") {
sources = [
- "screen_ai_chromeos_installer.cc",
- "screen_ai_chromeos_installer.h",
+ "screen_ai_dlc_installer.cc",
+ "screen_ai_dlc_installer.h",
]
deps = [
@@ -47,7 +47,7 @@ source_set("screen_ai_service_router_factory") {
deps = [
":screen_ai_install_state",
- "//components/keyed_service/content",
+ "//chrome/browser/profiles:profile",
"//components/keyed_service/core",
"//components/services/screen_ai/public/mojom",
"//content/public/browser",
diff --git a/chromium/chrome/browser/screenshot_monitor/BUILD.gn b/chromium/chrome/browser/screenshot_monitor/BUILD.gn
new file mode 100644
index 00000000000..9cf142abcbf
--- /dev/null
+++ b/chromium/chrome/browser/screenshot_monitor/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/config.gni")
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+ sources = [
+ "java/src/org/chromium/chrome/browser/screenshot_monitor/ScreenshotMonitor.java",
+ "java/src/org/chromium/chrome/browser/screenshot_monitor/ScreenshotMonitorDelegate.java",
+ "java/src/org/chromium/chrome/browser/screenshot_monitor/ScreenshotMonitorImpl.java",
+ "java/src/org/chromium/chrome/browser/screenshot_monitor/ScreenshotTabObserver.java",
+ ]
+
+ deps = [
+ "//base:base_java",
+ "//chrome/browser/profiles/android:java",
+ "//chrome/browser/tab:java",
+ "//components/ukm/android:java",
+ "//content/public/android:content_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_core_core_java",
+ "//ui/android:ui_full_java",
+ ]
+}
diff --git a/chromium/chrome/browser/search_engines/android/BUILD.gn b/chromium/chrome/browser/search_engines/android/BUILD.gn
index 939d1d5b109..f78132f3736 100644
--- a/chromium/chrome/browser/search_engines/android/BUILD.gn
+++ b/chromium/chrome/browser/search_engines/android/BUILD.gn
@@ -16,6 +16,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/search_engines/SearchEnginePromoType.java",
"java/src/org/chromium/chrome/browser/search_engines/SogouPromoDialog.java",
"java/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceFactory.java",
+ "java/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogCoordinator.java",
"java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java",
"java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettings.java",
]
@@ -45,6 +46,8 @@ android_library("java") {
"//components/location/android:location_java",
"//components/search_engines/android:java",
"//components/version_info/android:version_constants_java",
+ "//content/public/android:content_java",
+ "//third_party/androidx:androidx_activity_activity_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_fragment_fragment_java",
"//ui/android:ui_full_java",
@@ -70,3 +73,21 @@ android_resources("java_resources") {
"//components/browser_ui/widget/android:java_resources",
]
}
+
+robolectric_library("junit") {
+ resources_package = "org.chromium.chrome.browser.search_engines"
+ sources = [ "junit/src/org/chromium/chrome/browser/search_engines/choice_screen/ChoiceDialogCoordinatorUnitTest.java" ]
+ deps = [
+ ":java",
+ ":java_resources",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//base:base_junit_test_support",
+ "//chrome/android:chrome_java",
+ "//chrome/browser/flags:java",
+ "//third_party/androidx:androidx_test_runner_java",
+ "//third_party/junit",
+ "//third_party/mockito:mockito_java",
+ "//ui/android:ui_java",
+ ]
+}
diff --git a/chromium/chrome/browser/segmentation_platform/BUILD.gn b/chromium/chrome/browser/segmentation_platform/BUILD.gn
index e9163bb55ee..d25f6ead77e 100644
--- a/chromium/chrome/browser/segmentation_platform/BUILD.gn
+++ b/chromium/chrome/browser/segmentation_platform/BUILD.gn
@@ -13,6 +13,7 @@ if (is_android) {
sources = [ "android/java/src/org/chromium/chrome/browser/segmentation_platform/SegmentationPlatformServiceFactory.java" ]
deps = [
+ "//base:base_java",
"//base:jni_java",
"//build/android:build_java",
"//chrome/browser/profiles/android:java",
diff --git a/chromium/chrome/browser/selection/android/BUILD.gn b/chromium/chrome/browser/selection/android/BUILD.gn
index 97b25204d33..6ae58a801de 100644
--- a/chromium/chrome/browser/selection/android/BUILD.gn
+++ b/chromium/chrome/browser/selection/android/BUILD.gn
@@ -37,6 +37,7 @@ android_library("javatests") {
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_activity_activity_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit:junit",
]
}
diff --git a/chromium/chrome/browser/settings/BUILD.gn b/chromium/chrome/browser/settings/BUILD.gn
index 23153ec96f6..ce5a047891f 100644
--- a/chromium/chrome/browser/settings/BUILD.gn
+++ b/chromium/chrome/browser/settings/BUILD.gn
@@ -21,9 +21,11 @@ android_library("java") {
"//components/favicon/android:java",
"//components/prefs/android:java",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_constraintlayout_constraintlayout_java",
"//third_party/androidx:androidx_fragment_fragment_java",
+ "//third_party/androidx:androidx_preference_preference_java",
"//url:gurl_java",
]
resources_package = "org.chromium.chrome.browser.settings"
@@ -40,6 +42,8 @@ android_library("test_support_java") {
"//chrome/browser/settings:java",
"//components/browser_ui/settings/android:java",
"//content/public/test/android:content_java_test_support",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_monitor_java",
diff --git a/chromium/chrome/browser/share/BUILD.gn b/chromium/chrome/browser/share/BUILD.gn
index 1df5d26f904..3c2e0367168 100644
--- a/chromium/chrome/browser/share/BUILD.gn
+++ b/chromium/chrome/browser/share/BUILD.gn
@@ -48,7 +48,7 @@ source_set("share") {
]
deps += [
":jni_headers",
- "//chrome/browser/share/android:jni_headers",
+ "//chrome/android:chrome_jni_headers",
"//components/history/core/browser:browser",
"//components/ukm/content:content",
"//ui/android",
diff --git a/chromium/chrome/browser/share/android/BUILD.gn b/chromium/chrome/browser/share/android/BUILD.gn
index 1af29f81626..4f4db71d2ac 100644
--- a/chromium/chrome/browser/share/android/BUILD.gn
+++ b/chromium/chrome/browser/share/android/BUILD.gn
@@ -7,7 +7,6 @@ import("//build/config/android/rules.gni")
android_resources("java_resources") {
sources = [
"java/res/drawable-ldrtl/text_icon.xml",
- "java/res/drawable/camera_img.xml",
"java/res/drawable/delete_icon.xml",
"java/res/drawable/edit_icon.xml",
"java/res/drawable/generic_file.xml",
@@ -21,30 +20,9 @@ android_resources("java_resources") {
"java/res/drawable/share_icon.xml",
"java/res/drawable/text_icon.xml",
"java/res/drawable/webnote.xml",
- "java/res/layout/qrcode_camera_error_layout.xml",
"java/res/layout/qrcode_dialog.xml",
- "java/res/layout/qrcode_open_settings_layout.xml",
- "java/res/layout/qrcode_permission_layout.xml",
"java/res/layout/qrcode_share_layout.xml",
"java/res/layout/screenshot_share_sheet.xml",
- "java/res/values-night/colors.xml",
- "java/res/values-sw600dp/dimens.xml",
- "java/res/values/colors.xml",
"java/res/values/dimens.xml",
]
}
-
-generate_jni("jni_headers") {
- sources = [
- "java/src/org/chromium/chrome/browser/share/BitmapDownloadRequest.java",
- "java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextBridge.java",
- "java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsTabService.java",
- "java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsTabServiceFactory.java",
- "java/src/org/chromium/chrome/browser/share/qrcode/QRCodeGenerationRequest.java",
- "java/src/org/chromium/chrome/browser/share/screenshot/EditorScreenshotTask.java",
- "java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java",
- "java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationManager.java",
- "java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java",
- "java/src/org/chromium/chrome/browser/share/send_tab_to_self/TargetDeviceInfo.java",
- ]
-}
diff --git a/chromium/chrome/browser/share/android/java_sources.gni b/chromium/chrome/browser/share/android/java_sources.gni
index 69688261c5c..7ad187289e7 100644
--- a/chromium/chrome/browser/share/android/java_sources.gni
+++ b/chromium/chrome/browser/share/android/java_sources.gni
@@ -34,13 +34,6 @@ share_java_sources = [
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialogTab.java",
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodePageAdapter.java",
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/TabLayoutPageListener.java",
- "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/scan_tab/CameraPreview.java",
- "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/scan_tab/CameraPreviewOverlay.java",
- "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/scan_tab/QrCodeScanCoordinator.java",
- "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/scan_tab/QrCodeScanMediator.java",
- "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/scan_tab/QrCodeScanView.java",
- "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/scan_tab/QrCodeScanViewBinder.java",
- "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/scan_tab/QrCodeScanViewProperties.java",
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/share_tab/QrCodeShareCoordinator.java",
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/share_tab/QrCodeShareMediator.java",
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/share_tab/QrCodeShareView.java",
diff --git a/chromium/chrome/browser/share/android/test_java_sources.gni b/chromium/chrome/browser/share/android/test_java_sources.gni
index 264cd1e7d14..776a88c2247 100644
--- a/chromium/chrome/browser/share/android/test_java_sources.gni
+++ b/chromium/chrome/browser/share/android/test_java_sources.gni
@@ -16,6 +16,7 @@ share_test_java_sources = [
]
share_unit_device_javatest_java_sources = [
+ "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialogTest.java",
"//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewTest.java",
"//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContentTest.java",
"//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilderTest.java",
diff --git a/chromium/chrome/browser/signin/account_consistency_mode_manager.cc b/chromium/chrome/browser/signin/account_consistency_mode_manager.cc
index a7853cc0140..c495783de02 100644
--- a/chromium/chrome/browser/signin/account_consistency_mode_manager.cc
+++ b/chromium/chrome/browser/signin/account_consistency_mode_manager.cc
@@ -191,9 +191,8 @@ AccountConsistencyModeManager::ComputeAccountConsistencyMethod(
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
- // Account consistency is unavailable on Managed Guest Sessions and Public
- // Sessions.
- if (profiles::IsPublicSession() || profile->IsGuestSession()) {
+ // Account consistency is unavailable on Guest and Managed Guest Sessions.
+ if (profiles::IsManagedGuestSession() || profile->IsGuestSession()) {
return AccountConsistencyMethod::kDisabled;
}
#endif
diff --git a/chromium/chrome/browser/signin/account_consistency_mode_manager_factory.cc b/chromium/chrome/browser/signin/account_consistency_mode_manager_factory.cc
index 8502353047a..ae3f335e0c1 100644
--- a/chromium/chrome/browser/signin/account_consistency_mode_manager_factory.cc
+++ b/chromium/chrome/browser/signin/account_consistency_mode_manager_factory.cc
@@ -29,12 +29,13 @@ AccountConsistencyModeManagerFactory::AccountConsistencyModeManagerFactory()
AccountConsistencyModeManagerFactory::~AccountConsistencyModeManagerFactory() =
default;
-KeyedService* AccountConsistencyModeManagerFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+AccountConsistencyModeManagerFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
DCHECK(!context->IsOffTheRecord());
Profile* profile = Profile::FromBrowserContext(context);
- return new AccountConsistencyModeManager(profile);
+ return std::make_unique<AccountConsistencyModeManager>(profile);
}
void AccountConsistencyModeManagerFactory::RegisterProfilePrefs(
diff --git a/chromium/chrome/browser/signin/account_consistency_mode_manager_factory.h b/chromium/chrome/browser/signin/account_consistency_mode_manager_factory.h
index 86ec3414bb7..4fe3035c717 100644
--- a/chromium/chrome/browser/signin/account_consistency_mode_manager_factory.h
+++ b/chromium/chrome/browser/signin/account_consistency_mode_manager_factory.h
@@ -23,7 +23,7 @@ class AccountConsistencyModeManagerFactory : public ProfileKeyedServiceFactory {
~AccountConsistencyModeManagerFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
void RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) override;
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.cc
index 29bc9d8a0a5..95acb2a330f 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.cc
@@ -5,14 +5,41 @@
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h"
#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_util.h"
#include "url/gurl.h"
BoundSessionCookieController::BoundSessionCookieController(
- const GURL& url,
- const std::string& cookie_name,
+ const bound_session_credentials::BoundSessionParams& bound_session_params,
+ const base::flat_set<std::string>& cookie_names,
Delegate* delegate)
- : url_(url), cookie_name_(cookie_name), delegate_(delegate) {}
+ : url_(bound_session_params.site()),
+ session_id_(bound_session_params.session_id()),
+ session_creation_time_(bound_session_credentials::TimestampToTime(
+ bound_session_params.creation_time())),
+ delegate_(delegate) {
+ CHECK(!url_.is_empty());
+ CHECK(!cookie_names.empty());
+ for (const std::string& cookie_name : cookie_names) {
+ bound_cookies_info_.insert({cookie_name, base::Time()});
+ }
+}
BoundSessionCookieController::~BoundSessionCookieController() = default;
void BoundSessionCookieController::Initialize() {}
+
+base::Time BoundSessionCookieController::min_cookie_expiration_time() {
+ CHECK(!bound_cookies_info_.empty());
+ return base::ranges::min_element(bound_cookies_info_, {},
+ [](const auto& bound_cookie_info) {
+ return bound_cookie_info.second;
+ })
+ ->second;
+}
+
+chrome::mojom::BoundSessionThrottlerParamsPtr
+BoundSessionCookieController::bound_session_throttler_params() {
+ return chrome::mojom::BoundSessionThrottlerParams::New(
+ url().host(), url().path(), min_cookie_expiration_time());
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h
index faab67f3abb..4da37e77235 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h
@@ -5,9 +5,12 @@
#ifndef CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_COOKIE_CONTROLLER_H_
#define CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_COOKIE_CONTROLLER_H_
+#include "base/containers/flat_map.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+#include "chrome/common/renderer_configuration.mojom.h"
#include "url/gurl.h"
// This class is responsible for tracking a single bound session cookie:
@@ -34,15 +37,16 @@ class BoundSessionCookieController {
// be deleted after this call.
virtual void TerminateSession() = 0;
- // Called when the cookie tracked in this controller has a change in its
- // expiration date. Cookie deletion is considered as a change in the
- // expiration date to the null time.
- virtual void OnCookieExpirationDateChanged() = 0;
+ // Called when the bound session parameters change, for example the minimum
+ // cookie expiration date changes. Cookie deletion is considered as a change
+ // in the expiration date to the null time.
+ virtual void OnBoundSessionThrottlerParamsChanged() = 0;
};
- BoundSessionCookieController(const GURL& url,
- const std::string& cookie_name,
- Delegate* delegate);
+ BoundSessionCookieController(
+ const bound_session_credentials::BoundSessionParams& bound_session_params,
+ const base::flat_set<std::string>& cookie_names,
+ Delegate* delegate);
virtual ~BoundSessionCookieController();
@@ -56,13 +60,22 @@ class BoundSessionCookieController {
base::OnceClosure resume_blocked_request) = 0;
const GURL& url() const { return url_; }
- const std::string& cookie_name() const { return cookie_name_; }
- base::Time cookie_expiration_time() { return cookie_expiration_time_; }
+ const std::string& session_id() const { return session_id_; }
+ base::Time session_creation_time() const { return session_creation_time_; }
+ base::Time min_cookie_expiration_time();
+ chrome::mojom::BoundSessionThrottlerParamsPtr
+ bound_session_throttler_params();
protected:
const GURL url_;
- const std::string cookie_name_;
- base::Time cookie_expiration_time_;
+ const std::string session_id_;
+ const base::Time session_creation_time_;
+ // Map from cookie name to cookie expiration time, it is expected to have two
+ // elements the 1P and 3P cookies.
+ // Cookie expiration time is reduced by threshold to guarantee cookie will be
+ // fresh when cookies are added to the request, as URL Loader throttle(s)
+ // attached to the request may decide to defer it.
+ base::flat_map<std::string, base::Time> bound_cookies_info_;
raw_ptr<Delegate> delegate_;
};
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc
index 157144377ac..0f79669fa5d 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.cc
@@ -6,23 +6,45 @@
#include <memory>
-#include "base/debug/dump_without_crashing.h"
+#include "base/check.h"
#include "base/functional/bind.h"
#include "base/time/time.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h"
-#include "components/signin/public/base/signin_client.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_status_code.h"
+#include "chrome/browser/signin/bound_session_credentials/session_binding_helper.h"
+#include "chrome/browser/signin/wait_for_network_callback_helper_chrome.h"
+#include "content/public/browser/storage_partition.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
+
+namespace {
+using Result = BoundSessionRefreshCookieFetcher::Result;
+}
BoundSessionCookieControllerImpl::BoundSessionCookieControllerImpl(
- SigninClient* client,
- const GURL& url,
- const std::string& cookie_name,
+ unexportable_keys::UnexportableKeyService& key_service,
+ content::StoragePartition* storage_partition,
+ network::NetworkConnectionTracker* network_connection_tracker,
+ const bound_session_credentials::BoundSessionParams& bound_session_params,
+ const base::flat_set<std::string>& cookie_names,
Delegate* delegate)
- : BoundSessionCookieController(url, cookie_name, delegate),
- client_(client) {}
+ : BoundSessionCookieController(bound_session_params,
+ cookie_names,
+ delegate),
+ key_service_(key_service),
+ storage_partition_(storage_partition),
+ network_connection_tracker_(network_connection_tracker),
+ wait_for_network_callback_helper_(
+ std::make_unique<WaitForNetworkCallbackHelperChrome>()) {
+ CHECK(!bound_session_params.wrapped_key().empty());
+ base::span<const uint8_t> wrapped_key =
+ base::as_bytes(base::make_span(bound_session_params.wrapped_key()));
+ session_binding_helper_ = std::make_unique<SessionBindingHelper>(
+ key_service_.get(), wrapped_key, session_id_);
+ // Preemptively load the binding key to speed up the generation of binding
+ // key assertion.
+ session_binding_helper_->MaybeLoadBindingKey();
+}
BoundSessionCookieControllerImpl::~BoundSessionCookieControllerImpl() {
// On shutdown or session termination, resume blocked requests if any.
@@ -30,19 +52,34 @@ BoundSessionCookieControllerImpl::~BoundSessionCookieControllerImpl() {
}
void BoundSessionCookieControllerImpl::Initialize() {
- // `base::Unretained(this)` is safe because `this` owns
- // `cookie_observer_`.
- cookie_observer_ = std::make_unique<BoundSessionCookieObserver>(
- client_, url_, cookie_name_,
- base::BindRepeating(
- &BoundSessionCookieControllerImpl::SetCookieExpirationTimeAndNotify,
- base::Unretained(this)));
+ network_connection_observer_.Observe(network_connection_tracker_);
+ CreateBoundCookiesObservers();
MaybeRefreshCookie();
}
+void BoundSessionCookieControllerImpl::OnConnectionChanged(
+ network::mojom::ConnectionType type) {
+ if (type == network::mojom::ConnectionType::CONNECTION_NONE) {
+ // Let network requests fail now while there is no internet connection,
+ // instead of holding them up until the network is back or timeout occurs.
+ // The network could come back shortly before the timeout which would result
+ // in requests being released without a valid cookie.
+ ResumeBlockedRequests();
+ }
+}
+
+bool BoundSessionCookieControllerImpl::IsConnectionTypeAvailableAndOffline() {
+ network::mojom::ConnectionType type;
+ return network_connection_tracker_->GetConnectionType(
+ &type, base::BindOnce(
+ &BoundSessionCookieControllerImpl::OnConnectionChanged,
+ weak_ptr_factory_.GetWeakPtr())) &&
+ type == network::mojom::ConnectionType::CONNECTION_NONE;
+}
+
void BoundSessionCookieControllerImpl::OnRequestBlockedOnCookie(
base::OnceClosure resume_blocked_request) {
- if (IsCookieFresh()) {
+ if (AreAllCookiesFresh()) {
// Cookie is fresh.
std::move(resume_blocked_request).Run();
return;
@@ -50,36 +87,90 @@ void BoundSessionCookieControllerImpl::OnRequestBlockedOnCookie(
resume_blocked_requests_.push_back(std::move(resume_blocked_request));
MaybeRefreshCookie();
+
+ if (IsConnectionTypeAvailableAndOffline()) {
+ // See the comment in `OnConnectionChanged()` for explanation.
+ ResumeBlockedRequests();
+ return;
+ }
+
+ if (!resume_blocked_requests_timer_.IsRunning() &&
+ !resume_blocked_requests_.empty()) {
+ // Ensure all blocked requests are released after a timeout.
+ // `base::Unretained(this)` is safe because `this` owns
+ // `resume_blocked_requests_timer_`.
+ const base::TimeDelta kResumeBlockedRequestTimeout = base::Seconds(20);
+ resume_blocked_requests_timer_.Start(
+ FROM_HERE, kResumeBlockedRequestTimeout,
+ base::BindRepeating(
+ &BoundSessionCookieControllerImpl::OnResumeBlockedRequestsTimeout,
+ base::Unretained(this)));
+ }
}
void BoundSessionCookieControllerImpl::SetCookieExpirationTimeAndNotify(
+ const std::string& cookie_name,
base::Time expiration_time) {
- if (cookie_expiration_time_ == expiration_time) {
+ const base::TimeDelta kCookieExpirationThreshold = base::Seconds(15);
+ if (!expiration_time.is_null()) {
+ expiration_time -= kCookieExpirationThreshold;
+ }
+
+ auto it = bound_cookies_info_.find(cookie_name);
+ CHECK(it != bound_cookies_info_.end());
+ if (it->second == expiration_time) {
return;
}
- // TODO(b/263264391): Subtract a safety margin (e.g 2 seconds) from the cookie
- // expiration time.
- cookie_expiration_time_ = expiration_time;
- if (IsCookieFresh()) {
+ base::Time old_min_expiration_time = min_cookie_expiration_time();
+ it->second = expiration_time;
+ if (AreAllCookiesFresh()) {
ResumeBlockedRequests();
}
- delegate_->OnCookieExpirationDateChanged();
+
+ if (min_cookie_expiration_time() != old_min_expiration_time) {
+ delegate_->OnBoundSessionThrottlerParamsChanged();
+ MaybeScheduleCookieRotation();
+ }
+}
+
+void BoundSessionCookieControllerImpl::CreateBoundCookiesObservers() {
+ for (const auto& [cookie_name, _] : bound_cookies_info_) {
+ // `base::Unretained(this)` is safe because `this` owns
+ // `cookie_observer_`.
+ std::unique_ptr<BoundSessionCookieObserver> cookie_observer =
+ std::make_unique<BoundSessionCookieObserver>(
+ storage_partition_, url_, cookie_name,
+ base::BindRepeating(&BoundSessionCookieControllerImpl::
+ SetCookieExpirationTimeAndNotify,
+ base::Unretained(this)));
+ bound_cookies_observers_.push_back(std::move(cookie_observer));
+ }
}
std::unique_ptr<BoundSessionRefreshCookieFetcher>
BoundSessionCookieControllerImpl::CreateRefreshCookieFetcher() const {
+ base::flat_set<std::string> cookie_names;
+ for (const auto& [cookie_name, _] : bound_cookies_info_) {
+ cookie_names.insert(cookie_name);
+ }
+
return refresh_cookie_fetcher_factory_for_testing_.is_null()
- ? std::make_unique<BoundSessionRefreshCookieFetcherImpl>(client_)
- : refresh_cookie_fetcher_factory_for_testing_.Run(client_, url_,
- cookie_name_);
+ ? std::make_unique<BoundSessionRefreshCookieFetcherImpl>(
+ storage_partition_->GetURLLoaderFactoryForBrowserProcess(),
+ *wait_for_network_callback_helper_, *session_binding_helper_,
+ url_, std::move(cookie_names))
+ : refresh_cookie_fetcher_factory_for_testing_.Run(
+ storage_partition_->GetCookieManagerForBrowserProcess(),
+ url_, std::move(cookie_names));
}
-bool BoundSessionCookieControllerImpl::IsCookieFresh() {
- return cookie_expiration_time() > base::Time::Now();
+bool BoundSessionCookieControllerImpl::AreAllCookiesFresh() {
+ return min_cookie_expiration_time() > base::Time::Now();
}
void BoundSessionCookieControllerImpl::MaybeRefreshCookie() {
+ preemptive_cookie_refresh_timer_.Stop();
if (refresh_cookie_fetcher_) {
return;
}
@@ -102,17 +193,44 @@ void BoundSessionCookieControllerImpl::OnCookieRefreshFetched(
// Persistent errors result in session termination.
// Transient errors have no impact on future requests.
- if (result ==
- BoundSessionRefreshCookieFetcher::Result::kServerPersistentError) {
+
+ if (BoundSessionRefreshCookieFetcher::IsPersistentError(result)) {
delegate_->TerminateSession();
// `this` should be deleted.
}
}
+void BoundSessionCookieControllerImpl::MaybeScheduleCookieRotation() {
+ const base::TimeDelta kCookieRefreshInterval = base::Minutes(2);
+ base::TimeDelta refresh_in =
+ min_cookie_expiration_time() - base::Time::Now() - kCookieRefreshInterval;
+ if (!refresh_in.is_positive()) {
+ MaybeRefreshCookie();
+ return;
+ }
+
+ // If a refresh task is already scheduled, this will reschedule it.
+ // `base::Unretained(this)` is safe because `this` owns
+ // `cookie_rotation_timer_`.
+ preemptive_cookie_refresh_timer_.Start(
+ FROM_HERE, refresh_in,
+ base::BindRepeating(&BoundSessionCookieControllerImpl::MaybeRefreshCookie,
+ base::Unretained(this)));
+}
+
void BoundSessionCookieControllerImpl::ResumeBlockedRequests() {
+ resume_blocked_requests_timer_.Stop();
std::vector<base::OnceClosure> callbacks;
std::swap(callbacks, resume_blocked_requests_);
for (auto& callback : callbacks) {
std::move(callback).Run();
}
}
+
+void BoundSessionCookieControllerImpl::OnResumeBlockedRequestsTimeout() {
+ // TODO(b/292511796): Add a histogram.
+ // Reset the fetcher, it has been taking at least
+ // kResumeBlockedRequestTimeout. New requests will trigger a new fetch.
+ refresh_cookie_fetcher_.reset();
+ ResumeBlockedRequests();
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h
index 9efa8c1047e..752a05bcf61 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h
@@ -8,30 +8,40 @@
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h"
#include <memory>
-#include "base/functional/bind.h"
+
#include "base/functional/callback_forward.h"
+#include "base/scoped_observation.h"
#include "base/time/time.h"
-#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h"
+#include "base/timer/timer.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
+#include "content/public/browser/storage_partition.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "url/gurl.h"
-class SigninClient;
-class BoundSessionCookieFetcher;
-class BoundSessionCookieObserver;
+namespace unexportable_keys {
+class UnexportableKeyService;
+} // namespace unexportable_keys
-class BoundSessionCookieControllerImpl : public BoundSessionCookieController {
- public:
- BoundSessionCookieControllerImpl(SigninClient* client,
- const GURL& url,
- const std::string& cookie_name,
- Delegate* delegate);
+namespace content {
+class StoragePartition;
+}
- void Initialize() override;
+class BoundSessionCookieObserver;
+class SessionBindingHelper;
+class WaitForNetworkCallbackHelper;
- void OnRequestBlockedOnCookie(
- base::OnceClosure resume_blocked_request) override;
+class BoundSessionCookieControllerImpl
+ : public BoundSessionCookieController,
+ public network::NetworkConnectionTracker::NetworkConnectionObserver {
+ public:
+ BoundSessionCookieControllerImpl(
+ unexportable_keys::UnexportableKeyService& key_service,
+ content::StoragePartition* storage_partition,
+ network::NetworkConnectionTracker* network_connection_tracker,
+ const bound_session_credentials::BoundSessionParams& bound_session_params,
+ const base::flat_set<std::string>& cookie_names,
+ Delegate* delegate);
~BoundSessionCookieControllerImpl() override;
@@ -40,6 +50,15 @@ class BoundSessionCookieControllerImpl : public BoundSessionCookieController {
BoundSessionCookieControllerImpl& operator=(
const BoundSessionCookieControllerImpl&) = delete;
+ // BoundSessionCookieController:
+ void Initialize() override;
+
+ void OnRequestBlockedOnCookie(
+ base::OnceClosure resume_blocked_request) override;
+
+ // network::NetworkConnectionTracker::NetworkConnectionObserver:
+ void OnConnectionChanged(network::mojom::ConnectionType type) override;
+
private:
friend class BoundSessionCookieControllerImplTest;
@@ -47,18 +66,24 @@ class BoundSessionCookieControllerImpl : public BoundSessionCookieController {
// `BoundSessionRefreshCookieFetcher`.
using RefreshCookieFetcherFactoryForTesting =
base::RepeatingCallback<std::unique_ptr<BoundSessionRefreshCookieFetcher>(
- SigninClient* client,
+ network::mojom::CookieManager* cookie_manager,
const GURL& url,
- const std::string& cookie_name)>;
+ base::flat_set<std::string> cookie_names)>;
+
+ bool IsConnectionTypeAvailableAndOffline();
std::unique_ptr<BoundSessionRefreshCookieFetcher> CreateRefreshCookieFetcher()
const;
+ void CreateBoundCookiesObservers();
- bool IsCookieFresh();
+ bool AreAllCookiesFresh();
void MaybeRefreshCookie();
- void SetCookieExpirationTimeAndNotify(base::Time expiration_time);
+ void SetCookieExpirationTimeAndNotify(const std::string& cookie_name,
+ base::Time expiration_time);
void OnCookieRefreshFetched(BoundSessionRefreshCookieFetcher::Result result);
+ void MaybeScheduleCookieRotation();
void ResumeBlockedRequests();
+ void OnResumeBlockedRequestsTimeout();
void set_refresh_cookie_fetcher_factory_for_testing(
RefreshCookieFetcherFactoryForTesting
@@ -67,13 +92,33 @@ class BoundSessionCookieControllerImpl : public BoundSessionCookieController {
refresh_cookie_fetcher_factory_for_testing;
}
- const raw_ptr<SigninClient> client_;
- std::unique_ptr<BoundSessionCookieObserver> cookie_observer_;
+ const raw_ref<unexportable_keys::UnexportableKeyService> key_service_;
+ const raw_ptr<content::StoragePartition> storage_partition_;
+ const raw_ptr<network::NetworkConnectionTracker> network_connection_tracker_;
+ std::vector<std::unique_ptr<BoundSessionCookieObserver>>
+ bound_cookies_observers_;
+
+ base::ScopedObservation<
+ network::NetworkConnectionTracker,
+ network::NetworkConnectionTracker::NetworkConnectionObserver>
+ network_connection_observer_{this};
+
+ std::unique_ptr<WaitForNetworkCallbackHelper>
+ wait_for_network_callback_helper_;
+ std::unique_ptr<SessionBindingHelper> session_binding_helper_;
std::unique_ptr<BoundSessionRefreshCookieFetcher> refresh_cookie_fetcher_;
+
std::vector<base::OnceClosure> resume_blocked_requests_;
+ // Used to schedule preemptive cookie refresh.
+ base::OneShotTimer preemptive_cookie_refresh_timer_;
+ // Used to release blocked requests after a timeout.
+ base::OneShotTimer resume_blocked_requests_timer_;
RefreshCookieFetcherFactoryForTesting
refresh_cookie_fetcher_factory_for_testing_;
+
+ base::WeakPtrFactory<BoundSessionCookieControllerImpl> weak_ptr_factory_{
+ this};
};
#endif // CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_COOKIE_CONTROLLER_IMPL_H_
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl_unittest.cc
index 256d788bdbd..c464ba89b78 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl_unittest.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl_unittest.cc
@@ -6,25 +6,42 @@
#include <memory>
+#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
-#include "base/types/expected.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h"
#include "chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.h"
-#include "components/signin/public/base/test_signin_client.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "google_apis/gaia/gaia_urls.h"
+#include "chrome/browser/signin/bound_session_credentials/session_binding_helper.h"
+#include "components/unexportable_keys/service_error.h"
+#include "components/unexportable_keys/unexportable_key_id.h"
+#include "components/unexportable_keys/unexportable_key_loader.h"
+#include "components/unexportable_keys/unexportable_key_service_impl.h"
+#include "components/unexportable_keys/unexportable_key_task_manager.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/test/test_storage_partition.h"
+#include "crypto/scoped_mock_unexportable_key_provider.h"
+#include "crypto/signature_verifier.h"
#include "net/cookies/canonical_cookie.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
+#include "services/network/test/test_network_connection_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+using unexportable_keys::ServiceErrorOr;
+using unexportable_keys::UnexportableKeyId;
+
namespace {
-constexpr char kSIDTSCookieName[] = "__Secure-1PSIDTS";
+constexpr char k1PSIDTSCookieName[] = "__Secure-1PSIDTS";
+constexpr char k3PSIDTSCookieName[] = "__Secure-3PSIDTS";
+
+const base::TimeDelta kCookieExpirationThreshold = base::Seconds(15);
+const base::TimeDelta kCookieRefreshInterval = base::Minutes(2);
+const base::TimeDelta kResumeBlockedRequestTimeout = base::Seconds(20);
base::Time GetTimeInTenMinutes() {
return base::Time::Now() + base::Minutes(10);
@@ -35,13 +52,29 @@ class BoundSessionCookieControllerImplTest
: public testing::Test,
public BoundSessionCookieController::Delegate {
public:
- BoundSessionCookieControllerImplTest() : signin_client_(&prefs_) {
- signin_client_.set_cookie_manager(
- std::make_unique<BoundSessionTestCookieManager>());
+ BoundSessionCookieControllerImplTest()
+ : unexportable_key_service_(unexportable_key_task_manager_),
+ key_id_(GenerateNewKey()) {
+
+ std::vector<uint8_t> wrapped_key = GetWrappedKey(key_id_);
+ bound_session_credentials::BoundSessionParams bound_session_params;
+ bound_session_params.set_site("https://google.com");
+ bound_session_params.set_session_id("test_session_id");
+ bound_session_params.set_wrapped_key(
+ std::string(wrapped_key.begin(), wrapped_key.end()));
+
+ storage_partition_.set_cookie_manager_for_browser_process(&cookie_manager_);
+
+ SetUpNetworkConnection(true,
+ network::mojom::ConnectionType::CONNECTION_WIFI);
+
bound_session_cookie_controller_ =
std::make_unique<BoundSessionCookieControllerImpl>(
- &signin_client_, GaiaUrls::GetInstance()->secure_google_url(),
- kSIDTSCookieName, this);
+ unexportable_key_service_, &storage_partition_,
+ content::GetNetworkConnectionTracker(), bound_session_params,
+ base::flat_set<std::string>(
+ {k1PSIDTSCookieName, k3PSIDTSCookieName}),
+ this);
bound_session_cookie_controller_
->set_refresh_cookie_fetcher_factory_for_testing(
@@ -53,24 +86,46 @@ class BoundSessionCookieControllerImplTest
~BoundSessionCookieControllerImplTest() override = default;
+ UnexportableKeyId GenerateNewKey() {
+ base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> generate_future;
+ unexportable_key_service_.GenerateSigningKeySlowlyAsync(
+ base::span<const crypto::SignatureVerifier::SignatureAlgorithm>(
+ {crypto::SignatureVerifier::ECDSA_SHA256}),
+ unexportable_keys::BackgroundTaskPriority::kUserBlocking,
+ generate_future.GetCallback());
+ ServiceErrorOr<unexportable_keys::UnexportableKeyId> key_id =
+ generate_future.Get();
+ CHECK(key_id.has_value());
+ return *key_id;
+ }
+
+ std::vector<uint8_t> GetWrappedKey(const UnexportableKeyId& key_id) {
+ ServiceErrorOr<std::vector<uint8_t>> wrapped_key =
+ unexportable_key_service_.GetWrappedKey(key_id);
+ CHECK(wrapped_key.has_value());
+ return *wrapped_key;
+ }
+
std::unique_ptr<BoundSessionRefreshCookieFetcher>
- CreateBoundSessionRefreshCookieFetcher(SigninClient* client,
- const GURL& url,
- const std::string& cookie_name) {
+ CreateBoundSessionRefreshCookieFetcher(
+ network::mojom::CookieManager* cookie_manager,
+ const GURL& url,
+ base::flat_set<std::string> cookie_names) {
// `SimulateCompleteRefreshRequest()` must be called for the
- // refresh request to complete.
- auto fetcher = std::make_unique<FakeBoundSessionRefreshCookieFetcher>(
- client, url, cookie_name);
- cookie_fetcher_ = fetcher.get();
- return fetcher;
+ return std::make_unique<FakeBoundSessionRefreshCookieFetcher>(
+ cookie_manager, url, std::move(cookie_names));
}
void MaybeRefreshCookie() {
bound_session_cookie_controller_->MaybeRefreshCookie();
}
+ bool AreAllCookiesFresh() {
+ return bound_session_cookie_controller_->AreAllCookiesFresh();
+ }
+
bool CompletePendingRefreshRequestIfAny() {
- if (!cookie_fetcher_) {
+ if (!cookie_fetcher()) {
return false;
}
SimulateCompleteRefreshRequest(
@@ -83,102 +138,161 @@ class BoundSessionCookieControllerImplTest
void SimulateCompleteRefreshRequest(
BoundSessionRefreshCookieFetcher::Result result,
absl::optional<base::Time> cookie_expiration) {
- EXPECT_TRUE(cookie_fetcher_);
- cookie_fetcher_->SimulateCompleteRefreshRequest(result, cookie_expiration);
- // It is not safe to access the `cookie_fetcher_` after the request has been
- // completed. The controller will destroy the fetcher upon completion.
- cookie_fetcher_ = nullptr;
+ ASSERT_TRUE(cookie_fetcher());
+ FakeBoundSessionRefreshCookieFetcher* fetcher =
+ static_cast<FakeBoundSessionRefreshCookieFetcher*>(cookie_fetcher());
+ fetcher->SimulateCompleteRefreshRequest(result, cookie_expiration);
}
- void SimulateCookieChange(absl::optional<base::Time> cookie_expiration) {
+ void SimulateCookieChange(const std::string& cookie_name,
+ absl::optional<base::Time> cookie_expiration) {
net::CanonicalCookie cookie = BoundSessionTestCookieManager::CreateCookie(
- bound_session_cookie_controller()->url(),
- bound_session_cookie_controller()->cookie_name(), cookie_expiration);
- cookie_observer()->OnCookieChange(net::CookieChangeInfo(
- cookie, net::CookieAccessResult(), net::CookieChangeCause::INSERTED));
+ bound_session_cookie_controller()->url(), cookie_name,
+ cookie_expiration);
+ cookie_observer(cookie_name)
+ ->OnCookieChange(
+ net::CookieChangeInfo(cookie, net::CookieAccessResult(),
+ net::CookieChangeCause::INSERTED));
}
base::test::TaskEnvironment* task_environment() { return &task_environment_; }
- void OnCookieExpirationDateChanged() override {
- on_cookie_expiration_date_changed_call_count_++;
+ void OnBoundSessionThrottlerParamsChanged() override {
+ on_bound_session_throttler_params_changed_call_count_++;
}
void TerminateSession() override { on_terminate_session_called_ = true; }
- void SetExpirationTimeAndNotify(const base::Time& expiration_time) {
+ void SetExpirationTimeAndNotify(const std::string& cookie_name,
+ const base::Time& expiration_time) {
bound_session_cookie_controller_->SetCookieExpirationTimeAndNotify(
- expiration_time);
+ cookie_name, expiration_time);
}
BoundSessionCookieControllerImpl* bound_session_cookie_controller() {
return bound_session_cookie_controller_.get();
}
- FakeBoundSessionRefreshCookieFetcher* cookie_fetcher() {
- return cookie_fetcher_;
+ BoundSessionRefreshCookieFetcher* cookie_fetcher() {
+ return bound_session_cookie_controller_->refresh_cookie_fetcher_.get();
+ }
+
+ std::vector<std::unique_ptr<BoundSessionCookieObserver>>*
+ bound_cookies_observers() {
+ return &bound_session_cookie_controller()->bound_cookies_observers_;
+ }
+
+ BoundSessionCookieObserver* cookie_observer(const std::string& cookie_name) {
+ for (auto& observer : *bound_cookies_observers()) {
+ if (observer->cookie_name_ == cookie_name) {
+ return observer.get();
+ }
+ }
+ NOTREACHED_NORETURN() << "No observer found for " << cookie_name;
+ }
+
+ base::Time cookie_expiration_time(const std::string& cookie_name) {
+ auto& bound_cookies_info =
+ bound_session_cookie_controller()->bound_cookies_info_;
+ auto it = bound_cookies_info.find(cookie_name);
+ CHECK(it != bound_cookies_info.end())
+ << "No cookie found for " << cookie_name;
+ return it->second;
+ }
+
+ base::OneShotTimer* preemptive_cookie_refresh_timer() {
+ return &bound_session_cookie_controller()->preemptive_cookie_refresh_timer_;
}
- BoundSessionCookieObserver* cookie_observer() {
- return bound_session_cookie_controller()->cookie_observer_.get();
+ base::OneShotTimer* resume_blocked_requests_timer() {
+ return &bound_session_cookie_controller()->resume_blocked_requests_timer_;
}
- size_t on_cookie_expiration_date_changed_call_count() {
- return on_cookie_expiration_date_changed_call_count_;
+ unexportable_keys::UnexportableKeyLoader* key_loader() {
+ return bound_session_cookie_controller()
+ ->session_binding_helper_->key_loader_.get();
+ }
+
+ const UnexportableKeyId& key_id() { return key_id_; }
+
+ size_t on_bound_session_throttler_params_changed_call_count() {
+ return on_bound_session_throttler_params_changed_call_count_;
}
bool on_cookie_refresh_persistent_failure_called() {
return on_terminate_session_called_;
}
- void ResetOnCookieExpirationDateChangedCallCount() {
- on_cookie_expiration_date_changed_call_count_ = 0;
+ void ResetOnBoundSessionThrottlerParamsChangedCallCount() {
+ on_bound_session_throttler_params_changed_call_count_ = 0;
}
void ResetBoundSessionCookieController() {
bound_session_cookie_controller_.reset();
}
+ void SetUpNetworkConnection(bool respond_synchronously,
+ network::mojom::ConnectionType connection_type) {
+ network::TestNetworkConnectionTracker* tracker =
+ network::TestNetworkConnectionTracker::GetInstance();
+ tracker->SetRespondSynchronously(respond_synchronously);
+ tracker->SetConnectionType(connection_type);
+ }
+
+ void SetConnectionType(network::mojom::ConnectionType connection_type) {
+ network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
+ connection_type);
+ }
+
+ bool IsConnectionTypeAvailableAndOffline() {
+ CHECK(bound_session_cookie_controller_);
+ return bound_session_cookie_controller_
+ ->IsConnectionTypeAvailableAndOffline();
+ }
+
private:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
- sync_preferences::TestingPrefServiceSyncable prefs_;
- TestSigninClient signin_client_;
+ crypto::ScopedMockUnexportableKeyProvider scoped_key_provider_;
+ unexportable_keys::UnexportableKeyTaskManager unexportable_key_task_manager_;
+ unexportable_keys::UnexportableKeyServiceImpl unexportable_key_service_;
+ BoundSessionTestCookieManager cookie_manager_;
+ content::TestStoragePartition storage_partition_;
+ UnexportableKeyId key_id_;
std::unique_ptr<BoundSessionCookieControllerImpl>
bound_session_cookie_controller_;
- raw_ptr<FakeBoundSessionRefreshCookieFetcher, DanglingUntriaged>
- cookie_fetcher_ = nullptr;
- size_t on_cookie_expiration_date_changed_call_count_ = 0;
+ size_t on_bound_session_throttler_params_changed_call_count_ = 0;
bool on_terminate_session_called_ = false;
};
-TEST_F(BoundSessionCookieControllerImplTest, CookieRefreshOnStartup) {
- EXPECT_TRUE(CompletePendingRefreshRequestIfAny());
- EXPECT_EQ(on_cookie_expiration_date_changed_call_count(), 1u);
- EXPECT_EQ(bound_session_cookie_controller()->cookie_expiration_time(),
- GetTimeInTenMinutes());
+TEST_F(BoundSessionCookieControllerImplTest, KeyLoadedOnStartup) {
+ EXPECT_NE(key_loader()->GetStateForTesting(),
+ unexportable_keys::UnexportableKeyLoader::State::kNotStarted);
+ base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> future;
+ key_loader()->InvokeCallbackAfterKeyLoaded(future.GetCallback());
+ EXPECT_EQ(*future.Get(), key_id());
}
-TEST_F(BoundSessionCookieControllerImplTest,
- OnRefreshCookieFailedDoesNotUpdateCookieExpirationTime) {
- CompletePendingRefreshRequestIfAny();
- ResetOnCookieExpirationDateChangedCallCount();
- base::Time cookie_expiration =
- bound_session_cookie_controller()->cookie_expiration_time();
+TEST_F(BoundSessionCookieControllerImplTest, TwoCookieObserversCreated) {
+ EXPECT_EQ(bound_cookies_observers()->size(), 2u);
+ CHECK(cookie_observer(k1PSIDTSCookieName));
+ CHECK(cookie_observer(k3PSIDTSCookieName));
+}
- MaybeRefreshCookie();
- SimulateCompleteRefreshRequest(
- BoundSessionRefreshCookieFetcher::Result::kServerTransientError,
- absl::nullopt);
- EXPECT_EQ(on_cookie_expiration_date_changed_call_count(), 0u);
- EXPECT_EQ(bound_session_cookie_controller()->cookie_expiration_time(),
- cookie_expiration);
+TEST_F(BoundSessionCookieControllerImplTest, CookieRefreshOnStartup) {
+ EXPECT_TRUE(CompletePendingRefreshRequestIfAny());
+ EXPECT_EQ(on_bound_session_throttler_params_changed_call_count(), 1u);
+ EXPECT_EQ(cookie_expiration_time(k1PSIDTSCookieName),
+ GetTimeInTenMinutes() - kCookieExpirationThreshold);
+ EXPECT_EQ(cookie_expiration_time(k3PSIDTSCookieName),
+ GetTimeInTenMinutes() - kCookieExpirationThreshold);
+ EXPECT_TRUE(AreAllCookiesFresh());
}
TEST_F(BoundSessionCookieControllerImplTest,
MaybeRefreshCookieMultipleRequests) {
CompletePendingRefreshRequestIfAny();
- ResetOnCookieExpirationDateChangedCallCount();
+ ResetOnBoundSessionThrottlerParamsChangedCallCount();
EXPECT_FALSE(cookie_fetcher());
MaybeRefreshCookie();
@@ -192,40 +306,70 @@ TEST_F(BoundSessionCookieControllerImplTest,
}
TEST_F(BoundSessionCookieControllerImplTest,
- NotifiesOnlyIfCookieExpiryDateChanged) {
+ NotifiesOnlyIfMinimumCookieExpirationDateChanged) {
CompletePendingRefreshRequestIfAny();
- ResetOnCookieExpirationDateChangedCallCount();
-
- // Update with the same date
- base::Time cookie_expiration =
- bound_session_cookie_controller()->cookie_expiration_time();
- SetExpirationTimeAndNotify(cookie_expiration);
- EXPECT_EQ(on_cookie_expiration_date_changed_call_count(), 0u);
-
- // Update with null time should trigger a notification.
- SetExpirationTimeAndNotify(base::Time());
- EXPECT_EQ(on_cookie_expiration_date_changed_call_count(), 1u);
- EXPECT_EQ(bound_session_cookie_controller()->cookie_expiration_time(),
+ ResetOnBoundSessionThrottlerParamsChangedCallCount();
+
+ // Update with the same date.
+ SetExpirationTimeAndNotify(
+ k1PSIDTSCookieName,
+ cookie_expiration_time(k1PSIDTSCookieName) + kCookieExpirationThreshold);
+ EXPECT_EQ(on_bound_session_throttler_params_changed_call_count(), 0u);
+
+ // Update with null time should change the minimum expiration date and
+ // trigger a notification.
+ SetExpirationTimeAndNotify(k1PSIDTSCookieName, base::Time());
+ EXPECT_EQ(on_bound_session_throttler_params_changed_call_count(), 1u);
+ EXPECT_EQ(cookie_expiration_time(k1PSIDTSCookieName), base::Time());
+ EXPECT_EQ(bound_session_cookie_controller()->min_cookie_expiration_time(),
base::Time());
}
TEST_F(BoundSessionCookieControllerImplTest, CookieChange) {
CompletePendingRefreshRequestIfAny();
- ResetOnCookieExpirationDateChangedCallCount();
+ ResetOnBoundSessionThrottlerParamsChangedCallCount();
+ task_environment()->FastForwardBy(base::Minutes(2));
- // Simulate cookie change.
BoundSessionCookieController* controller = bound_session_cookie_controller();
- SimulateCookieChange(base::Time::Now());
- EXPECT_EQ(on_cookie_expiration_date_changed_call_count(), 1u);
- EXPECT_EQ(controller->cookie_expiration_time(), base::Time::Now());
+ base::Time expiration_time_1PSIDTS =
+ cookie_expiration_time(k1PSIDTSCookieName);
+ base::Time expiration_time_3PSIDTS =
+ cookie_expiration_time(k3PSIDTSCookieName);
+ base::Time minimum_expiration_time = controller->min_cookie_expiration_time();
+ EXPECT_EQ(expiration_time_1PSIDTS, minimum_expiration_time);
+ EXPECT_EQ(expiration_time_1PSIDTS, expiration_time_3PSIDTS);
+
+ // Simulate cookie change of 1st cookie.
+ SimulateCookieChange(k1PSIDTSCookieName, GetTimeInTenMinutes());
+ expiration_time_1PSIDTS = cookie_expiration_time(k1PSIDTSCookieName);
+ EXPECT_EQ(expiration_time_1PSIDTS,
+ GetTimeInTenMinutes() - kCookieExpirationThreshold);
+ // The other cookie expiration time remains unchanged.
+ EXPECT_EQ(cookie_expiration_time(k3PSIDTSCookieName),
+ expiration_time_3PSIDTS);
+ // The new `expiration_time_1PSIDTS` is larger than the other cookie
+ // expiration time so the minimum remains unchanged.
+ EXPECT_EQ(controller->min_cookie_expiration_time(), minimum_expiration_time);
+ EXPECT_EQ(on_bound_session_throttler_params_changed_call_count(), 0u);
+
+ task_environment()->FastForwardBy(base::Minutes(2));
+ // Simulate cookie change of 2nd cookie.
+ SimulateCookieChange(k3PSIDTSCookieName, GetTimeInTenMinutes());
+ EXPECT_EQ(cookie_expiration_time(k3PSIDTSCookieName),
+ GetTimeInTenMinutes() - kCookieExpirationThreshold);
+ // Expiration time of: `k3PSIDTSCookieName` > `k1PSIDTSCookieName`.
+ // The minimum changes to the expiration date of `k1PSIDTSCookieName`.
+ EXPECT_EQ(controller->min_cookie_expiration_time(), expiration_time_1PSIDTS);
+ EXPECT_EQ(on_bound_session_throttler_params_changed_call_count(), 1u);
}
TEST_F(BoundSessionCookieControllerImplTest,
RequestBlockedOnCookieWhenCookieFresh) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
// Set fresh cookie.
CompletePendingRefreshRequestIfAny();
BoundSessionCookieController* controller = bound_session_cookie_controller();
- EXPECT_EQ(controller->cookie_expiration_time(), GetTimeInTenMinutes());
+ EXPECT_TRUE(AreAllCookiesFresh());
// No fetch should be triggered since the cookie is fresh.
// The callback should return immediately.
@@ -236,16 +380,21 @@ TEST_F(BoundSessionCookieControllerImplTest,
}
TEST_F(BoundSessionCookieControllerImplTest,
- RequestBlockedOnCookieWhenCookieStale) {
+ RequestBlockedOnCookieWhenCookieStaleTriggersARefresh) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
CompletePendingRefreshRequestIfAny();
BoundSessionCookieController* controller = bound_session_cookie_controller();
task_environment()->FastForwardBy(base::Minutes(12));
// Cookie stale.
- EXPECT_LT(controller->cookie_expiration_time(), base::Time::Now());
+ EXPECT_FALSE(AreAllCookiesFresh());
+ // Preemptive cookie rotation also fails with persistent error.
+ SimulateCompleteRefreshRequest(
+ BoundSessionRefreshCookieFetcher::Result::kConnectionError,
+ absl::nullopt);
EXPECT_FALSE(cookie_fetcher());
- // Request blocked on the cookie
+ // Request blocked on the cookie.
base::test::TestFuture<void> future;
controller->OnRequestBlockedOnCookie(future.GetCallback());
EXPECT_FALSE(future.IsReady());
@@ -256,20 +405,51 @@ TEST_F(BoundSessionCookieControllerImplTest,
GetTimeInTenMinutes());
task_environment()->RunUntilIdle();
EXPECT_TRUE(future.IsReady());
- EXPECT_EQ(controller->cookie_expiration_time(), GetTimeInTenMinutes());
+ EXPECT_TRUE(AreAllCookiesFresh());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ RequestBlockedWhenNotAllCookiesFresh) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
+ CompletePendingRefreshRequestIfAny();
+ BoundSessionCookieController* controller = bound_session_cookie_controller();
+
+ // All cookies stale.
+ task_environment()->FastForwardBy(base::Minutes(12));
+ EXPECT_FALSE(AreAllCookiesFresh());
+ // Request blocked on the cookies.
+ base::test::TestFuture<void> future;
+ controller->OnRequestBlockedOnCookie(future.GetCallback());
+ EXPECT_FALSE(future.IsReady());
+
+ // One cookie is fresh.
+ SetExpirationTimeAndNotify(k1PSIDTSCookieName, GetTimeInTenMinutes());
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_FALSE(AreAllCookiesFresh());
+
+ // All cookies fresh.
+ SetExpirationTimeAndNotify(k3PSIDTSCookieName, GetTimeInTenMinutes());
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_TRUE(AreAllCookiesFresh());
+ CompletePendingRefreshRequestIfAny();
}
TEST_F(BoundSessionCookieControllerImplTest,
RequestBlockedOnCookieRefreshFailedWithPersistentError) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
CompletePendingRefreshRequestIfAny();
EXPECT_FALSE(on_cookie_refresh_persistent_failure_called());
BoundSessionCookieController* controller = bound_session_cookie_controller();
task_environment()->FastForwardBy(base::Minutes(12));
- base::Time cookie_expiration = controller->cookie_expiration_time();
+ base::Time min_cookie_expiration = controller->min_cookie_expiration_time();
// Cookie stale.
- EXPECT_LT(cookie_expiration, base::Time::Now());
+ EXPECT_FALSE(AreAllCookiesFresh());
+ // Preemptive cookie rotation also fails with persistent error.
+ SimulateCompleteRefreshRequest(
+ BoundSessionRefreshCookieFetcher::Result::kConnectionError,
+ absl::nullopt);
EXPECT_FALSE(cookie_fetcher());
base::test::TestFuture<void> future;
@@ -283,16 +463,14 @@ TEST_F(BoundSessionCookieControllerImplTest,
task_environment()->RunUntilIdle();
EXPECT_TRUE(on_cookie_refresh_persistent_failure_called());
EXPECT_TRUE(future.IsReady());
- EXPECT_EQ(controller->cookie_expiration_time(), cookie_expiration);
+ EXPECT_EQ(controller->min_cookie_expiration_time(), min_cookie_expiration);
}
TEST_F(BoundSessionCookieControllerImplTest, RefreshFailedTransient) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
CompletePendingRefreshRequestIfAny();
task_environment()->FastForwardBy(base::Minutes(12));
-
- BoundSessionCookieController* controller = bound_session_cookie_controller();
- EXPECT_LT(controller->cookie_expiration_time(), base::Time::Now());
-
+ EXPECT_FALSE(AreAllCookiesFresh());
std::array<BoundSessionRefreshCookieFetcher::Result, 2> result_types = {
BoundSessionRefreshCookieFetcher::Result::kConnectionError,
BoundSessionRefreshCookieFetcher::Result::kServerTransientError};
@@ -322,8 +500,9 @@ TEST_F(BoundSessionCookieControllerImplTest, RefreshFailedTransient) {
TEST_F(BoundSessionCookieControllerImplTest,
RequestBlockedOnCookieMultipleRequests) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
CompletePendingRefreshRequestIfAny();
- ResetOnCookieExpirationDateChangedCallCount();
+ ResetOnBoundSessionThrottlerParamsChangedCallCount();
// Cookie stale.
task_environment()->FastForwardBy(base::Minutes(12));
@@ -341,12 +520,13 @@ TEST_F(BoundSessionCookieControllerImplTest,
for (auto& future : futures) {
EXPECT_TRUE(future.IsReady());
}
- EXPECT_EQ(on_cookie_expiration_date_changed_call_count(), 1u);
- EXPECT_EQ(controller->cookie_expiration_time(), GetTimeInTenMinutes());
+ EXPECT_EQ(on_bound_session_throttler_params_changed_call_count(), 1u);
+ EXPECT_TRUE(AreAllCookiesFresh());
}
TEST_F(BoundSessionCookieControllerImplTest,
CookieChangesToFreshWhileRequestBlockedOnCookieIsPending) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
CompletePendingRefreshRequestIfAny();
// Stale cookie.
task_environment()->FastForwardBy(base::Minutes(12));
@@ -359,7 +539,9 @@ TEST_F(BoundSessionCookieControllerImplTest,
EXPECT_FALSE(future.IsReady());
// Cookie fresh.
- SimulateCookieChange(GetTimeInTenMinutes());
+ SimulateCookieChange(k1PSIDTSCookieName, GetTimeInTenMinutes());
+ EXPECT_FALSE(future.IsReady());
+ SimulateCookieChange(k3PSIDTSCookieName, GetTimeInTenMinutes());
EXPECT_TRUE(future.IsReady());
// Complete the pending fetch.
@@ -371,6 +553,7 @@ TEST_F(BoundSessionCookieControllerImplTest,
TEST_F(BoundSessionCookieControllerImplTest,
ControllerDestroyedRequestBlockedOnCookieIsPending) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
BoundSessionCookieController* controller = bound_session_cookie_controller();
std::array<base::test::TestFuture<void>, 5> futures;
for (auto& future : futures) {
@@ -383,3 +566,199 @@ TEST_F(BoundSessionCookieControllerImplTest,
EXPECT_TRUE(future.IsReady());
}
}
+
+TEST_F(BoundSessionCookieControllerImplTest, ResumeBlockedRequestsOnTimeout) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
+ base::test::TestFuture<void> future;
+ bound_session_cookie_controller()->OnRequestBlockedOnCookie(
+ future.GetCallback());
+ EXPECT_FALSE(future.IsReady());
+
+ task_environment()->FastForwardBy(kResumeBlockedRequestTimeout);
+ // The required cookies are stale, but the request is resumed due to timeout.
+ EXPECT_FALSE(AreAllCookiesFresh());
+ // Request resumed.
+ EXPECT_TRUE(future.IsReady());
+ // Fetcher reset.
+ EXPECT_FALSE(cookie_fetcher());
+ EXPECT_FALSE(resume_blocked_requests_timer()->IsRunning());
+ EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ BlockedRequestsCalculateTimeoutFromFirstRequest) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
+ constexpr int kBlockedRequestCount = 2;
+ constexpr base::TimeDelta kDeltaBetweenRequests = base::Seconds(1);
+ BoundSessionCookieController* controller = bound_session_cookie_controller();
+ std::array<base::test::TestFuture<void>, kBlockedRequestCount> futures;
+
+ for (auto& future : futures) {
+ controller->OnRequestBlockedOnCookie(future.GetCallback());
+ EXPECT_FALSE(future.IsReady());
+ task_environment()->FastForwardBy(kDeltaBetweenRequests);
+ }
+
+ // We should release as soon as `kResumeBlockedRequestTimeout` has passed
+ // since the first blocked request.
+ task_environment()->FastForwardBy(kResumeBlockedRequestTimeout -
+ kDeltaBetweenRequests *
+ kBlockedRequestCount);
+ // The required cookies are stale, but requests are resumed due to timeout.
+ EXPECT_FALSE(AreAllCookiesFresh());
+ EXPECT_FALSE(cookie_fetcher());
+ for (auto& future : futures) {
+ EXPECT_TRUE(future.IsReady());
+ }
+ EXPECT_FALSE(resume_blocked_requests_timer()->IsRunning());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest, ResumeBlockedRequestsTimerReset) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
+ base::test::TestFuture<void> future;
+ bound_session_cookie_controller()->OnRequestBlockedOnCookie(
+ future.GetCallback());
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_TRUE(resume_blocked_requests_timer()->IsRunning());
+ SimulateCompleteRefreshRequest(
+ BoundSessionRefreshCookieFetcher::Result::kSuccess,
+ GetTimeInTenMinutes());
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_FALSE(resume_blocked_requests_timer()->IsRunning());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ ResumeNewBlockedRequestsWhenOffline) {
+ SetUpNetworkConnection(true, network::mojom::ConnectionType::CONNECTION_NONE);
+ ASSERT_TRUE(IsConnectionTypeAvailableAndOffline());
+ base::test::TestFuture<void> future;
+ bound_session_cookie_controller()->OnRequestBlockedOnCookie(
+ future.GetCallback());
+ EXPECT_FALSE(AreAllCookiesFresh());
+ EXPECT_TRUE(cookie_fetcher());
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_FALSE(resume_blocked_requests_timer()->IsRunning());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ BlockRequestsWhenConnectionTypeUnavailable) {
+ SetUpNetworkConnection(false,
+ network::mojom::ConnectionType::CONNECTION_WIFI);
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
+ base::test::TestFuture<void> future;
+ bound_session_cookie_controller()->OnRequestBlockedOnCookie(
+ future.GetCallback());
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_TRUE(resume_blocked_requests_timer()->IsRunning());
+ task_environment()->RunUntilIdle();
+ EXPECT_FALSE(future.IsReady());
+ CompletePendingRefreshRequestIfAny();
+ EXPECT_TRUE(future.IsReady());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ ResumePendingBlockedRequestsOnNetworkConnectionChangedToOffline) {
+ ASSERT_FALSE(IsConnectionTypeAvailableAndOffline());
+ base::test::TestFuture<void> future;
+ bound_session_cookie_controller()->OnRequestBlockedOnCookie(
+ future.GetCallback());
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_TRUE(resume_blocked_requests_timer()->IsRunning());
+
+ SetConnectionType(network::mojom::ConnectionType::CONNECTION_NONE);
+ ASSERT_TRUE(IsConnectionTypeAvailableAndOffline());
+ task_environment()->RunUntilIdle();
+ EXPECT_FALSE(AreAllCookiesFresh());
+ EXPECT_TRUE(cookie_fetcher());
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_FALSE(resume_blocked_requests_timer()->IsRunning());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ NotNullCookieExpirationTimeIsReducedByThreshold) {
+ EXPECT_TRUE(CompletePendingRefreshRequestIfAny());
+ EXPECT_EQ(cookie_expiration_time(k1PSIDTSCookieName),
+ GetTimeInTenMinutes() - kCookieExpirationThreshold);
+ EXPECT_EQ(cookie_expiration_time(k3PSIDTSCookieName),
+ GetTimeInTenMinutes() - kCookieExpirationThreshold);
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ NullCookieExpirationTimeIsNotReducedByThreshold) {
+ EXPECT_TRUE(CompletePendingRefreshRequestIfAny());
+ SetExpirationTimeAndNotify(k1PSIDTSCookieName, base::Time());
+ EXPECT_EQ(cookie_expiration_time(k1PSIDTSCookieName), base::Time());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ ScheduleCookieRotationOnSetCookieExpiration) {
+ ResetOnBoundSessionThrottlerParamsChangedCallCount();
+ EXPECT_TRUE(CompletePendingRefreshRequestIfAny());
+ EXPECT_EQ(on_bound_session_throttler_params_changed_call_count(), 1u);
+ EXPECT_TRUE(preemptive_cookie_refresh_timer()->IsRunning());
+ base::TimeDelta expected_refresh_delay =
+ bound_session_cookie_controller()->min_cookie_expiration_time() -
+ base::Time::Now() - kCookieRefreshInterval;
+ EXPECT_EQ(preemptive_cookie_refresh_timer()->GetCurrentDelay(),
+ expected_refresh_delay);
+ task_environment()->FastForwardBy(expected_refresh_delay);
+ EXPECT_TRUE(cookie_fetcher());
+ CompletePendingRefreshRequestIfAny();
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ RescheduleCookieRotationOnlyIfMinimumExpirationDateChanged) {
+ CompletePendingRefreshRequestIfAny();
+ EXPECT_TRUE(preemptive_cookie_refresh_timer()->IsRunning());
+ task_environment()->FastForwardBy(base::Minutes(12));
+
+ // We want to test that a cookie refresh is scheduled only when the minimum
+ // expiration time of the two cookies changes.
+ // We first set up a situation where both cookies are stale and there is no
+ // ongoing refresh. `kServerTransientError` is used to complete the refresh
+ // request without updating the cookies.
+ SimulateCompleteRefreshRequest(
+ BoundSessionRefreshCookieFetcher::Result::kServerTransientError,
+ absl::nullopt);
+ EXPECT_FALSE(cookie_fetcher());
+ EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+ base::Time old_min_cookie_expiration =
+ bound_session_cookie_controller()->min_cookie_expiration_time();
+
+ SetExpirationTimeAndNotify(k1PSIDTSCookieName, GetTimeInTenMinutes());
+ // The new expiration time of `k1PSIDTSCookieName` is larger than the other
+ // cookie expiration time so the minimum remains unchanged.
+ EXPECT_EQ(bound_session_cookie_controller()->min_cookie_expiration_time(),
+ old_min_cookie_expiration);
+ // Cookie rotation is not scheduled.
+ EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+
+ SetExpirationTimeAndNotify(k3PSIDTSCookieName, GetTimeInTenMinutes());
+ // The expiration time of the other cookie is updated, and the minimum
+ // expiration time changes.
+ EXPECT_NE(bound_session_cookie_controller()->min_cookie_expiration_time(),
+ old_min_cookie_expiration);
+ // Cookie rotation scheduled.
+ EXPECT_TRUE(preemptive_cookie_refresh_timer()->IsRunning());
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ RefreshCookieImmediatelyOnSetCookieExpirationBelowRefreshInterval) {
+ EXPECT_TRUE(CompletePendingRefreshRequestIfAny());
+ ResetOnBoundSessionThrottlerParamsChangedCallCount();
+ SetExpirationTimeAndNotify(k1PSIDTSCookieName,
+ base::Time::Now() + kCookieRefreshInterval / 2);
+ EXPECT_EQ(on_bound_session_throttler_params_changed_call_count(), 1u);
+ EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+ EXPECT_TRUE(cookie_fetcher());
+ CompletePendingRefreshRequestIfAny();
+}
+
+TEST_F(BoundSessionCookieControllerImplTest,
+ StopCookieRotationOnCookieRefresh) {
+ EXPECT_TRUE(CompletePendingRefreshRequestIfAny());
+ EXPECT_TRUE(preemptive_cookie_refresh_timer()->IsRunning());
+ MaybeRefreshCookie();
+ EXPECT_FALSE(preemptive_cookie_refresh_timer()->IsRunning());
+ CompletePendingRefreshRequestIfAny();
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.cc
index 2d288ad9509..1014521d7cf 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.cc
@@ -4,7 +4,7 @@
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h"
-#include "chrome/browser/signin/chrome_signin_client.h"
+#include "content/public/browser/storage_partition.h"
#include "net/cookies/canonical_cookie.h"
namespace {
@@ -26,11 +26,11 @@ absl::optional<const net::CanonicalCookie> GetCookie(
} // namespace
BoundSessionCookieObserver::BoundSessionCookieObserver(
- SigninClient* client,
+ content::StoragePartition* storage_partion,
const GURL& url,
const std::string& cookie_name,
CookieExpirationDateUpdate callback)
- : client_(client),
+ : storage_partition_(storage_partion),
url_(url),
cookie_name_(cookie_name),
callback_(std::move(callback)) {
@@ -41,7 +41,8 @@ BoundSessionCookieObserver::BoundSessionCookieObserver(
BoundSessionCookieObserver::~BoundSessionCookieObserver() = default;
void BoundSessionCookieObserver::StartGetCookieList() {
- network::mojom::CookieManager* cookie_manager = client_->GetCookieManager();
+ network::mojom::CookieManager* cookie_manager =
+ storage_partition_->GetCookieManagerForBrowserProcess();
if (!cookie_manager) {
return;
}
@@ -60,7 +61,8 @@ void BoundSessionCookieObserver::OnGetCookieList(
GetCookie(cookie_list, cookie_name_);
DCHECK(!GetCookie(excluded_cookies, cookie_name_).has_value())
<< "BSC cookie should not be excluded!";
- callback_.Run(cookie.has_value() ? cookie->ExpiryDate() : base::Time());
+ callback_.Run(cookie_name_,
+ cookie.has_value() ? cookie->ExpiryDate() : base::Time());
}
void BoundSessionCookieObserver::OnCookieChange(
@@ -70,7 +72,7 @@ void BoundSessionCookieObserver::OnCookieChange(
switch (change.cause) {
// The cookie was inserted.
case net::CookieChangeCause::INSERTED:
- callback_.Run(change.cookie.ExpiryDate());
+ callback_.Run(cookie_name_, change.cookie.ExpiryDate());
break;
// The cookie was automatically removed due to an insert operation that
@@ -90,20 +92,21 @@ void BoundSessionCookieObserver::OnCookieChange(
// The cookie was overwritten with an already-expired expiration date.
case net::CookieChangeCause::EXPIRED_OVERWRITE:
DCHECK(net::CookieChangeCauseIsDeletion(change.cause));
- callback_.Run(base::Time());
+ callback_.Run(cookie_name_, base::Time());
break;
// The cookie was automatically removed as it expired.
case net::CookieChangeCause::EXPIRED:
DCHECK(net::CookieChangeCauseIsDeletion(change.cause));
DCHECK(change.cookie.ExpiryDate() < base::Time::Now());
- callback_.Run(change.cookie.ExpiryDate());
+ callback_.Run(cookie_name_, change.cookie.ExpiryDate());
}
}
void BoundSessionCookieObserver::AddCookieChangeListener() {
DCHECK(!cookie_listener_receiver_.is_bound());
- network::mojom::CookieManager* cookie_manager = client_->GetCookieManager();
+ network::mojom::CookieManager* cookie_manager =
+ storage_partition_->GetCookieManagerForBrowserProcess();
// NOTE: `cookie_manager` can be nullptr when TestSigninClient is used in
// testing contexts.
if (!cookie_manager) {
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h
index 0fade36ae42..d0d60a01f78 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer.h
@@ -7,11 +7,15 @@
#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/cookies/cookie_change_dispatcher.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
-class SigninClient;
+namespace content {
+class StoragePartition;
+}
+
class GURL;
// This class monitors a cookie and notifies a callback whenever the expiration
@@ -20,10 +24,11 @@ class BoundSessionCookieObserver : public network::mojom::CookieChangeListener {
public:
// Returns the expected expiration date of the observed cookie or
// `base::Time()` if the cookie was removed.
- using CookieExpirationDateUpdate = base::RepeatingCallback<void(base::Time)>;
+ using CookieExpirationDateUpdate =
+ base::RepeatingCallback<void(const std::string&, base::Time)>;
- // `client_` must outlive `this`.
- BoundSessionCookieObserver(SigninClient* client,
+ // `storage_partition_` must outlive `this`.
+ BoundSessionCookieObserver(content::StoragePartition* storage_partion_,
const GURL& url,
const std::string& cookie_name,
CookieExpirationDateUpdate callback);
@@ -47,7 +52,7 @@ class BoundSessionCookieObserver : public network::mojom::CookieChangeListener {
mojo::Receiver<network::mojom::CookieChangeListener>
cookie_listener_receiver_{this};
- const raw_ptr<SigninClient> client_;
+ const raw_ptr<content::StoragePartition> storage_partition_;
const GURL url_;
const std::string cookie_name_;
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer_unittest.cc
index c18b091b39b..f70eba6ac6c 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer_unittest.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_observer_unittest.cc
@@ -10,28 +10,22 @@
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
-#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h"
-#include "components/signin/public/base/test_signin_client.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/test_storage_partition.h"
#include "net/cookies/canonical_cookie.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
-using signin::ConsentLevel;
-
namespace {
constexpr char kSIDTSCookieName[] = "__Secure-1PSIDTS";
class BoundSessionCookieObserverTest : public testing::Test {
public:
const GURL kGaiaUrl = GURL("https://google.com");
- BoundSessionCookieObserverTest() : signin_client_(&prefs_) {
- ResetCookieManager();
- }
+ BoundSessionCookieObserverTest() { ResetCookieManager(); }
~BoundSessionCookieObserverTest() override = default;
@@ -39,7 +33,7 @@ class BoundSessionCookieObserverTest : public testing::Test {
if (!bound_session_cookie_observer_) {
bound_session_cookie_observer_ =
std::make_unique<BoundSessionCookieObserver>(
- &signin_client_, kGaiaUrl, kSIDTSCookieName,
+ &storage_partition_, kGaiaUrl, kSIDTSCookieName,
base::BindRepeating(
&BoundSessionCookieObserverTest::UpdateExpirationDate,
base::Unretained(this)));
@@ -47,10 +41,12 @@ class BoundSessionCookieObserverTest : public testing::Test {
}
void ResetCookieManager() {
- std::unique_ptr<BoundSessionTestCookieManager> fake_cookie_manager =
- std::make_unique<BoundSessionTestCookieManager>();
- cookie_manager_ = fake_cookie_manager.get();
- signin_client_.set_cookie_manager(std::move(fake_cookie_manager));
+ auto cookie_manager = std::make_unique<BoundSessionTestCookieManager>();
+ storage_partition_.set_cookie_manager_for_browser_process(
+ cookie_manager.get());
+ // Reset storage partition's cookie manager before resetting
+ // `cookie_manager_` to avoid having a dangling raw pointer.
+ cookie_manager_ = std::move(cookie_manager);
}
void Reset() {
@@ -61,30 +57,30 @@ class BoundSessionCookieObserverTest : public testing::Test {
}
void SetNextCookieChangeCallback(
- base::OnceCallback<void(base::Time)> callback) {
+ base::OnceCallback<void(const std::string&, base::Time)> callback) {
// Old expectations should have been verified.
EXPECT_FALSE(on_cookie_change_callback_);
on_cookie_change_callback_ = std::move(callback);
}
- void UpdateExpirationDate(base::Time expiration_date) {
+ void UpdateExpirationDate(const std::string& cookie_name,
+ base::Time expiration_date) {
update_expiration_date_call_count_++;
cookie_expiration_date_ = expiration_date;
if (on_cookie_change_callback_) {
- std::move(on_cookie_change_callback_).Run(expiration_date);
+ std::move(on_cookie_change_callback_).Run(cookie_name, expiration_date);
}
}
protected:
base::test::TaskEnvironment task_environment_;
- sync_preferences::TestingPrefServiceSyncable prefs_;
- TestSigninClient signin_client_;
-
- raw_ptr<BoundSessionTestCookieManager> cookie_manager_;
+ std::unique_ptr<BoundSessionTestCookieManager> cookie_manager_;
+ content::TestStoragePartition storage_partition_;
std::unique_ptr<BoundSessionCookieObserver> bound_session_cookie_observer_;
size_t update_expiration_date_call_count_ = 0;
base::Time cookie_expiration_date_;
- base::OnceCallback<void(base::Time)> on_cookie_change_callback_;
+ base::OnceCallback<void(const std::string&, base::Time)>
+ on_cookie_change_callback_;
};
TEST_F(BoundSessionCookieObserverTest, CookieAvailableOnStartup) {
@@ -116,12 +112,13 @@ TEST_F(BoundSessionCookieObserverTest, CookieInserted) {
// Insert event.
net::CanonicalCookie cookie =
BoundSessionTestCookieManager::CreateCookie(kGaiaUrl, kSIDTSCookieName);
- base::test::TestFuture<base::Time> future;
+ base::test::TestFuture<const std::string&, base::Time> future;
SetNextCookieChangeCallback(future.GetCallback());
cookie_manager_->DispatchCookieChange(net::CookieChangeInfo(
cookie, net::CookieAccessResult(), net::CookieChangeCause::INSERTED));
- EXPECT_EQ(cookie.ExpiryDate(), future.Get());
+ EXPECT_EQ(future.Get<0>(), cookie.Name());
+ EXPECT_EQ(future.Get<1>(), cookie.ExpiryDate());
EXPECT_EQ(update_expiration_date_call_count_, 1u);
EXPECT_EQ(cookie_expiration_date_, cookie.ExpiryDate());
}
@@ -161,12 +158,13 @@ TEST_F(BoundSessionCookieObserverTest, CookieDeleted) {
update_expiration_date_call_count_;
for (auto cookie_change_cause : cookie_deleted) {
SCOPED_TRACE(net::CookieChangeCauseToString(cookie_change_cause));
- base::test::TestFuture<base::Time> future;
+ base::test::TestFuture<const std::string&, base::Time> future;
SetNextCookieChangeCallback(future.GetCallback());
cookie_manager_->DispatchCookieChange(net::CookieChangeInfo(
cookie, net::CookieAccessResult(), cookie_change_cause));
expected_update_expiration_date_call_count++;
- EXPECT_EQ(future.Get(), base::Time());
+ EXPECT_EQ(future.Get<0>(), cookie.Name());
+ EXPECT_EQ(future.Get<1>(), base::Time());
EXPECT_EQ(update_expiration_date_call_count_,
expected_update_expiration_date_call_count);
EXPECT_TRUE(cookie_expiration_date_.is_null());
@@ -179,11 +177,12 @@ TEST_F(BoundSessionCookieObserverTest, CookieExpired) {
net::CanonicalCookie cookie = BoundSessionTestCookieManager::CreateCookie(
kGaiaUrl, kSIDTSCookieName, base::Time::Now() - base::Minutes(1));
- base::test::TestFuture<base::Time> future;
+ base::test::TestFuture<const std::string&, base::Time> future;
SetNextCookieChangeCallback(future.GetCallback());
cookie_manager_->DispatchCookieChange(net::CookieChangeInfo(
cookie, net::CookieAccessResult(), net::CookieChangeCause::EXPIRED));
- EXPECT_EQ(future.Get(), cookie.ExpiryDate());
+ EXPECT_EQ(future.Get<0>(), cookie.Name());
+ EXPECT_EQ(future.Get<1>(), cookie.ExpiryDate());
EXPECT_EQ(update_expiration_date_call_count_, 2u);
EXPECT_EQ(cookie_expiration_date_, cookie.ExpiryDate());
}
@@ -198,7 +197,7 @@ TEST_F(BoundSessionCookieObserverTest, OnCookieChangeListenerConnectionError) {
EXPECT_EQ(update_expiration_date_call_count_, 1u);
EXPECT_EQ(cookie_expiration_date_, cookie.ExpiryDate());
- base::test::TestFuture<base::Time> future_removed;
+ base::test::TestFuture<const std::string&, base::Time> future_removed;
SetNextCookieChangeCallback(future_removed.GetCallback());
// Reset the cookie manager to simulate
@@ -208,19 +207,19 @@ TEST_F(BoundSessionCookieObserverTest, OnCookieChangeListenerConnectionError) {
ResetCookieManager();
// Expect a notification that the cookie was removed.
- EXPECT_EQ(base::Time(), future_removed.Get());
+ EXPECT_EQ(future_removed.Get<1>(), base::Time());
EXPECT_EQ(update_expiration_date_call_count_, 2u);
EXPECT_TRUE(cookie_expiration_date_.is_null());
// Trigger a cookie change to verify the cookie listener has been hooked up
// to the new `cookie_manager_`.
// Insert event.
- base::test::TestFuture<base::Time> future_inserted;
+ base::test::TestFuture<const std::string&, base::Time> future_inserted;
SetNextCookieChangeCallback(future_inserted.GetCallback());
cookie_manager_->DispatchCookieChange(net::CookieChangeInfo(
cookie, net::CookieAccessResult(), net::CookieChangeCause::INSERTED));
- EXPECT_EQ(cookie.ExpiryDate(), future_inserted.Get());
+ EXPECT_EQ(future_inserted.Get<1>(), cookie.ExpiryDate());
EXPECT_EQ(update_expiration_date_call_count_, 3u);
EXPECT_EQ(cookie_expiration_date_, cookie.ExpiryDate());
}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h
index 3038252c9cd..ca58d112ed0 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h
@@ -7,7 +7,8 @@
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
-#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_params.pb.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h"
#include "chrome/common/renderer_configuration.mojom.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -22,7 +23,8 @@ class BoundSessionCookieRefreshService
: public KeyedService,
public chrome::mojom::BoundSessionRequestThrottledListener {
public:
- using RendererBoundSessionParamsUpdaterDelegate = base::RepeatingClosure;
+ using RendererBoundSessionThrottlerParamsUpdaterDelegate =
+ base::RepeatingClosure;
BoundSessionCookieRefreshService() = default;
@@ -36,14 +38,20 @@ class BoundSessionCookieRefreshService
// Registers a new bound session and starts tracking it immediately. The
// session persists across browser startups.
virtual void RegisterNewBoundSession(
- const bound_session_credentials::RegistrationParams& params) = 0;
+ const bound_session_credentials::BoundSessionParams& params) = 0;
- // Returns true if session is bound.
- virtual bool IsBoundSession() const = 0;
+ // Terminate the session if the session termination header is set and the
+ // `session_id` matches the current bound session's id. This header is
+ // expected to be set on signout.
+ virtual void MaybeTerminateSession(
+ const net::HttpResponseHeaders* headers) = 0;
// Returns bound session params.
- virtual chrome::mojom::BoundSessionParamsPtr GetBoundSessionParams()
- const = 0;
+ virtual chrome::mojom::BoundSessionThrottlerParamsPtr
+ GetBoundSessionThrottlerParams() const = 0;
+
+ virtual void CreateRegistrationRequest(
+ BoundSessionRegistrationFetcherParam registration_params) = 0;
virtual base::WeakPtr<BoundSessionCookieRefreshService> GetWeakPtr() = 0;
@@ -51,9 +59,10 @@ class BoundSessionCookieRefreshService
friend class RendererUpdater;
// `RendererUpdater` class that is responsible for pushing updates to all
- // renderers calls this setter to subscribe for bound session params updates.
- virtual void SetRendererBoundSessionParamsUpdaterDelegate(
- RendererBoundSessionParamsUpdaterDelegate renderer_updater) = 0;
+ // renderers calls this setter to subscribe for bound session throttler params
+ // updates.
+ virtual void SetRendererBoundSessionThrottlerParamsUpdaterDelegate(
+ RendererBoundSessionThrottlerParamsUpdaterDelegate renderer_updater) = 0;
// Adds a Receiver to `BoundSessionCookieRefreshService` to receive
// notification when a request is throttled and requires a fresh cookie.
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.cc
index 3553dab8022..a06f22b5f57 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.cc
@@ -5,17 +5,22 @@
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.h"
#include <memory>
+#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/account_consistency_mode_manager_factory.h"
-#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_storage.h"
+#include "chrome/browser/signin/bound_session_credentials/unexportable_key_service_factory.h"
#include "chrome/browser/signin/chrome_signin_client_factory.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/signin_features.h"
#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/signin/public/base/account_consistency_method.h"
#include "components/signin/public/base/signin_switches.h"
+#include "content/public/browser/network_service_instance.h"
// static
BoundSessionCookieRefreshServiceFactory*
@@ -36,14 +41,11 @@ BoundSessionCookieRefreshServiceFactory::
: ProfileKeyedServiceFactory(
"BoundSessionCookieRefreshService",
ProfileSelections::Builder()
- .WithRegular(ProfileSelection::kOriginalOnly)
- // TODO(crbug.com/1418376): Check if this service is needed in
- // Guest mode.
- .WithGuest(ProfileSelection::kOriginalOnly)
+ .WithRegular(ProfileSelection::kOwnInstance)
+ .WithGuest(ProfileSelection::kOwnInstance)
.Build()) {
- DependsOn(IdentityManagerFactory::GetInstance());
+ DependsOn(UnexportableKeyServiceFactory::GetInstance());
DependsOn(AccountConsistencyModeManagerFactory::GetInstance());
- DependsOn(ChromeSigninClientFactory::GetInstance());
}
BoundSessionCookieRefreshServiceFactory::
@@ -57,22 +59,39 @@ BoundSessionCookieRefreshServiceFactory::BuildServiceInstanceForBrowserContext(
}
Profile* profile = Profile::FromBrowserContext(context);
- // The account consistency method should not change during the lifetime of a
- // profile. This service is needed when Dice is enabled.
- if (!AccountConsistencyModeManager::IsDiceEnabledForProfile(profile)) {
+ unexportable_keys::UnexportableKeyService* key_service =
+ UnexportableKeyServiceFactory::GetForProfile(profile);
+
+ if (!key_service) {
+ // A bound session requires a crypto provider.
+ return nullptr;
+ }
+
+ signin::AccountConsistencyMethod account_consistency_method =
+ AccountConsistencyModeManager::GetMethodForProfile(profile);
+ bool should_create_service =
+ account_consistency_method ==
+ signin::AccountConsistencyMethod::kDisabled ||
+ (account_consistency_method == signin::AccountConsistencyMethod::kDice &&
+ base::FeatureList::IsEnabled(
+ kEnableBoundSessionCredentialsOnDiceProfiles));
+
+ if (!should_create_service) {
return nullptr;
}
std::unique_ptr<BoundSessionCookieRefreshService>
bound_session_cookie_refresh_service =
std::make_unique<BoundSessionCookieRefreshServiceImpl>(
- ChromeSigninClientFactory::GetForProfile(profile),
- IdentityManagerFactory::GetForProfile(profile));
+ *key_service,
+ BoundSessionParamsStorage::CreateForProfile(*profile),
+ profile->GetDefaultStoragePartition(),
+ content::GetNetworkConnectionTracker());
bound_session_cookie_refresh_service->Initialize();
return bound_session_cookie_refresh_service;
}
void BoundSessionCookieRefreshServiceFactory::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
- BoundSessionCookieRefreshServiceImpl::RegisterProfilePrefs(registry);
+ BoundSessionParamsStorage::RegisterProfilePrefs(registry);
}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory_unittest.cc
new file mode 100644
index 00000000000..1ddcbac7603
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory_unittest.cc
@@ -0,0 +1,184 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/containers/contains.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_forward.h"
+#include "base/test/scoped_command_line.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/browser/signin/account_consistency_mode_manager_factory.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h"
+#include "chrome/browser/signin/bound_session_credentials/unexportable_key_service_factory.h"
+#include "chrome/browser/signin/signin_features.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/signin/public/base/account_consistency_method.h"
+#include "components/signin/public/base/signin_pref_names.h"
+#include "components/signin/public/base/signin_switches.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/unexportable_keys/fake_unexportable_key_service.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest-param-test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+using base::test::FeatureRef;
+using base::test::ScopedFeatureList;
+using sync_preferences::TestingPrefServiceSyncable;
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+std::unique_ptr<KeyedService> CreateFakeUnexportableKeyService(
+ content::BrowserContext* context) {
+ return std::make_unique<unexportable_keys::FakeUnexportableKeyService>();
+}
+
+bool DoesServiceExistForProfile(Profile* profile) {
+ return BoundSessionCookieRefreshServiceFactory::GetForProfile(profile) !=
+ nullptr;
+}
+
+struct BoundSessionCookieRefreshServiceFactoryTestParams {
+ std::string test_name;
+ std::vector<FeatureRef> enabled_features;
+ std::vector<FeatureRef> disabled_features;
+ ~BoundSessionCookieRefreshServiceFactoryTestParams() = default;
+};
+
+const BoundSessionCookieRefreshServiceFactoryTestParams kTestCases[] = {
+ {"OnlyBoundSessionCredentialsEnabled",
+ {switches::kEnableBoundSessionCredentials},
+ {kEnableBoundSessionCredentialsOnDiceProfiles}},
+ {"OnlyEnableBoundSessionCredentialsOnDiceProfilesEnabled",
+ {kEnableBoundSessionCredentialsOnDiceProfiles},
+ {switches::kEnableBoundSessionCredentials}},
+ {"AllEnabled",
+ {switches::kEnableBoundSessionCredentials,
+ kEnableBoundSessionCredentialsOnDiceProfiles},
+ {}},
+ {"AllDisabled",
+ {},
+ {switches::kEnableBoundSessionCredentials,
+ kEnableBoundSessionCredentialsOnDiceProfiles}}};
+
+class BoundSessionCookieRefreshServiceFactoryTest
+ : public TestWithParam<BoundSessionCookieRefreshServiceFactoryTestParams> {
+ public:
+ BoundSessionCookieRefreshServiceFactoryTest() {
+ feature_list.InitWithFeatures(GetParam().enabled_features,
+ GetParam().disabled_features);
+ }
+
+ void CreateProfile(bool otr_profile = false) {
+ // `BoundSessionCookieRefreshService` depends on `UnexportableKeyService`,
+ // ensure it is not null.
+ profile_builder_.AddTestingFactory(
+ UnexportableKeyServiceFactory::GetInstance(),
+ base::BindRepeating(&CreateFakeUnexportableKeyService));
+
+ if (otr_profile) {
+ original_profile_ = std::make_unique<TestingProfile>();
+ otr_profile_ =
+ std::move(profile_builder_.BuildIncognito(original_profile_.get()));
+ } else {
+ original_profile_ = profile_builder_.Build();
+ }
+ }
+
+ bool ShouldServiceExistDiceEnabled() {
+ return base::Contains(GetParam().enabled_features,
+ switches::kEnableBoundSessionCredentials) &&
+ base::Contains(GetParam().enabled_features,
+ kEnableBoundSessionCredentialsOnDiceProfiles);
+ }
+
+ bool ShouldServiceExistAccountConsistencyDisabled() {
+ return base::Contains(GetParam().enabled_features,
+ switches::kEnableBoundSessionCredentials);
+ }
+
+ TestingProfile* original_profile() { return original_profile_.get(); }
+ TestingProfile* otr_profile() { return otr_profile_.get(); }
+
+ private:
+ content::BrowserTaskEnvironment task_environment;
+ ScopedFeatureList feature_list;
+ TestingProfile::Builder profile_builder_;
+ std::unique_ptr<TestingProfile> original_profile_;
+ raw_ptr<TestingProfile> otr_profile_;
+};
+
+TEST_P(BoundSessionCookieRefreshServiceFactoryTest, RegularProfileDiceEnabled) {
+ CreateProfile();
+ ASSERT_FALSE(original_profile()->IsOffTheRecord());
+ ASSERT_EQ(
+ AccountConsistencyModeManager::GetMethodForProfile(original_profile()),
+ signin::AccountConsistencyMethod::kDice);
+
+ EXPECT_EQ(ShouldServiceExistDiceEnabled(),
+ DoesServiceExistForProfile(original_profile()));
+}
+
+TEST_P(BoundSessionCookieRefreshServiceFactoryTest,
+ RegularProfileDiceDisabled) {
+ base::test::ScopedCommandLine scoped_command_line;
+ scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+ "allow-browser-signin", "false");
+ CreateProfile();
+ ASSERT_FALSE(original_profile()->IsOffTheRecord());
+ ASSERT_EQ(
+ AccountConsistencyModeManager::GetMethodForProfile(original_profile()),
+ signin::AccountConsistencyMethod::kDisabled);
+ EXPECT_EQ(ShouldServiceExistAccountConsistencyDisabled(),
+ DoesServiceExistForProfile(original_profile()));
+}
+
+TEST_P(BoundSessionCookieRefreshServiceFactoryTest, OTRProfile) {
+ CreateProfile(/*otr_profile=*/true);
+ ASSERT_TRUE(otr_profile()->IsOffTheRecord());
+ ASSERT_EQ(AccountConsistencyModeManager::GetMethodForProfile(otr_profile()),
+ signin::AccountConsistencyMethod::kDisabled);
+ EXPECT_EQ(ShouldServiceExistAccountConsistencyDisabled(),
+ DoesServiceExistForProfile(otr_profile()));
+}
+
+TEST(BoundSessionCookieRefreshServiceFactoryTestNullUnexportableKeyService,
+ NullUnexportableKeyService) {
+ content::BrowserTaskEnvironment task_environment;
+ ScopedFeatureList feature_list;
+ feature_list.InitWithFeatures({switches::kEnableBoundSessionCredentials,
+ kEnableBoundSessionCredentialsOnDiceProfiles},
+ {});
+
+ BrowserContextKeyedServiceFactory::TestingFactory
+ unexportable_key_service_factory = base::BindRepeating(
+ [](content::BrowserContext* context)
+ -> std::unique_ptr<KeyedService> { return nullptr; });
+ TestingProfile::Builder profile_builder;
+ profile_builder.AddTestingFactory(
+ UnexportableKeyServiceFactory::GetInstance(),
+ std::move(unexportable_key_service_factory));
+
+ std::unique_ptr<TestingProfile> profile = profile_builder.Build();
+ ASSERT_FALSE(UnexportableKeyServiceFactory::GetForProfile(profile.get()));
+ EXPECT_FALSE(DoesServiceExistForProfile(profile.get()));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ BoundSessionCookieRefreshServiceFactoryTest,
+ ::testing::ValuesIn<BoundSessionCookieRefreshServiceFactoryTestParams>(
+ kTestCases),
+ [](const testing::TestParamInfo<
+ BoundSessionCookieRefreshServiceFactoryTest::ParamType>& info) {
+ return info.param.test_name;
+ });
+} // namespace
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.cc
index 9f5982e675e..924e182eed5 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.cc
@@ -3,246 +3,96 @@
// found in the LICENSE file.
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.h"
+
#include <memory>
+#include "base/base64.h"
#include "base/check.h"
-#include "base/check_op.h"
-#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/memory/weak_ptr.h"
-#include "base/notreached.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller_impl.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_storage.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.h"
#include "chrome/common/renderer_configuration.mojom.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_service.h"
-#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_client.h"
-#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/signin/public/identity_manager/primary_account_change_event.h"
-#include "google_apis/gaia/core_account_id.h"
-#include "google_apis/gaia/gaia_urls.h"
-
-using signin::ConsentLevel;
-using signin::IdentityManager;
-using signin::PrimaryAccountChangeEvent;
+#include "content/public/browser/storage_partition.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "url/origin.h"
namespace {
-const char kRegistrationParamsPref[] =
- "bound_session_credentials_registration_params";
-}
-
-BASE_FEATURE(kBoundSessionExplicitRegistration,
- "BoundSessionExplicitRegistration",
- base::FEATURE_DISABLED_BY_DEFAULT);
-
-class BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker
- : public IdentityManager::Observer {
- public:
- BoundSessionStateTracker(IdentityManager* identity_manager,
- base::RepeatingCallback<void()> callback);
- ~BoundSessionStateTracker() override;
-
- // IdentityManager::Observer
- void OnPrimaryAccountChanged(
- const signin::PrimaryAccountChangeEvent& event_details) override;
- void OnEndBatchOfRefreshTokenStateChanges() override;
- void OnErrorStateOfRefreshTokenUpdatedForAccount(
- const CoreAccountInfo& account_info,
- const GoogleServiceAuthError& error) override;
- void OnRefreshTokensLoaded() override;
- void OnAccountsInCookieUpdated(
- const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
- const GoogleServiceAuthError& error) override;
-
- bool is_bound_session() const;
-
- private:
- bool ComputeIsBoundSession();
- void UpdateIsBoundSession();
- void SetIsBoundSession(bool new_value);
-
- // Assumes the session is bound until proven otherwise to avoid unauthorized
- // requests on startup.
- bool is_bound_session_ = true;
- const raw_ptr<IdentityManager> identity_manager_;
- base::RepeatingCallback<void()> callback_;
-
- base::ScopedObservation<IdentityManager, IdentityManager::Observer>
- identity_manager_observation_{this};
-};
-
-BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- BoundSessionStateTracker(IdentityManager* identity_manager,
- base::RepeatingCallback<void()> callback)
- : identity_manager_(identity_manager), callback_(callback) {
- DCHECK(callback);
- identity_manager_observation_.Observe(identity_manager_.get());
- // Set initial value.
- is_bound_session_ = ComputeIsBoundSession();
-}
-
-BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- ~BoundSessionStateTracker() = default;
-
-bool BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- ComputeIsBoundSession() {
- if (!identity_manager_->HasPrimaryAccount(ConsentLevel::kSignin)) {
- return false;
- }
-
- if (!identity_manager_->AreRefreshTokensLoaded()) {
- return is_bound_session_;
- }
-
- const CoreAccountId primary_account_id =
- identity_manager_->GetPrimaryAccountId(ConsentLevel::kSignin);
- bool is_primary_account_valid =
- identity_manager_->HasAccountWithRefreshToken(primary_account_id) &&
- !identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
- primary_account_id);
-
- // TODO: Add extra check that primary account is actually bound
- // `TokenBindingService::HasBindingKeyForAccount()`.
- return is_primary_account_valid;
-}
-
-void BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- UpdateIsBoundSession() {
- SetIsBoundSession(ComputeIsBoundSession());
-}
-
-void BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- SetIsBoundSession(bool new_value) {
- if (is_bound_session_ == new_value) {
- return;
- }
-
- is_bound_session_ = new_value;
- callback_.Run();
-}
-
-void BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- OnPrimaryAccountChanged(const PrimaryAccountChangeEvent& event_details) {
- if (event_details.GetEventTypeFor(ConsentLevel::kSignin) ==
- PrimaryAccountChangeEvent::Type::kNone) {
- // Upgrade consent to sync has no impact on bound session.
- return;
- }
- UpdateIsBoundSession();
-}
-
-void BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- OnEndBatchOfRefreshTokenStateChanges() {
- UpdateIsBoundSession();
-}
-
-void BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- OnErrorStateOfRefreshTokenUpdatedForAccount(
- const CoreAccountInfo& account_info,
- const GoogleServiceAuthError& error) {
- if (account_info.account_id !=
- identity_manager_->GetPrimaryAccountId(ConsentLevel::kSignin)) {
- return;
- }
- UpdateIsBoundSession();
-}
-
-void BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- OnRefreshTokensLoaded() {
- UpdateIsBoundSession();
-}
-
-void BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- OnAccountsInCookieUpdated(
- const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
- const GoogleServiceAuthError& error) {
- if (accounts_in_cookie_jar_info.accounts_are_fresh &&
- accounts_in_cookie_jar_info.signed_in_accounts.empty()) {
- DCHECK_EQ(error, GoogleServiceAuthError::AuthErrorNone());
- // No need to wait for `OnPrimaryAccountChanged`, update all renderers,
- // cancel any ongoing fetchers, and resume any blocked requests.
- SetIsBoundSession(false);
- } else {
- // Ensure the session stays bound even if list accounts request fails.
- UpdateIsBoundSession();
- }
- // TODO: May be cache last known default user.
-}
-
-bool BoundSessionCookieRefreshServiceImpl::BoundSessionStateTracker::
- is_bound_session() const {
- return is_bound_session_;
+const char kGoogleSessionTerminationHeader[] = "Sec-Session-Google-Termination";
}
BoundSessionCookieRefreshServiceImpl::BoundSessionCookieRefreshServiceImpl(
- SigninClient* client,
- IdentityManager* identity_manager)
- : client_(client), identity_manager_(identity_manager) {}
+ unexportable_keys::UnexportableKeyService& key_service,
+ std::unique_ptr<BoundSessionParamsStorage> session_params_storage,
+ content::StoragePartition* storage_partition,
+ network::NetworkConnectionTracker* network_connection_tracker)
+ : key_service_(key_service),
+ session_params_storage_(std::move(session_params_storage)),
+ storage_partition_(storage_partition),
+ network_connection_tracker_(network_connection_tracker) {
+ CHECK(session_params_storage_);
+ CHECK(storage_partition_);
+ data_removal_observation_.Observe(storage_partition_);
+}
BoundSessionCookieRefreshServiceImpl::~BoundSessionCookieRefreshServiceImpl() =
default;
-// static
-void BoundSessionCookieRefreshServiceImpl::RegisterProfilePrefs(
- user_prefs::PrefRegistrySyncable* registry) {
- registry->RegisterStringPref(kRegistrationParamsPref, std::string());
-}
-
void BoundSessionCookieRefreshServiceImpl::Initialize() {
- if (!base::FeatureList::IsEnabled(kBoundSessionExplicitRegistration)) {
- // `base::Unretained(this)` is safe because `this` owns
- // `bound_session_tracker_`.
- bound_session_tracker_ = std::make_unique<BoundSessionStateTracker>(
- identity_manager_,
- base::BindRepeating(
- &BoundSessionCookieRefreshServiceImpl::OnBoundSessionUpdated,
- base::Unretained(this)));
+ absl::optional<bound_session_credentials::BoundSessionParams>
+ bound_session_params = session_params_storage_->ReadParams();
+ if (bound_session_params.has_value()) {
+ InitializeBoundSession(bound_session_params.value());
}
- OnBoundSessionUpdated();
}
void BoundSessionCookieRefreshServiceImpl::RegisterNewBoundSession(
- const bound_session_credentials::RegistrationParams& params) {
- CHECK(base::FeatureList::IsEnabled(kBoundSessionExplicitRegistration));
- std::string serialized_params = params.SerializeAsString();
- if (serialized_params.empty()) {
- DVLOG(1) << "Failed to serialize bound session registration params.";
+ const bound_session_credentials::BoundSessionParams& params) {
+ if (!session_params_storage_->SaveParams(params)) {
+ DVLOG(1) << "Invalid session params or failed to serialize session params.";
return;
}
- // New session should override the existing one.
- if (IsBoundSession()) {
- StopManagingBoundSessionCookie();
- }
- client_->GetPrefs()->SetString(kRegistrationParamsPref, serialized_params);
- OnBoundSessionUpdated();
+ // New session should override an existing one.
+ cookie_controller_.reset();
+ InitializeBoundSession(params);
}
-bool BoundSessionCookieRefreshServiceImpl::IsBoundSession() const {
- if (base::FeatureList::IsEnabled(kBoundSessionExplicitRegistration)) {
- return client_->GetPrefs()->HasPrefPath(kRegistrationParamsPref);
- } else {
- CHECK(bound_session_tracker_);
- return !force_terminate_bound_session_ &&
- bound_session_tracker_->is_bound_session();
+void BoundSessionCookieRefreshServiceImpl::MaybeTerminateSession(
+ const net::HttpResponseHeaders* headers) {
+ if (!headers) {
+ return;
+ }
+
+ std::string session_id;
+ if (headers->GetNormalizedHeader(kGoogleSessionTerminationHeader,
+ &session_id)) {
+ if (session_id == cookie_controller_->session_id()) {
+ TerminateSession();
+ } else {
+ DVLOG(1) << "Session id on session termination header (" << session_id
+ << ") doesn't match with the current session id ("
+ << cookie_controller_->session_id() << ")";
+ }
}
}
-chrome::mojom::BoundSessionParamsPtr
-BoundSessionCookieRefreshServiceImpl::GetBoundSessionParams() const {
+chrome::mojom::BoundSessionThrottlerParamsPtr
+BoundSessionCookieRefreshServiceImpl::GetBoundSessionThrottlerParams() const {
if (!cookie_controller_) {
- return chrome::mojom::BoundSessionParamsPtr();
+ return chrome::mojom::BoundSessionThrottlerParamsPtr();
}
- return chrome::mojom::BoundSessionParams::New(
- cookie_controller_->url().host(), cookie_controller_->url().path(),
- cookie_controller_->cookie_expiration_time());
+ return cookie_controller_->bound_session_throttler_params();
}
void BoundSessionCookieRefreshServiceImpl::
- SetRendererBoundSessionParamsUpdaterDelegate(
- RendererBoundSessionParamsUpdaterDelegate renderer_updater) {
+ SetRendererBoundSessionThrottlerParamsUpdaterDelegate(
+ RendererBoundSessionThrottlerParamsUpdaterDelegate renderer_updater) {
renderer_updater_ = renderer_updater;
}
@@ -255,64 +105,117 @@ void BoundSessionCookieRefreshServiceImpl::
void BoundSessionCookieRefreshServiceImpl::OnRequestBlockedOnCookie(
OnRequestBlockedOnCookieCallback resume_blocked_request) {
- if (!IsBoundSession()) {
+ if (!cookie_controller_) {
// Session has been terminated.
std::move(resume_blocked_request).Run();
return;
}
- CHECK(cookie_controller_);
cookie_controller_->OnRequestBlockedOnCookie(
std::move(resume_blocked_request));
}
+void BoundSessionCookieRefreshServiceImpl::CreateRegistrationRequest(
+ BoundSessionRegistrationFetcherParam registration_params) {
+ if (active_registration_request_) {
+ // If there are multiple racing registration requests, only one will be
+ // processed and it will contain the most up-to-date set of cookies.
+ return;
+ }
+
+ active_registration_request_ =
+ std::make_unique<BoundSessionRegistrationFetcherImpl>(
+ std::move(registration_params),
+ storage_partition_->GetURLLoaderFactoryForBrowserProcess(),
+ key_service_.get());
+ // `base::Unretained(this)` is safe here because `this` owns the fetcher via
+ // `active_registration_requests_`
+ active_registration_request_->Start(base::BindOnce(
+ &BoundSessionCookieRefreshServiceImpl::OnRegistrationRequestComplete,
+ base::Unretained(this)));
+}
+
base::WeakPtr<BoundSessionCookieRefreshService>
BoundSessionCookieRefreshServiceImpl::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
-void BoundSessionCookieRefreshServiceImpl::OnCookieExpirationDateChanged() {
+void BoundSessionCookieRefreshServiceImpl::OnRegistrationRequestComplete(
+ absl::optional<bound_session_credentials::BoundSessionParams>
+ bound_session_params) {
+ if (bound_session_params.has_value()) {
+ RegisterNewBoundSession(*bound_session_params);
+ }
+
+ active_registration_request_.reset();
+}
+
+void BoundSessionCookieRefreshServiceImpl::
+ OnBoundSessionThrottlerParamsChanged() {
UpdateAllRenderers();
}
void BoundSessionCookieRefreshServiceImpl::TerminateSession() {
- if (base::FeatureList::IsEnabled(kBoundSessionExplicitRegistration)) {
- client_->GetPrefs()->ClearPref(kRegistrationParamsPref);
- } else {
- force_terminate_bound_session_ = true;
+ cookie_controller_.reset();
+ session_params_storage_->ClearParams();
+ UpdateAllRenderers();
+}
+
+void BoundSessionCookieRefreshServiceImpl::OnStorageKeyDataCleared(
+ uint32_t remove_mask,
+ content::StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
+ const base::Time begin,
+ const base::Time end) {
+ // No active session is running. Nothing to terminate.
+ if (!cookie_controller_) {
+ return;
+ }
+
+ // Only terminate a session if cookies are cleared.
+ // TODO(b/296372836): introduce a specific data type for bound sessions.
+ if (!(remove_mask & content::StoragePartition::REMOVE_DATA_MASK_COOKIES)) {
+ return;
+ }
+
+ // Only terminate a session if it was created within the specified time range.
+ base::Time session_creation_time =
+ cookie_controller_->session_creation_time();
+ if (session_creation_time < begin || session_creation_time > end) {
+ return;
}
- OnBoundSessionUpdated();
+
+ // Only terminate a session if its URL matches `storage_key_matcher`.
+ // Bound sessions are only supported in first-party contexts, so it's
+ // acceptable to use `blink::StorageKey::CreateFirstParty()`.
+ if (!storage_key_matcher.Run(blink::StorageKey::CreateFirstParty(
+ url::Origin::Create(cookie_controller_->url())))) {
+ return;
+ }
+
+ TerminateSession();
}
std::unique_ptr<BoundSessionCookieController>
BoundSessionCookieRefreshServiceImpl::CreateBoundSessionCookieController(
- const GURL& url,
- const std::string& cookie_name) {
+ const bound_session_credentials::BoundSessionParams& bound_session_params,
+ const base::flat_set<std::string>& cookie_names) {
return controller_factory_for_testing_.is_null()
? std::make_unique<BoundSessionCookieControllerImpl>(
- client_, url, cookie_name, this)
- : controller_factory_for_testing_.Run(url, cookie_name, this);
+ key_service_.get(), storage_partition_,
+ network_connection_tracker_, bound_session_params,
+ cookie_names, this)
+ : controller_factory_for_testing_.Run(bound_session_params,
+ cookie_names, this);
}
-void BoundSessionCookieRefreshServiceImpl::StartManagingBoundSessionCookie() {
+void BoundSessionCookieRefreshServiceImpl::InitializeBoundSession(
+ const bound_session_credentials::BoundSessionParams& bound_session_params) {
CHECK(!cookie_controller_);
- constexpr char kSIDTSCookieName[] = "__Secure-1PSIDTS";
+ constexpr char k1PSIDTSCookieName[] = "__Secure-1PSIDTS";
+ constexpr char k3PSIDTSCookieName[] = "__Secure-3PSIDTS";
- // TODO(http://b/286222327): pass registration params to controller.
cookie_controller_ = CreateBoundSessionCookieController(
- GaiaUrls::GetInstance()->secure_google_url(), kSIDTSCookieName);
+ bound_session_params, {k1PSIDTSCookieName, k3PSIDTSCookieName});
cookie_controller_->Initialize();
-}
-
-void BoundSessionCookieRefreshServiceImpl::StopManagingBoundSessionCookie() {
- cookie_controller_.reset();
-}
-
-void BoundSessionCookieRefreshServiceImpl::OnBoundSessionUpdated() {
- if (!IsBoundSession()) {
- StopManagingBoundSessionCookie();
- } else {
- StartManagingBoundSessionCookie();
- }
UpdateAllRenderers();
}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.h
index 3c92efdaa66..2de43b00202 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl.h
@@ -9,45 +9,50 @@
#include <memory>
-#include "base/feature_list.h"
+#include "base/containers/span.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h"
-#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_params.pb.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h"
+#include "content/public/browser/storage_partition.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
-class SigninClient;
+namespace unexportable_keys {
+class UnexportableKeyService;
+}
-namespace user_prefs {
-class PrefRegistrySyncable;
+namespace content {
+class StoragePartition;
}
-// If the feature is on, `BoundSessionCookieRefreshServiceImpl` uses only
-// explicitly registered sessions instead of relying on the primary account
-// state.
-BASE_DECLARE_FEATURE(kBoundSessionExplicitRegistration);
+class BoundSessionParamsStorage;
class BoundSessionCookieRefreshServiceImpl
: public BoundSessionCookieRefreshService,
- public BoundSessionCookieController::Delegate {
+ public BoundSessionCookieController::Delegate,
+ public content::StoragePartition::DataRemovalObserver {
public:
explicit BoundSessionCookieRefreshServiceImpl(
- SigninClient* client,
- signin::IdentityManager* identity_manager);
+ unexportable_keys::UnexportableKeyService& key_service,
+ std::unique_ptr<BoundSessionParamsStorage> session_params_storage,
+ content::StoragePartition* storage_partition,
+ network::NetworkConnectionTracker* network_connection_tracker);
~BoundSessionCookieRefreshServiceImpl() override;
- static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
// BoundSessionCookieRefreshService:
void Initialize() override;
// Can be called iff the kBoundSessionExplicitRegistration feature is enabled.
void RegisterNewBoundSession(
- const bound_session_credentials::RegistrationParams& params) override;
- bool IsBoundSession() const override;
- chrome::mojom::BoundSessionParamsPtr GetBoundSessionParams() const override;
+ const bound_session_credentials::BoundSessionParams& params) override;
+ void MaybeTerminateSession(const net::HttpResponseHeaders* headers) override;
+ chrome::mojom::BoundSessionThrottlerParamsPtr GetBoundSessionThrottlerParams()
+ const override;
void AddBoundSessionRequestThrottledListenerReceiver(
mojo::PendingReceiver<chrome::mojom::BoundSessionRequestThrottledListener>
receiver) override;
@@ -56,6 +61,9 @@ class BoundSessionCookieRefreshServiceImpl
void OnRequestBlockedOnCookie(
OnRequestBlockedOnCookieCallback resume_blocked_request) override;
+ void CreateRegistrationRequest(
+ BoundSessionRegistrationFetcherParam registration_params) override;
+
base::WeakPtr<BoundSessionCookieRefreshService> GetWeakPtr() override;
private:
@@ -66,13 +74,15 @@ class BoundSessionCookieRefreshServiceImpl
// `BoundSessionCookieController`.
using BoundSessionCookieControllerFactoryForTesting =
base::RepeatingCallback<std::unique_ptr<BoundSessionCookieController>(
- const GURL& url,
- const std::string& cookie_name,
+ const bound_session_credentials::BoundSessionParams&
+ bound_session_params,
+ const base::flat_set<std::string>& cookie_names,
Delegate* delegate)>;
// BoundSessionCookieRefreshService:
- void SetRendererBoundSessionParamsUpdaterDelegate(
- RendererBoundSessionParamsUpdaterDelegate renderer_updater) override;
+ void SetRendererBoundSessionThrottlerParamsUpdaterDelegate(
+ RendererBoundSessionThrottlerParamsUpdaterDelegate renderer_updater)
+ override;
void set_controller_factory_for_testing(
const BoundSessionCookieControllerFactoryForTesting&
@@ -80,35 +90,50 @@ class BoundSessionCookieRefreshServiceImpl
controller_factory_for_testing_ = controller_factory_for_testing;
}
+ void OnRegistrationRequestComplete(
+ absl::optional<bound_session_credentials::BoundSessionParams>
+ bound_session_params);
+
// BoundSessionCookieController::Delegate
- void OnCookieExpirationDateChanged() override;
+ void OnBoundSessionThrottlerParamsChanged() override;
void TerminateSession() override;
+ // StoragePartition::DataRemovalObserver:
+ void OnStorageKeyDataCleared(
+ uint32_t remove_mask,
+ content::StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
+ const base::Time begin,
+ const base::Time end) override;
+
std::unique_ptr<BoundSessionCookieController>
- CreateBoundSessionCookieController(const GURL& url,
- const std::string& cookie_name);
- void StartManagingBoundSessionCookie();
- void StopManagingBoundSessionCookie();
- void OnBoundSessionUpdated();
+ CreateBoundSessionCookieController(
+ const bound_session_credentials::BoundSessionParams& bound_session_params,
+ const base::flat_set<std::string>& cookie_names);
+ void InitializeBoundSession(
+ const bound_session_credentials::BoundSessionParams&
+ bound_session_params);
void UpdateAllRenderers();
- const raw_ptr<SigninClient> client_;
- const raw_ptr<signin::IdentityManager> identity_manager_;
+ const raw_ref<unexportable_keys::UnexportableKeyService> key_service_;
+ // Never null. Stored as `std::unique_ptr` for polymorphism.
+ const std::unique_ptr<BoundSessionParamsStorage> session_params_storage_;
+ const raw_ptr<content::StoragePartition> storage_partition_;
+ const raw_ptr<network::NetworkConnectionTracker> network_connection_tracker_;
BoundSessionCookieControllerFactoryForTesting controller_factory_for_testing_;
- RendererBoundSessionParamsUpdaterDelegate renderer_updater_;
+ RendererBoundSessionThrottlerParamsUpdaterDelegate renderer_updater_;
+
+ base::ScopedObservation<content::StoragePartition,
+ content::StoragePartition::DataRemovalObserver>
+ data_removal_observation_{this};
- std::unique_ptr<BoundSessionStateTracker> bound_session_tracker_;
std::unique_ptr<BoundSessionCookieController> cookie_controller_;
mojo::ReceiverSet<chrome::mojom::BoundSessionRequestThrottledListener>
renderer_request_throttled_listener_;
- // TODO(b/273920956): Remove when the registration flow is implemented and we
- // no longer rely on chrome signin status. Note: This is not stored on disk.
- // On next startup, the session will still be bound. This is fine as the
- // feature is still WIP.
- bool force_terminate_bound_session_ = false;
+ // There is only one active session registration at a time.
+ std::unique_ptr<BoundSessionRegistrationFetcher> active_registration_request_;
base::WeakPtrFactory<BoundSessionCookieRefreshService> weak_ptr_factory_{
this};
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl_unittest.cc
index f0be819a023..e76aeeb00f7 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl_unittest.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_impl_unittest.cc
@@ -7,48 +7,69 @@
#include <memory>
#include <utility>
-#include "base/allocator/partition_allocator/pointers/raw_ptr.h"
-#include "base/feature_list.h"
+#include "base/base64.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
+#include "base/memory/raw_ptr.h"
+#include "base/test/bind.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_controller.h"
-#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_params.pb.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_storage.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_util.h"
#include "chrome/common/renderer_configuration.mojom.h"
-#include "components/signin/public/base/account_consistency_method.h"
-#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/test_signin_client.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "google_apis/gaia/google_service_auth_error.h"
+#include "components/unexportable_keys/fake_unexportable_key_service.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/test/test_storage_partition.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-
-using signin::ConsentLevel;
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "url/gurl.h"
+#include "url/origin.h"
namespace {
-constexpr char kEmail[] = "primaryaccount@example.com";
-constexpr char kSIDTSCookieName[] = "__Secure-1PSIDTS";
-constexpr char kRegistrationParamsPref[] =
- "bound_session_credentials_registration_params";
+constexpr char k1PSIDTSCookieName[] = "__Secure-1PSIDTS";
+constexpr char k3PSIDTSCookieName[] = "__Secure-3PSIDTS";
+constexpr char kBoundSessionParamsPref[] =
+ "bound_session_credentials_bound_session_params";
+const char kSessionTerminationHeader[] = "Sec-Session-Google-Termination";
+constexpr char kWrappedKey[] = "wrapped_key";
+constexpr char kTestSessionId[] = "test_session_id";
class FakeBoundSessionCookieController : public BoundSessionCookieController {
public:
- explicit FakeBoundSessionCookieController(const GURL& url,
- const std::string& cookie_name,
- Delegate* delegate)
- : BoundSessionCookieController(url, cookie_name, delegate) {}
+ explicit FakeBoundSessionCookieController(
+ const bound_session_credentials::BoundSessionParams& bound_session_params,
+ const base::flat_set<std::string>& cookie_names,
+ Delegate* delegate)
+ : BoundSessionCookieController(bound_session_params,
+ cookie_names,
+ delegate) {
+ std::string wrapped_key_str = bound_session_params.wrapped_key();
+ wrapped_key_.assign(wrapped_key_str.begin(), wrapped_key_str.end());
+ }
~FakeBoundSessionCookieController() override {
DCHECK(on_destroy_callback_);
std::move(on_destroy_callback_).Run();
}
+ base::flat_set<std::string> cookie_names() {
+ base::flat_set<std::string> cookie_names;
+ for (const auto& [cookie_name, _] : bound_cookies_info_) {
+ cookie_names.insert(cookie_name);
+ }
+ return cookie_names;
+ }
+
+ const std::vector<uint8_t>& wrapped_key() { return wrapped_key_; }
+
void OnRequestBlockedOnCookie(
base::OnceClosure resume_blocked_request) override {
resume_blocked_requests_.push_back(std::move(resume_blocked_request));
@@ -59,9 +80,13 @@ class FakeBoundSessionCookieController : public BoundSessionCookieController {
}
void SimulateOnCookieExpirationDateChanged(
+ const std::string& cookie_name,
const base::Time& cookie_expiration_date) {
- cookie_expiration_time_ = cookie_expiration_date;
- delegate_->OnCookieExpirationDateChanged();
+ base::Time old_min_cookie_expiration_time = min_cookie_expiration_time();
+ bound_cookies_info_[cookie_name] = cookie_expiration_date;
+ if (min_cookie_expiration_time() != old_min_cookie_expiration_time) {
+ delegate_->OnBoundSessionThrottlerParamsChanged();
+ }
}
void SimulateTerminateSession() { delegate_->TerminateSession(); }
@@ -78,41 +103,27 @@ class FakeBoundSessionCookieController : public BoundSessionCookieController {
private:
base::OnceCallback<void()> on_destroy_callback_;
std::vector<base::OnceClosure> resume_blocked_requests_;
+ std::vector<uint8_t> wrapped_key_;
};
-
-bound_session_credentials::RegistrationParams CreateTestRegistrationParams() {
- bound_session_credentials::RegistrationParams params;
- params.set_site("google.com");
- params.set_session_id("test_session_id");
- return params;
-}
-
} // namespace
-class BoundSessionCookieRefreshServiceImplTest
- : public testing::TestWithParam<bool> {
+class BoundSessionCookieRefreshServiceImplTest : public testing::Test {
public:
const GURL kTestGaiaURL = GURL("https://google.com");
- BoundSessionCookieRefreshServiceImplTest()
- : identity_test_env_(&test_url_loader_factory_,
- &prefs_,
- signin::AccountConsistencyMethod::kDice) {
- scoped_feature_list_.InitWithFeatureState(kBoundSessionExplicitRegistration,
- IsExplicitRegistrationEnabled());
- BoundSessionCookieRefreshServiceImpl::RegisterProfilePrefs(
- prefs_.registry());
+ BoundSessionCookieRefreshServiceImplTest() {
+ BoundSessionParamsStorage::RegisterProfilePrefs(prefs_.registry());
}
~BoundSessionCookieRefreshServiceImplTest() override = default;
std::unique_ptr<BoundSessionCookieController> GetBoundSessionCookieController(
- const GURL& url,
- const std::string& cookie_name,
+ const bound_session_credentials::BoundSessionParams& bound_session_params,
+ const base::flat_set<std::string>& cookie_names,
BoundSessionCookieController::Delegate* delegate) {
std::unique_ptr<FakeBoundSessionCookieController> controller =
- std::make_unique<FakeBoundSessionCookieController>(url, cookie_name,
- delegate);
+ std::make_unique<FakeBoundSessionCookieController>(
+ bound_session_params, cookie_names, delegate);
controller->set_on_destroy_callback(base::BindOnce(
&BoundSessionCookieRefreshServiceImplTest::OnCookieControllerDestroy,
base::Unretained(this)));
@@ -126,7 +137,9 @@ class BoundSessionCookieRefreshServiceImplTest
if (!cookie_refresh_service_) {
cookie_refresh_service_ =
std::make_unique<BoundSessionCookieRefreshServiceImpl>(
- identity_test_env_.signin_client(), identity_manager());
+ fake_unexportable_key_service_,
+ BoundSessionParamsStorage::CreatePrefsStorageForTesting(prefs_),
+ &storage_partition_, content::GetNetworkConnectionTracker());
cookie_refresh_service_->set_controller_factory_for_testing(
base::BindRepeating(&BoundSessionCookieRefreshServiceImplTest::
GetBoundSessionCookieController,
@@ -138,27 +151,35 @@ class BoundSessionCookieRefreshServiceImplTest
void SetRendererUpdater(
BoundSessionCookieRefreshService::
- RendererBoundSessionParamsUpdaterDelegate renderer_updater) {
+ RendererBoundSessionThrottlerParamsUpdaterDelegate renderer_updater) {
CHECK(cookie_refresh_service_);
- cookie_refresh_service_->SetRendererBoundSessionParamsUpdaterDelegate(
- renderer_updater);
+ cookie_refresh_service_
+ ->SetRendererBoundSessionThrottlerParamsUpdaterDelegate(
+ renderer_updater);
}
void ResetRendererUpdater() {
CHECK(cookie_refresh_service_);
- cookie_refresh_service_->SetRendererBoundSessionParamsUpdaterDelegate(
- base::RepeatingClosure());
+ cookie_refresh_service_
+ ->SetRendererBoundSessionThrottlerParamsUpdaterDelegate(
+ base::RepeatingClosure());
}
- void ResetCookieRefreshService() { cookie_refresh_service_.reset(); }
-
- signin::IdentityManager* identity_manager() {
- return identity_test_env_.identity_manager();
+ void ClearOriginData(uint32_t remove_mask,
+ const url::Origin& origin,
+ base::Time begin = base::Time::Now(),
+ base::Time end = base::Time::Now()) {
+ CHECK(cookie_refresh_service_);
+ cookie_refresh_service_->OnStorageKeyDataCleared(
+ remove_mask,
+ base::BindLambdaForTesting(
+ [&origin](const blink::StorageKey& storage_key) {
+ return storage_key.MatchesOriginForTrustedStorageDeletion(origin);
+ }),
+ begin, end);
}
- signin::IdentityTestEnvironment* identity_test_env() {
- return &identity_test_env_;
- }
+ void ResetCookieRefreshService() { cookie_refresh_service_.reset(); }
FakeBoundSessionCookieController* cookie_controller() {
return cookie_controller_;
@@ -166,91 +187,109 @@ class BoundSessionCookieRefreshServiceImplTest
sync_preferences::TestingPrefServiceSyncable* prefs() { return &prefs_; }
+ // Emulates an existing session that resumes after `cookie_refresh_service_`
+ // is created
void SetupPreConditionForBoundSession() {
- if (IsExplicitRegistrationEnabled()) {
- bound_session_credentials::RegistrationParams params =
- CreateTestRegistrationParams();
- if (cookie_refresh_service_) {
- cookie_refresh_service_->RegisterNewBoundSession(params);
- } else {
- // Emulates an existing session that starts after
- // `cookie_refresh_service_` is created.
- prefs()->SetString(kRegistrationParamsPref, params.SerializeAsString());
- }
- } else {
- identity_test_env_.MakePrimaryAccountAvailable(kEmail,
- ConsentLevel::kSignin);
- }
+ CHECK(!cookie_refresh_service_)
+ << "If the cookie refresh service is already created, consider using "
+ "`RegisterNewBoundSession` to start a new bound session.";
+ bound_session_credentials::BoundSessionParams params =
+ CreateTestBoundSessionParams();
+ std::string bound_session_params;
+ base::Base64Encode(params.SerializeAsString(), &bound_session_params);
+ prefs()->SetString(kBoundSessionParamsPref, bound_session_params);
}
void RunUntilIdle() { task_environment_.RunUntilIdle(); }
- bool IsExplicitRegistrationEnabled() { return GetParam(); }
+ void VerifyBoundSession() {
+ CHECK(cookie_refresh_service_);
+ EXPECT_TRUE(cookie_refresh_service_->GetBoundSessionThrottlerParams());
+ EXPECT_TRUE(prefs()->HasPrefPath(kBoundSessionParamsPref));
+ EXPECT_TRUE(cookie_controller());
+ }
void VerifyNoBoundSession() {
CHECK(cookie_refresh_service_);
- EXPECT_FALSE(cookie_refresh_service_->IsBoundSession());
- EXPECT_FALSE(cookie_refresh_service_->GetBoundSessionParams());
+ EXPECT_FALSE(cookie_refresh_service_->GetBoundSessionThrottlerParams());
EXPECT_FALSE(cookie_controller());
+ EXPECT_FALSE(prefs()->HasPrefPath(kBoundSessionParamsPref));
+ }
+
+ bound_session_credentials::BoundSessionParams CreateTestBoundSessionParams() {
+ bound_session_credentials::BoundSessionParams params;
+ params.set_site(kTestGaiaURL.spec());
+ params.set_session_id(kTestSessionId);
+ params.set_wrapped_key(kWrappedKey);
+ *params.mutable_creation_time() =
+ bound_session_credentials::TimeToTimestamp(base::Time::Now());
+ return params;
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
- base::test::TaskEnvironment task_environment_;
+ base::test::TaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
sync_preferences::TestingPrefServiceSyncable prefs_;
- network::TestURLLoaderFactory test_url_loader_factory_;
- signin::IdentityTestEnvironment identity_test_env_;
+ content::TestStoragePartition storage_partition_;
std::unique_ptr<BoundSessionCookieRefreshServiceImpl> cookie_refresh_service_;
+ unexportable_keys::FakeUnexportableKeyService fake_unexportable_key_service_;
raw_ptr<FakeBoundSessionCookieController> cookie_controller_ = nullptr;
};
-TEST_P(BoundSessionCookieRefreshServiceImplTest, VerifyControllerParams) {
+TEST_F(BoundSessionCookieRefreshServiceImplTest, VerifyControllerParams) {
SetupPreConditionForBoundSession();
- BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
+ GetCookieRefreshServiceImpl();
+ VerifyBoundSession();
FakeBoundSessionCookieController* controller = cookie_controller();
EXPECT_TRUE(controller);
EXPECT_EQ(controller->url(), kTestGaiaURL);
- EXPECT_EQ(controller->cookie_name(), kSIDTSCookieName);
- EXPECT_EQ(controller->cookie_expiration_time(), base::Time());
+ EXPECT_EQ(controller->session_id(), kTestSessionId);
+ EXPECT_EQ(
+ controller->cookie_names(),
+ base::flat_set<std::string>({k1PSIDTSCookieName, k3PSIDTSCookieName}));
+ EXPECT_EQ(controller->min_cookie_expiration_time(), base::Time());
+ EXPECT_EQ(controller->wrapped_key(),
+ std::vector<uint8_t>(std::begin(kWrappedKey),
+ // Omit `\0`.
+ std::end(kWrappedKey) - 1));
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
- VerifyBoundSessionParamsUnboundSession) {
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ VerifyBoundSessionThrottlerParamsUnboundSession) {
GetCookieRefreshServiceImpl();
VerifyNoBoundSession();
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
- VerifyBoundSessionParamsBoundSession) {
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ VerifyBoundSessionThrottlerParamsBoundSession) {
SetupPreConditionForBoundSession();
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
+ ASSERT_TRUE(cookie_controller());
- chrome::mojom::BoundSessionParamsPtr bound_session_params =
- service->GetBoundSessionParams();
- EXPECT_EQ(bound_session_params->domain, kTestGaiaURL.host());
- EXPECT_EQ(bound_session_params->path, kTestGaiaURL.path_piece());
+ chrome::mojom::BoundSessionThrottlerParamsPtr bound_session_throttler_params =
+ service->GetBoundSessionThrottlerParams();
+ EXPECT_EQ(bound_session_throttler_params->domain, kTestGaiaURL.host());
+ EXPECT_EQ(bound_session_throttler_params->path, kTestGaiaURL.path_piece());
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
RefreshBoundSessionCookieBoundSession) {
SetupPreConditionForBoundSession();
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
+ EXPECT_TRUE(cookie_controller());
base::test::TestFuture<void> future;
service->OnRequestBlockedOnCookie(future.GetCallback());
- EXPECT_TRUE(cookie_controller());
EXPECT_FALSE(future.IsReady());
cookie_controller()->SimulateRefreshBoundSessionCompleted();
EXPECT_TRUE(future.IsReady());
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
RefreshBoundSessionCookieUnboundSession) {
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_FALSE(service->IsBoundSession());
+ EXPECT_FALSE(cookie_controller());
// Unbound session, the callback should be called immediately.
base::test::TestFuture<void> future;
@@ -258,10 +297,11 @@ TEST_P(BoundSessionCookieRefreshServiceImplTest,
EXPECT_TRUE(future.IsReady());
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
UpdateAllRenderersOnBoundSessionStarted) {
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_FALSE(service->IsBoundSession());
+ EXPECT_FALSE(cookie_controller());
+ EXPECT_FALSE(service->GetBoundSessionThrottlerParams());
base::MockRepeatingCallback<void()> renderer_updater;
EXPECT_CALL(renderer_updater, Run()).Times(0);
SetRendererUpdater(renderer_updater.Get());
@@ -269,38 +309,44 @@ TEST_P(BoundSessionCookieRefreshServiceImplTest,
// Create bound session.
EXPECT_CALL(renderer_updater, Run()).WillOnce([&] {
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_FALSE(service->GetBoundSessionParams().is_null());
+ EXPECT_TRUE(cookie_controller());
+ EXPECT_FALSE(service->GetBoundSessionThrottlerParams().is_null());
});
- SetupPreConditionForBoundSession();
+ service->RegisterNewBoundSession(CreateTestBoundSessionParams());
testing::Mock::VerifyAndClearExpectations(&renderer_updater);
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
- UpdateAllRenderersOnCookieExpirationDateChanged) {
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ UpdateAllRenderersOnBoundSessionThrottlerParamsChanged) {
base::MockRepeatingCallback<void()> renderer_updater;
EXPECT_CALL(renderer_updater, Run()).Times(0);
SetupPreConditionForBoundSession();
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
+ EXPECT_TRUE(cookie_controller());
SetRendererUpdater(renderer_updater.Get());
testing::Mock::VerifyAndClearExpectations(&renderer_updater);
+ EXPECT_CALL(renderer_updater, Run()).Times(0);
+ cookie_controller()->SimulateOnCookieExpirationDateChanged(k1PSIDTSCookieName,
+ base::Time::Now());
+ testing::Mock::VerifyAndClearExpectations(&renderer_updater);
+
EXPECT_CALL(renderer_updater, Run()).WillOnce([&] {
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_FALSE(service->GetBoundSessionParams().is_null());
+ EXPECT_TRUE(cookie_controller());
+ EXPECT_FALSE(service->GetBoundSessionThrottlerParams().is_null());
});
- cookie_controller()->SimulateOnCookieExpirationDateChanged(base::Time::Now());
+ cookie_controller()->SimulateOnCookieExpirationDateChanged(k3PSIDTSCookieName,
+ base::Time::Now());
testing::Mock::VerifyAndClearExpectations(&renderer_updater);
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
UpdateAllRenderersOnBoundSessionTerminated) {
base::MockRepeatingCallback<void()> renderer_updater;
EXPECT_CALL(renderer_updater, Run()).Times(0);
SetupPreConditionForBoundSession();
- BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
+ GetCookieRefreshServiceImpl();
+ EXPECT_TRUE(cookie_controller());
SetRendererUpdater(renderer_updater.Get());
testing::Mock::VerifyAndClearExpectations(&renderer_updater);
@@ -311,31 +357,62 @@ TEST_P(BoundSessionCookieRefreshServiceImplTest,
testing::Mock::VerifyAndClearExpectations(&renderer_updater);
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest, TerminateSession) {
+TEST_F(BoundSessionCookieRefreshServiceImplTest, TerminateSession) {
SetupPreConditionForBoundSession();
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_TRUE(service->GetBoundSessionParams());
+ EXPECT_TRUE(service->GetBoundSessionThrottlerParams());
cookie_controller()->SimulateTerminateSession();
VerifyNoBoundSession();
- if (IsExplicitRegistrationEnabled()) {
- // Verify prefs were cleared.
- // Ensure on next startup, there won't be a bound session.
- ResetCookieRefreshService();
- service = GetCookieRefreshServiceImpl();
+ // Verify prefs were cleared.
+ // Ensure on next startup, there won't be a bound session.
+ ResetCookieRefreshService();
+ service = GetCookieRefreshServiceImpl();
+
+ SCOPED_TRACE("No bound session on Startup.");
+ VerifyNoBoundSession();
+}
+
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ TerminateSessionOnSessionTerminationHeader) {
+ SetupPreConditionForBoundSession();
+ scoped_refptr<net::HttpResponseHeaders> headers =
+ base::MakeRefCounted<net::HttpResponseHeaders>("");
+ headers->AddHeader(kSessionTerminationHeader, kTestSessionId);
+ BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
+ service->MaybeTerminateSession(headers.get());
+ VerifyNoBoundSession();
+}
- SCOPED_TRACE("No bound session on Startup.");
- VerifyNoBoundSession();
- }
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ DontTerminateSessionSessionIdsMismatch) {
+ SetupPreConditionForBoundSession();
+ scoped_refptr<net::HttpResponseHeaders> headers =
+ base::MakeRefCounted<net::HttpResponseHeaders>("");
+ headers->AddHeader(kSessionTerminationHeader, "different_session_id");
+
+ BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
+ service->MaybeTerminateSession(headers.get());
+ VerifyBoundSession();
+}
+
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ DontTerminateSessionWithoutSessionTerminationHeader) {
+ SetupPreConditionForBoundSession();
+ scoped_refptr<net::HttpResponseHeaders> headers =
+ base::MakeRefCounted<net::HttpResponseHeaders>("");
+
+ BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
+ service->MaybeTerminateSession(headers.get());
+ VerifyBoundSession();
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
AddBoundSessionRequestThrottledListenerReceivers) {
SetupPreConditionForBoundSession();
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
+ ASSERT_TRUE(cookie_controller());
mojo::Remote<chrome::mojom::BoundSessionRequestThrottledListener> listener_1;
mojo::Remote<chrome::mojom::BoundSessionRequestThrottledListener> listener_2;
service->AddBoundSessionRequestThrottledListenerReceiver(
@@ -357,142 +434,91 @@ TEST_P(BoundSessionCookieRefreshServiceImplTest,
EXPECT_TRUE(future_2.Wait());
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
- IsBoundSessionNoPrimaryAccount) {
- if (IsExplicitRegistrationEnabled()) {
- GTEST_SKIP();
- }
- EXPECT_FALSE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));
+TEST_F(BoundSessionCookieRefreshServiceImplTest, RegisterNewBoundSession) {
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_FALSE(service->IsBoundSession());
-}
+ VerifyNoBoundSession();
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
- IsBoundSessionSigninPrimaryAccount) {
- if (IsExplicitRegistrationEnabled()) {
- GTEST_SKIP();
- }
- SetupPreConditionForBoundSession();
- EXPECT_TRUE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));
- BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
- identity_test_env()->WaitForRefreshTokensLoaded();
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_TRUE(cookie_controller());
+ service->RegisterNewBoundSession(CreateTestBoundSessionParams());
+ VerifyBoundSession();
+ // TODO(http://b/286222327): check bound session params once they are
+ // properly passed to controller.
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
- IsBoundSessionAccountsNotLoadedYet) {
- if (IsExplicitRegistrationEnabled()) {
- GTEST_SKIP();
- }
- SetupPreConditionForBoundSession();
- EXPECT_TRUE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));
- identity_test_env()->ResetToAccountsNotYetLoadedFromDiskState();
+TEST_F(BoundSessionCookieRefreshServiceImplTest, OverrideExistingBoundSession) {
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_TRUE(cookie_controller());
+ service->RegisterNewBoundSession(CreateTestBoundSessionParams());
+
+ auto new_params = CreateTestBoundSessionParams();
+ new_params.set_session_id("test_session_id_2");
+ service->RegisterNewBoundSession(new_params);
+
+ VerifyBoundSession();
+ // TODO(http://b/286222327): check bound session params once they are
+ // properly passed to controller.
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
- IsBoundSessionRefreshTokenInPersistentErrorState) {
- if (IsExplicitRegistrationEnabled()) {
- GTEST_SKIP();
- }
- SetupPreConditionForBoundSession();
- EXPECT_TRUE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ OverrideExistingBoundSessionWithInvalidParams) {
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_TRUE(cookie_controller());
+ service->RegisterNewBoundSession(CreateTestBoundSessionParams());
- identity_test_env()->UpdatePersistentErrorOfRefreshTokenForAccount(
- identity_manager()->GetPrimaryAccountId(ConsentLevel::kSignin),
- GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
- GoogleServiceAuthError::InvalidGaiaCredentialsReason::
- CREDENTIALS_REJECTED_BY_CLIENT));
- VerifyNoBoundSession();
+ auto invalid_params = CreateTestBoundSessionParams();
+ invalid_params.clear_session_id();
+ service->RegisterNewBoundSession(invalid_params);
- identity_test_env()->ResetToAccountsNotYetLoadedFromDiskState();
- ResetCookieRefreshService();
- service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_TRUE(cookie_controller());
-
- identity_test_env()->ReloadAccountsFromDisk();
- identity_test_env()->WaitForRefreshTokensLoaded();
- VerifyNoBoundSession();
+ // Original session should not be modified.
+ VerifyBoundSession();
+ // TODO(http://b/286222327): check bound session params once they are
+ // properly passed to controller.
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
- IsBoundSessionOnPrimaryAccountChanged) {
- if (IsExplicitRegistrationEnabled()) {
- GTEST_SKIP();
- }
+TEST_F(BoundSessionCookieRefreshServiceImplTest, ClearMatchingData) {
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- identity_test_env()->WaitForRefreshTokensLoaded();
- VerifyNoBoundSession();
-
- // `MakeAccountAvailable()` is used to ensure the primary account has
- // already
- // a refresh token when `OnPrimaryAccountChanged()` is fired.
- CoreAccountId account_id =
- identity_test_env()->MakeAccountAvailable(kEmail).account_id;
- EXPECT_FALSE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));
- identity_test_env()->SetPrimaryAccount(kEmail, ConsentLevel::kSignin);
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_TRUE(cookie_controller());
+ service->RegisterNewBoundSession(CreateTestBoundSessionParams());
- identity_test_env()->ClearPrimaryAccount();
+ ClearOriginData(content::StoragePartition::REMOVE_DATA_MASK_COOKIES,
+ url::Origin::Create(kTestGaiaURL));
VerifyNoBoundSession();
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest,
- IsBoundSessionEmptyGaiaAccounts) {
- if (IsExplicitRegistrationEnabled()) {
- GTEST_SKIP();
- }
- SetupPreConditionForBoundSession();
- EXPECT_TRUE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ ClearMatchingDataTypeMismatch) {
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_TRUE(cookie_controller());
+ service->RegisterNewBoundSession(CreateTestBoundSessionParams());
- identity_test_env()->SetCookieAccounts({});
- VerifyNoBoundSession();
+ ClearOriginData(content::StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE,
+ url::Origin::Create(kTestGaiaURL));
+ VerifyBoundSession();
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest, RegisterNewBoundSession) {
- if (!IsExplicitRegistrationEnabled()) {
- GTEST_SKIP();
- }
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ ClearMatchingDataOriginMismatch) {
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- EXPECT_FALSE(service->IsBoundSession());
- EXPECT_FALSE(cookie_controller());
+ service->RegisterNewBoundSession(CreateTestBoundSessionParams());
- service->RegisterNewBoundSession(CreateTestRegistrationParams());
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_TRUE(cookie_controller());
- // TODO(http://b/286222327): check registration params once they are properly
- // passed to controller.
+ ClearOriginData(content::StoragePartition::REMOVE_DATA_MASK_COOKIES,
+ url::Origin::Create(GURL("https://example.org")));
+ VerifyBoundSession();
}
-TEST_P(BoundSessionCookieRefreshServiceImplTest, OverrideExistingBoundSession) {
- if (!IsExplicitRegistrationEnabled()) {
- GTEST_SKIP();
- }
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ ClearMatchingDataOriginMismatchSuborigin) {
BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
- service->RegisterNewBoundSession(CreateTestRegistrationParams());
-
- auto new_params = CreateTestRegistrationParams();
- new_params.set_session_id("test_session_id_2");
- service->RegisterNewBoundSession(new_params);
+ service->RegisterNewBoundSession(CreateTestBoundSessionParams());
- EXPECT_TRUE(service->IsBoundSession());
- EXPECT_TRUE(cookie_controller());
- // TODO(http://b/286222327): check registration params once they are properly
- // passed to controller.
+ ClearOriginData(content::StoragePartition::REMOVE_DATA_MASK_COOKIES,
+ url::Origin::Create(GURL("https://accounts.google.com")));
+ VerifyBoundSession();
}
-INSTANTIATE_TEST_SUITE_P(,
- BoundSessionCookieRefreshServiceImplTest,
- testing::Bool());
+TEST_F(BoundSessionCookieRefreshServiceImplTest,
+ ClearMatchingDataCreationTimeMismatch) {
+ BoundSessionCookieRefreshServiceImpl* service = GetCookieRefreshServiceImpl();
+ service->RegisterNewBoundSession(CreateTestBoundSessionParams());
+
+ ClearOriginData(content::StoragePartition::REMOVE_DATA_MASK_COOKIES,
+ url::Origin::Create(kTestGaiaURL),
+ base::Time::Now() - base::Seconds(5),
+ base::Time::Now() - base::Seconds(3));
+ VerifyBoundSession();
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_params.proto b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params.proto
index 76fe09f172d..86236cd4e8e 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_params.proto
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params.proto
@@ -25,8 +25,13 @@ message Credential {
}
}
+message Timestamp {
+ // Represents microseconds since the Windows epoch.
+ required int64 microseconds = 1;
+}
+
// Set of parameters for a single device bound session.
-message RegistrationParams {
+message BoundSessionParams {
// Site on which the bound session is running.
// The protocol is currently in a prototype state and only supports
// "google.com".
@@ -37,4 +42,6 @@ message RegistrationParams {
optional bytes wrapped_key = 3;
// List of credentials that the browser needs to refresh periodically.
repeated Credential credentials = 4;
+ // Creation time of the session.
+ optional Timestamp creation_time = 5;
}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage.cc
new file mode 100644
index 00000000000..a4bc3e353e2
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage.cc
@@ -0,0 +1,156 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_storage.h"
+
+#include "base/base64.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace {
+
+const char kBoundSessionParamsPref[] =
+ "bound_session_credentials_bound_session_params";
+
+class BoundSessionParamsPrefsStorage : public BoundSessionParamsStorage {
+ public:
+ explicit BoundSessionParamsPrefsStorage(PrefService& pref_service);
+ ~BoundSessionParamsPrefsStorage() override;
+
+ [[nodiscard]] bool SaveParams(
+ const bound_session_credentials::BoundSessionParams& params) override;
+
+ absl::optional<bound_session_credentials::BoundSessionParams> ReadParams()
+ const override;
+
+ void ClearParams() override;
+
+ private:
+ const raw_ref<PrefService> pref_service_;
+};
+
+BoundSessionParamsPrefsStorage::BoundSessionParamsPrefsStorage(
+ PrefService& pref_service)
+ : pref_service_(pref_service) {}
+
+BoundSessionParamsPrefsStorage::~BoundSessionParamsPrefsStorage() = default;
+
+bool BoundSessionParamsPrefsStorage::SaveParams(
+ const bound_session_credentials::BoundSessionParams& params) {
+ if (!AreParamsValid(params)) {
+ return false;
+ }
+
+ std::string serialized_params = params.SerializeAsString();
+ if (serialized_params.empty()) {
+ return false;
+ }
+
+ std::string encoded_serialized_params;
+ base::Base64Encode(serialized_params, &encoded_serialized_params);
+ pref_service_->SetString(kBoundSessionParamsPref, encoded_serialized_params);
+ return true;
+}
+
+absl::optional<bound_session_credentials::BoundSessionParams>
+BoundSessionParamsPrefsStorage::ReadParams() const {
+ std::string encoded_params_str =
+ pref_service_->GetString(kBoundSessionParamsPref);
+ if (encoded_params_str.empty()) {
+ return absl::nullopt;
+ }
+
+ std::string params_str;
+ if (!base::Base64Decode(encoded_params_str, &params_str)) {
+ return absl::nullopt;
+ }
+
+ bound_session_credentials::BoundSessionParams params;
+ if (params.ParseFromString(params_str) && AreParamsValid(params)) {
+ return params;
+ }
+ return absl::nullopt;
+}
+
+void BoundSessionParamsPrefsStorage::ClearParams() {
+ pref_service_->ClearPref(kBoundSessionParamsPref);
+}
+
+class BoundSessionParamsInMemoryStorage : public BoundSessionParamsStorage {
+ public:
+ BoundSessionParamsInMemoryStorage();
+ ~BoundSessionParamsInMemoryStorage() override;
+
+ [[nodiscard]] bool SaveParams(
+ const bound_session_credentials::BoundSessionParams& params) override;
+
+ absl::optional<bound_session_credentials::BoundSessionParams> ReadParams()
+ const override;
+
+ void ClearParams() override;
+
+ private:
+ absl::optional<bound_session_credentials::BoundSessionParams>
+ in_memory_params_;
+};
+
+BoundSessionParamsInMemoryStorage::BoundSessionParamsInMemoryStorage() =
+ default;
+BoundSessionParamsInMemoryStorage::~BoundSessionParamsInMemoryStorage() =
+ default;
+
+bool BoundSessionParamsInMemoryStorage::SaveParams(
+ const bound_session_credentials::BoundSessionParams& params) {
+ if (!AreParamsValid(params)) {
+ return false;
+ }
+
+ in_memory_params_ = params;
+ return true;
+}
+
+absl::optional<bound_session_credentials::BoundSessionParams>
+BoundSessionParamsInMemoryStorage::ReadParams() const {
+ return in_memory_params_;
+}
+
+void BoundSessionParamsInMemoryStorage::ClearParams() {
+ in_memory_params_ = absl::nullopt;
+}
+
+} // namespace
+
+// static
+std::unique_ptr<BoundSessionParamsStorage>
+BoundSessionParamsStorage::CreateForProfile(Profile& profile) {
+ if (profile.IsOffTheRecord()) {
+ return std::make_unique<BoundSessionParamsInMemoryStorage>();
+ }
+ return std::make_unique<BoundSessionParamsPrefsStorage>(*profile.GetPrefs());
+}
+
+// static
+std::unique_ptr<BoundSessionParamsStorage>
+BoundSessionParamsStorage::CreatePrefsStorageForTesting(
+ PrefService& pref_service) {
+ return std::make_unique<BoundSessionParamsPrefsStorage>(pref_service);
+}
+
+// static
+void BoundSessionParamsStorage::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterStringPref(kBoundSessionParamsPref, std::string());
+}
+
+// static
+bool BoundSessionParamsStorage::AreParamsValid(
+ const bound_session_credentials::BoundSessionParams& bound_session_params) {
+ // TODO(crbug.com/1441168): Check for validity of other fields once they are
+ // available.
+ return bound_session_params.has_session_id() &&
+ bound_session_params.has_wrapped_key();
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage.h
new file mode 100644
index 00000000000..58e0161ce3a
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage.h
@@ -0,0 +1,67 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_PARAMS_STORAGE_H_
+#define CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_PARAMS_STORAGE_H_
+
+#include <memory>
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class Profile;
+class PrefService;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+// Stores bound session parameters.
+//
+// Depending on the profile type, either
+// - stores the parameters in memory if a profile is off-the-record, or
+// - stores the parameters on disk, in user prefs, otherwise
+//
+// Currently, supports only a single simultaneous session.
+class BoundSessionParamsStorage {
+ public:
+ BoundSessionParamsStorage() = default;
+
+ BoundSessionParamsStorage(const BoundSessionParamsStorage&) = delete;
+ BoundSessionParamsStorage& operator=(const BoundSessionParamsStorage&) =
+ delete;
+
+ virtual ~BoundSessionParamsStorage() = default;
+
+ // Creates a new storage instance for `profile`.
+ static std::unique_ptr<BoundSessionParamsStorage> CreateForProfile(
+ Profile& profile);
+
+ // Allows tests to create a prefs-backed storage without creating a testing
+ // profile.
+ static std::unique_ptr<BoundSessionParamsStorage>
+ CreatePrefsStorageForTesting(PrefService& pref_service);
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ static bool AreParamsValid(
+ const bound_session_credentials::BoundSessionParams&
+ bound_session_params);
+
+ // Saves `params` to storage. Overwrites existing params if any. `params` are
+ // verified before being saved.
+ // Returns whether the new parameters were saved. In case of a failure, keeps
+ // the existing value intact.
+ [[nodiscard]] virtual bool SaveParams(
+ const bound_session_credentials::BoundSessionParams& params) = 0;
+
+ // Returns currently stored parameters if any.
+ virtual absl::optional<bound_session_credentials::BoundSessionParams>
+ ReadParams() const = 0;
+
+ // Cleans the storage.
+ virtual void ClearParams() = 0;
+};
+
+#endif // CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_PARAMS_STORAGE_H_
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage_unittest.cc
new file mode 100644
index 00000000000..eab6a8facdd
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_storage_unittest.cc
@@ -0,0 +1,176 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_storage.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace {
+
+bound_session_credentials::BoundSessionParams CreateValidBoundSessionParams() {
+ bound_session_credentials::BoundSessionParams params;
+ params.set_session_id("123");
+ params.set_site("https://example.org");
+ params.set_wrapped_key("456");
+ return params;
+}
+
+bound_session_credentials::BoundSessionParams
+CreateInvalidBoundSessionParams() {
+ bound_session_credentials::BoundSessionParams params;
+ // Leaves a required `session_id` field empty.
+ return params;
+}
+
+MATCHER_P(EqualsProto, message, "") {
+ return message.SerializeAsString() == arg.SerializeAsString();
+}
+
+} // namespace
+
+TEST(BoundSessionParamsStorageAreParamsValidTest, Valid) {
+ EXPECT_TRUE(BoundSessionParamsStorage::AreParamsValid(
+ CreateValidBoundSessionParams()));
+}
+
+TEST(BoundSessionParamsStorageAreParamsValidTest, InvalidMissingSessionId) {
+ bound_session_credentials::BoundSessionParams params =
+ CreateValidBoundSessionParams();
+ params.clear_session_id();
+ EXPECT_FALSE(BoundSessionParamsStorage::AreParamsValid(params));
+}
+
+TEST(BoundSessionParamsStorageAreParamsValidTest, InvalidMissingWrappedKey) {
+ bound_session_credentials::BoundSessionParams params =
+ CreateValidBoundSessionParams();
+ params.clear_wrapped_key();
+ EXPECT_FALSE(BoundSessionParamsStorage::AreParamsValid(params));
+}
+
+class BoundSessionParamsStorageTest : public testing::TestWithParam<bool> {
+ public:
+ BoundSessionParamsStorageTest() : storage_(CreateStorage()) {}
+
+ bool IsOffTheRecord() { return GetParam(); }
+
+ BoundSessionParamsStorage& storage() { return *storage_; }
+
+ void ResetStorage() { storage_ = CreateStorage(); }
+
+ private:
+ std::unique_ptr<BoundSessionParamsStorage> CreateStorage() {
+ return BoundSessionParamsStorage::CreateForProfile(
+ IsOffTheRecord()
+ ? *profile_.GetPrimaryOTRProfile(/*create_if_needed=*/true)
+ : profile_);
+ }
+
+ content::BrowserTaskEnvironment task_environment_;
+ TestingProfile profile_;
+ std::unique_ptr<BoundSessionParamsStorage> storage_;
+};
+
+TEST_P(BoundSessionParamsStorageTest, InitiallyEmpty) {
+ EXPECT_EQ(storage().ReadParams(), absl::nullopt);
+}
+
+TEST_P(BoundSessionParamsStorageTest, SaveAndRead) {
+ bound_session_credentials::BoundSessionParams params =
+ CreateValidBoundSessionParams();
+ ASSERT_TRUE(storage().SaveParams(params));
+ EXPECT_THAT(storage().ReadParams(), testing::Optional(EqualsProto(params)));
+}
+
+TEST_P(BoundSessionParamsStorageTest, SaveInvalidParams) {
+ EXPECT_FALSE(storage().SaveParams(CreateInvalidBoundSessionParams()));
+ EXPECT_EQ(storage().ReadParams(), absl::nullopt);
+}
+
+TEST_P(BoundSessionParamsStorageTest, OverwriteWithValidParams) {
+ ASSERT_TRUE(storage().SaveParams(CreateValidBoundSessionParams()));
+ bound_session_credentials::BoundSessionParams new_params =
+ CreateValidBoundSessionParams();
+ new_params.set_session_id("unique_id");
+ EXPECT_TRUE(storage().SaveParams(new_params));
+ EXPECT_THAT(storage().ReadParams(),
+ testing::Optional(EqualsProto(new_params)));
+}
+
+TEST_P(BoundSessionParamsStorageTest, OverwriteWithInvalidParams) {
+ bound_session_credentials::BoundSessionParams valid_params =
+ CreateValidBoundSessionParams();
+ ASSERT_TRUE(storage().SaveParams(valid_params));
+ EXPECT_FALSE(storage().SaveParams(CreateInvalidBoundSessionParams()));
+ EXPECT_THAT(storage().ReadParams(),
+ testing::Optional(EqualsProto(valid_params)));
+}
+
+TEST_P(BoundSessionParamsStorageTest, Clear) {
+ ASSERT_TRUE(storage().SaveParams(CreateValidBoundSessionParams()));
+ storage().ClearParams();
+ EXPECT_EQ(storage().ReadParams(), absl::nullopt);
+}
+
+TEST_P(BoundSessionParamsStorageTest, Persistence) {
+ bound_session_credentials::BoundSessionParams params =
+ CreateValidBoundSessionParams();
+ ASSERT_TRUE(storage().SaveParams(params));
+ EXPECT_TRUE(storage().ReadParams().has_value());
+
+ ResetStorage();
+
+ if (IsOffTheRecord()) {
+ EXPECT_EQ(storage().ReadParams(), absl::nullopt);
+ } else {
+ EXPECT_THAT(storage().ReadParams(), testing::Optional(EqualsProto(params)));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+ BoundSessionParamsStorageTest,
+ testing::Bool(),
+ [](const auto& info) {
+ return info.param ? "OTR" : "Persistent";
+ });
+
+class BoundSessionParamsStorageOTRTest : public testing::Test {
+ public:
+ TestingProfile& parent_profile() { return profile_; }
+
+ private:
+ content::BrowserTaskEnvironment task_environment_;
+ TestingProfile profile_;
+};
+
+// Tests that an OTR profile storage isn't affected by the contents of the
+// parent storage.
+TEST_F(BoundSessionParamsStorageOTRTest, NoInheritance) {
+ std::unique_ptr<BoundSessionParamsStorage> parent_storage =
+ BoundSessionParamsStorage::CreateForProfile(parent_profile());
+ bound_session_credentials::BoundSessionParams params =
+ CreateValidBoundSessionParams();
+ ASSERT_TRUE(parent_storage->SaveParams(params));
+ EXPECT_TRUE(parent_storage->ReadParams().has_value());
+
+ std::unique_ptr<BoundSessionParamsStorage> otr_storage =
+ BoundSessionParamsStorage::CreateForProfile(
+ *parent_profile().GetPrimaryOTRProfile(/*create_if_needed=*/true));
+ EXPECT_EQ(otr_storage->ReadParams(), absl::nullopt);
+ bound_session_credentials::BoundSessionParams params2 =
+ CreateValidBoundSessionParams();
+ params2.set_session_id("otr_session");
+ ASSERT_TRUE(otr_storage->SaveParams(params2));
+ EXPECT_THAT(otr_storage->ReadParams(),
+ testing::Optional(EqualsProto(params2)));
+
+ // Parent storage hasn't changed.
+ EXPECT_THAT(parent_storage->ReadParams(),
+ testing::Optional(EqualsProto(params)));
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util.cc
new file mode 100644
index 00000000000..be317db7cea
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util.cc
@@ -0,0 +1,23 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_util.h"
+
+#include "base/time/time.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+
+namespace bound_session_credentials {
+
+Timestamp TimeToTimestamp(base::Time time) {
+ Timestamp timestamp = Timestamp();
+ timestamp.set_microseconds(time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ return timestamp;
+}
+
+base::Time TimestampToTime(const Timestamp& timestamp) {
+ return base::Time::FromDeltaSinceWindowsEpoch(
+ base::Microseconds(timestamp.microseconds()));
+}
+
+} // namespace bound_session_credentials
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util.h
new file mode 100644
index 00000000000..aa2fa4b9225
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util.h
@@ -0,0 +1,19 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_PARAMS_UTIL_H_
+#define CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_PARAMS_UTIL_H_
+
+#include "base/time/time.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+
+namespace bound_session_credentials {
+
+Timestamp TimeToTimestamp(base::Time time);
+
+base::Time TimestampToTime(const Timestamp& timestamp);
+
+} // namespace bound_session_credentials
+
+#endif // CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_PARAMS_UTIL_H_
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util_unittest.cc
new file mode 100644
index 00000000000..c29fb25effc
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_params_util_unittest.cc
@@ -0,0 +1,18 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_util.h"
+
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace bound_session_credentials {
+
+TEST(BoundSessionParamsUtilTest, Timestamp) {
+ base::Time time =
+ base::Time::UnixEpoch() + base::Milliseconds(987984); // arbitrary
+ EXPECT_EQ(TimestampToTime(TimeToTimestamp(time)), time);
+}
+
+} // namespace bound_session_credentials
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.cc
index 47b3cb8bf81..737fbf0ab06 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.cc
@@ -4,6 +4,22 @@
#include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h"
+// static
+bool BoundSessionRefreshCookieFetcher::IsPersistentError(Result result) {
+ switch (result) {
+ case Result::kSuccess:
+ case Result::kConnectionError:
+ case Result::kServerTransientError:
+ return false;
+ case Result::kServerPersistentError:
+ case Result::kServerUnexepectedResponse:
+ case Result::kChallengeRequiredUnexpectedFormat:
+ case Result::kChallengeRequiredLimitExceeded:
+ case Result::kSignChallengeFailed:
+ return true;
+ }
+}
+
std::ostream& operator<<(
std::ostream& os,
const BoundSessionRefreshCookieFetcher::Result& result) {
@@ -18,5 +34,15 @@ std::ostream& operator<<(
case BoundSessionRefreshCookieFetcher::Result::kServerPersistentError:
return os << "Cookie rotation request finished with Server Persistent "
"error.";
+ case BoundSessionRefreshCookieFetcher::Result::kServerUnexepectedResponse:
+ return os << "Cookie rotation request didn't set the expected cookies.";
+ case BoundSessionRefreshCookieFetcher::Result::
+ kChallengeRequiredUnexpectedFormat:
+ return os << "Challenge required unexpected format.";
+ case BoundSessionRefreshCookieFetcher::Result::
+ kChallengeRequiredLimitExceeded:
+ return os << "Challenge required limit exceeded.";
+ case BoundSessionRefreshCookieFetcher::Result::kSignChallengeFailed:
+ return os << "Sign challenge failed on cookie rotation request.";
}
}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h
index 9df13f5085a..e77239107cf 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h
@@ -8,21 +8,28 @@
#include <ostream>
#include "base/functional/callback_forward.h"
-#include "net/base/net_errors.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
// This class makes the network request to the Gaia cookie rotation endpoint to
// refresh bound Google authentication cookies. A new fetcher instance should be
// created per request.
class BoundSessionRefreshCookieFetcher {
public:
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
enum class Result {
kSuccess = 0,
kConnectionError = 1,
kServerTransientError = 2,
kServerPersistentError = 3,
+ kServerUnexepectedResponse = 4,
+ kChallengeRequiredUnexpectedFormat = 5,
+ kChallengeRequiredLimitExceeded = 6,
+ kSignChallengeFailed = 7,
+ kMaxValue = kSignChallengeFailed,
};
+ static bool IsPersistentError(Result result);
+
// Reports the result of the fetch request.
using RefreshCookieCompleteCallback = base::OnceCallback<void(Result)>;
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc
index a6f038b1583..d35c2c13703 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.cc
@@ -6,19 +6,58 @@
#include <memory>
-#include "components/signin/public/base/signin_client.h"
+#include "base/functional/bind.h"
+#include "base/location.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "chrome/browser/signin/bound_session_credentials/session_binding_helper.h"
+#include "components/signin/public/base/wait_for_network_callback_helper.h"
#include "google_apis/gaia/gaia_urls.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "net/cookies/canonical_cookie.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/cookie_access_observer.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+namespace {
+constexpr char kRotationChallengeHeader[] = "Sec-Session-Google-Challenge";
+constexpr char kRotationChallengeResponseHeader[] =
+ "Sec-Session-Google-Response";
+constexpr char kChallengeItemKey[] = "challenge";
+const size_t kMaxAssertionRequestsAllowed = 5;
+
+bool IsExpectedCookie(
+ const GURL& url,
+ const std::string& cookie_name,
+ const network::mojom::CookieOrLineWithAccessResultPtr& cookie_ptr) {
+ if (cookie_ptr->access_result.status.IsInclude()) {
+ CHECK(cookie_ptr->cookie_or_line->is_cookie());
+ const net::CanonicalCookie& cookie =
+ cookie_ptr->cookie_or_line->get_cookie();
+ return (cookie.Name() == cookie_name) && cookie.IsDomainMatch(url.host());
+ }
+ return false;
+}
+} // namespace
+
BoundSessionRefreshCookieFetcherImpl::BoundSessionRefreshCookieFetcherImpl(
- SigninClient* client)
- : client_(client), url_loader_factory_(client->GetURLLoaderFactory()) {}
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ WaitForNetworkCallbackHelper& wait_for_network_callback_helper,
+ SessionBindingHelper& session_binding_helper,
+ const GURL& cookie_url,
+ base::flat_set<std::string> cookie_names)
+ : url_loader_factory_(std::move(url_loader_factory)),
+ wait_for_network_callback_helper_(wait_for_network_callback_helper),
+ session_binding_helper_(session_binding_helper),
+ expected_cookie_domain_(cookie_url),
+ expected_cookie_names_(std::move(cookie_names)) {}
BoundSessionRefreshCookieFetcherImpl::~BoundSessionRefreshCookieFetcherImpl() =
default;
@@ -28,12 +67,13 @@ void BoundSessionRefreshCookieFetcherImpl::Start(
CHECK(!callback_);
CHECK(callback);
callback_ = std::move(callback);
- client_->DelayNetworkCall(
+ wait_for_network_callback_helper_->DelayNetworkCall(
base::BindOnce(&BoundSessionRefreshCookieFetcherImpl::StartRefreshRequest,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr(), absl::nullopt));
}
-void BoundSessionRefreshCookieFetcherImpl::StartRefreshRequest() {
+void BoundSessionRefreshCookieFetcherImpl::StartRefreshRequest(
+ absl::optional<std::string> sec_session_challenge_response) {
// TODO(b/273920907): Update the `traffic_annotation` setting once a mechanism
// allowing the user to disable the feature is implemented.
net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -64,8 +104,11 @@ void BoundSessionRefreshCookieFetcherImpl::StartRefreshRequest() {
cookies_allowed: YES
cookies_store: "user"
setting:
- "This feature is under development and must be enabled by user"
- " action."
+ "This is a new feature being developed behind a flag that is"
+ " disabled by default (kEnableBoundSessionCredentials). This"
+ " request will only be sent if the feature is enabled and once"
+ " a server requests it with a special header."
+
policy_exception_justification:
"Not implemented. "
"If the feature is on, this request must be made to ensure the user"
@@ -77,12 +120,21 @@ void BoundSessionRefreshCookieFetcherImpl::StartRefreshRequest() {
request->url = GaiaUrls::GetInstance()->rotate_bound_cookies_url();
request->method = "GET";
+ if (sec_session_challenge_response) {
+ request->headers.SetHeader(kRotationChallengeResponseHeader,
+ *sec_session_challenge_response);
+ }
+
url::Origin origin = GaiaUrls::GetInstance()->gaia_origin();
request->site_for_cookies = net::SiteForCookies::FromOrigin(origin);
request->trusted_params = network::ResourceRequest::TrustedParams();
request->trusted_params->isolation_info =
net::IsolationInfo::CreateForInternalRequest(origin);
+ mojo::PendingRemote<network::mojom::CookieAccessObserver> remote;
+ cookie_observers_.Add(this, remote.InitWithNewPipeAndPassReceiver());
+ request->trusted_params->cookie_observer = std::move(remote);
+
// TODO(b/273920907): Figure out how to handle redirects. Currently
// `network::SimpleURLLoader::SetOnRedirectCallback()` doesn't support
// modifying the headers nor asynchronously resuming the reguest.
@@ -103,10 +155,34 @@ void BoundSessionRefreshCookieFetcherImpl::OnURLLoaderComplete(
scoped_refptr<net::HttpResponseHeaders> headers) {
net::Error net_error = static_cast<net::Error>(url_loader_->NetError());
- Result result = GetResultFromNetErrorAndHttpStatusCode(
+ absl::optional<std::string> challenge_header_value =
+ GetChallengeIfBindingKeyAssertionRequired(headers);
+ if (challenge_header_value) {
+ HandleBindingKeyAssertionRequired(*challenge_header_value);
+ return;
+ }
+
+ cookie_refresh_completed_ = true;
+ result_ = GetResultFromNetErrorAndHttpStatusCode(
net_error,
headers ? absl::optional<int>(headers->response_code()) : absl::nullopt);
- std::move(callback_).Run(result);
+
+ if (result_ == Result::kSuccess && !reported_cookies_notified_) {
+ // Normally, a cookie update notification should be sent before the request
+ // is complete. Add some leeway in the case mojo messages are delivered out
+ // of order.
+ const base::TimeDelta kResponseCookiesNotifiedMaxDelay =
+ base::Milliseconds(100);
+ // `base::Unretained` is safe as `this` owns
+ // `reported_cookies_notified_timer_`.
+ reported_cookies_notified_timer_.Start(
+ FROM_HERE, kResponseCookiesNotifiedMaxDelay,
+ base::BindOnce(
+ &BoundSessionRefreshCookieFetcherImpl::ReportRefreshResult,
+ base::Unretained(this)));
+ } else {
+ ReportRefreshResult();
+ }
}
BoundSessionRefreshCookieFetcher::Result
@@ -116,23 +192,149 @@ BoundSessionRefreshCookieFetcherImpl::GetResultFromNetErrorAndHttpStatusCode(
if ((net_error != net::OK &&
net_error != net::ERR_HTTP_RESPONSE_CODE_FAILURE) ||
!response_code) {
- return BoundSessionRefreshCookieFetcher::Result::kConnectionError;
+ return Result::kConnectionError;
}
if (response_code == net::HTTP_OK) {
- return BoundSessionRefreshCookieFetcher::Result::kSuccess;
+ return Result::kSuccess;
}
if (response_code >= net::HTTP_INTERNAL_SERVER_ERROR) {
// Server error 5xx.
- return BoundSessionRefreshCookieFetcher::Result::kServerTransientError;
+ return Result::kServerTransientError;
}
if (response_code >= net::HTTP_BAD_REQUEST) {
// Server error 4xx.
- return BoundSessionRefreshCookieFetcher::Result::kServerPersistentError;
+ return Result::kServerPersistentError;
}
// Unexpected response code.
- return BoundSessionRefreshCookieFetcher::Result::kServerPersistentError;
+ return Result::kServerPersistentError;
+}
+
+void BoundSessionRefreshCookieFetcherImpl::ReportRefreshResult() {
+ reported_cookies_notified_timer_.Stop();
+ CHECK(cookie_refresh_completed_);
+ if (result_ == Result::kSuccess && !expected_cookies_set_) {
+ result_ = Result::kServerUnexepectedResponse;
+ }
+ base::UmaHistogramEnumeration(
+ "Signin.BoundSessionCredentials.CookieRotationResult", result_);
+ std::move(callback_).Run(result_);
+}
+
+absl::optional<std::string>
+BoundSessionRefreshCookieFetcherImpl::GetChallengeIfBindingKeyAssertionRequired(
+ const scoped_refptr<net::HttpResponseHeaders>& headers) const {
+ if (!headers || headers->response_code() != net::HTTP_UNAUTHORIZED ||
+ !headers->HasHeader(kRotationChallengeHeader)) {
+ return absl::nullopt;
+ }
+
+ std::string challenge;
+ headers->GetNormalizedHeader(kRotationChallengeHeader, &challenge);
+ return challenge;
+}
+
+void BoundSessionRefreshCookieFetcherImpl::HandleBindingKeyAssertionRequired(
+ const std::string& challenge_header_value) {
+ if (assertion_requests_count_ >= kMaxAssertionRequestsAllowed) {
+ CompleteRequestAndReportRefreshResult(
+ Result::kChallengeRequiredLimitExceeded);
+ return;
+ }
+
+ // Binding key assertion required.
+ assertion_requests_count_++;
+ std::string challenge = ParseChallengeHeader(challenge_header_value);
+ if (challenge.empty()) {
+ CompleteRequestAndReportRefreshResult(
+ Result::kChallengeRequiredUnexpectedFormat);
+ return;
+ }
+ RefreshWithChallenge(challenge);
+}
+
+// static
+std::string BoundSessionRefreshCookieFetcherImpl::ParseChallengeHeader(
+ const std::string& header) {
+ base::StringPairs items;
+ base::SplitStringIntoKeyValuePairs(header, '=', ';', &items);
+ std::string challenge;
+ for (const auto& [key, value] : items) {
+ // TODO(b/293838716): Check `session_id` matches the current session's id.
+ if (base::EqualsCaseInsensitiveASCII(key, kChallengeItemKey)) {
+ challenge = value;
+ }
+ }
+
+ if (!base::IsStringUTF8AllowingNoncharacters(challenge)) {
+ DVLOG(1) << "Server-side challenge has non-UTF8 characters.";
+ return std::string();
+ }
+ return challenge;
+}
+
+void BoundSessionRefreshCookieFetcherImpl::
+ CompleteRequestAndReportRefreshResult(Result result) {
+ cookie_refresh_completed_ = true;
+ result_ = result;
+ ReportRefreshResult();
+}
+
+void BoundSessionRefreshCookieFetcherImpl::RefreshWithChallenge(
+ const std::string& challenge) {
+ session_binding_helper_->GenerateBindingKeyAssertion(
+ challenge, GaiaUrls::GetInstance()->rotate_bound_cookies_url(),
+ base::BindOnce(
+ &BoundSessionRefreshCookieFetcherImpl::OnGenerateBindingKeyAssertion,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BoundSessionRefreshCookieFetcherImpl::OnGenerateBindingKeyAssertion(
+ std::string assertion) {
+ if (assertion.empty()) {
+ CompleteRequestAndReportRefreshResult(Result::kSignChallengeFailed);
+ return;
+ }
+ wait_for_network_callback_helper_->DelayNetworkCall(
+ base::BindOnce(&BoundSessionRefreshCookieFetcherImpl::StartRefreshRequest,
+ weak_ptr_factory_.GetWeakPtr(), std::move(assertion)));
+}
+
+void BoundSessionRefreshCookieFetcherImpl::OnCookiesAccessed(
+ std::vector<network::mojom::CookieAccessDetailsPtr> details_vector) {
+ for (const auto& cookie_details : details_vector) {
+ if (cookie_details->type !=
+ network::mojom::CookieAccessDetails::Type::kChange) {
+ continue;
+ }
+
+ reported_cookies_notified_ = true;
+ bool all_cookies_set = true;
+ for (const std::string& expected_cookie_name : expected_cookie_names_) {
+ auto it = base::ranges::find_if(
+ cookie_details->cookie_list,
+ [this, &expected_cookie_name](
+ const network::mojom::CookieOrLineWithAccessResultPtr& cookie) {
+ return IsExpectedCookie(expected_cookie_domain_,
+ expected_cookie_name, cookie);
+ });
+ if (it == cookie_details->cookie_list.end()) {
+ all_cookies_set = false;
+ break;
+ }
+ }
+ expected_cookies_set_ = expected_cookies_set_ || all_cookies_set;
+ }
+
+ if (cookie_refresh_completed_ && reported_cookies_notified_) {
+ ReportRefreshResult();
+ }
+}
+
+void BoundSessionRefreshCookieFetcherImpl::Clone(
+ mojo::PendingReceiver<network::mojom::CookieAccessObserver> observer) {
+ cookie_observers_.Add(this, std::move(observer));
}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h
index 52943f0194c..7a9612c23e0 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl.h
@@ -7,46 +7,102 @@
#include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h"
+#include <cstddef>
#include <memory>
-#include "base/functional/callback.h"
+#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "net/cookies/canonical_cookie.h"
#include "services/network/public/cpp/simple_url_loader.h"
-#include "url/gurl.h"
+#include "services/network/public/mojom/cookie_access_observer.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace network {
class SimpleURLLoader;
class SharedURLLoaderFactory;
} // namespace network
-class SigninClient;
+class WaitForNetworkCallbackHelper;
+class SessionBindingHelper;
class BoundSessionRefreshCookieFetcherImpl
- : public BoundSessionRefreshCookieFetcher {
+ : public BoundSessionRefreshCookieFetcher,
+ public network::mojom::CookieAccessObserver {
public:
- explicit BoundSessionRefreshCookieFetcherImpl(SigninClient* client);
+ BoundSessionRefreshCookieFetcherImpl(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ WaitForNetworkCallbackHelper& wait_for_network_callback_helper,
+ SessionBindingHelper& session_binding_helper,
+ const GURL& cookie_url,
+ base::flat_set<std::string> cookie_names);
~BoundSessionRefreshCookieFetcherImpl() override;
// BoundSessionRefreshCookieFetcher:
void Start(RefreshCookieCompleteCallback callback) override;
private:
+ friend class BoundSessionRefreshCookieFetcherImplTest;
FRIEND_TEST_ALL_PREFIXES(BoundSessionRefreshCookieFetcherImplTest,
GetResultFromNetErrorAndHttpStatusCode);
+ FRIEND_TEST_ALL_PREFIXES(BoundSessionRefreshCookieFetcherImplTest,
+ OnCookiesAccessedRead);
+ FRIEND_TEST_ALL_PREFIXES(BoundSessionRefreshCookieFetcherImplTest,
+ OnCookiesAccessedChange);
+ FRIEND_TEST_ALL_PREFIXES(
+ BoundSessionRefreshCookieFetcherImplParseChallengeHeaderTest,
+ ParseChallengeHeader);
+
+ // Returns empty if parsing challenge header failed. Otherwise, returns the
+ // decoded challenge field value.
+ static std::string ParseChallengeHeader(const std::string& header);
- void StartRefreshRequest();
+ void StartRefreshRequest(
+ absl::optional<std::string> sec_session_challenge_response);
void OnURLLoaderComplete(scoped_refptr<net::HttpResponseHeaders> headers);
Result GetResultFromNetErrorAndHttpStatusCode(
net::Error net_error,
absl::optional<int> response_code);
+ void ReportRefreshResult();
+
+ // Returns `absl::nullopt` if assertion isn't required.
+ absl::optional<std::string> GetChallengeIfBindingKeyAssertionRequired(
+ const scoped_refptr<net::HttpResponseHeaders>& headers) const;
+ void HandleBindingKeyAssertionRequired(
+ const std::string& challenge_header_value);
+ void CompleteRequestAndReportRefreshResult(Result result);
+ void RefreshWithChallenge(const std::string& challenge);
+ void OnGenerateBindingKeyAssertion(std::string assertion);
+
+ // network::mojom::CookieAccessObserver:
+ void OnCookiesAccessed(std::vector<network::mojom::CookieAccessDetailsPtr>
+ details_vector) override;
+ void Clone(mojo::PendingReceiver<network::mojom::CookieAccessObserver>
+ observer) override;
- const raw_ptr<SigninClient> client_;
const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+ const raw_ref<WaitForNetworkCallbackHelper> wait_for_network_callback_helper_;
+ const raw_ref<SessionBindingHelper> session_binding_helper_;
+
+ // Used to check whether the refresh request has set the required cookie.
+ // Otherwise, the request is considered a failure.
+ const GURL expected_cookie_domain_;
+ const base::flat_set<std::string> expected_cookie_names_;
+
RefreshCookieCompleteCallback callback_;
+ bool expected_cookies_set_ = false;
+ base::OneShotTimer reported_cookies_notified_timer_;
+ bool reported_cookies_notified_ = false;
+
+ // Refresh request result.
+ Result result_;
+ bool cookie_refresh_completed_ = false;
+ size_t assertion_requests_count_ = 0;
+
// Non-null after a fetch has started.
std::unique_ptr<network::SimpleURLLoader> url_loader_;
-
+ mojo::ReceiverSet<network::mojom::CookieAccessObserver> cookie_observers_;
base::WeakPtrFactory<BoundSessionRefreshCookieFetcherImpl> weak_ptr_factory_{
this};
};
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
index 36ae64f83e4..a081dc10e7d 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher_impl_unittest.cc
@@ -7,90 +7,480 @@
#include <memory>
#include <string>
+#include "base/base64url.h"
+#include "base/containers/span.h"
+#include "base/json/json_reader.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h"
+#include "chrome/browser/signin/bound_session_credentials/session_binding_helper.h"
+#include "components/signin/public/base/session_binding_test_utils.h"
+#include "components/signin/public/base/session_binding_utils.h"
#include "components/signin/public/base/test_signin_client.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/unexportable_keys/service_error.h"
+#include "components/unexportable_keys/unexportable_key_id.h"
+#include "components/unexportable_keys/unexportable_key_service_impl.h"
+#include "components/unexportable_keys/unexportable_key_task_manager.h"
+#include "crypto/scoped_mock_unexportable_key_provider.h"
#include "net/base/net_errors.h"
+#include "net/cookies/canonical_cookie.h"
#include "net/http/http_status_code.h"
+#include "services/network/public/mojom/cookie_access_observer.mojom.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace {
+using RefreshTestFuture =
+ base::test::TestFuture<BoundSessionRefreshCookieFetcher::Result>;
+using Result = BoundSessionRefreshCookieFetcher::Result;
+using testing::ElementsAre;
+using unexportable_keys::BackgroundTaskPriority;
+using unexportable_keys::ServiceErrorOr;
+using unexportable_keys::UnexportableKeyId;
+using unexportable_keys::UnexportableKeyService;
+
+constexpr char kSessionId[] = "session_id";
+constexpr char kChallenge[] = "aGVsbG8_d29ybGQ";
+
+UnexportableKeyId GenerateNewKey(
+ UnexportableKeyService& unexportable_key_service) {
+ base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>> generate_future;
+ unexportable_key_service.GenerateSigningKeySlowlyAsync(
+ base::span<const crypto::SignatureVerifier::SignatureAlgorithm>(
+ {crypto::SignatureVerifier::ECDSA_SHA256}),
+ BackgroundTaskPriority::kUserBlocking, generate_future.GetCallback());
+ ServiceErrorOr<UnexportableKeyId> key_id = generate_future.Get();
+ CHECK(key_id.has_value());
+ return *key_id;
+}
+
+std::string GetChallengeFromJwt(std::string_view jwt) {
+ std::vector<base::StringPiece> parts = base::SplitStringPiece(
+ jwt, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ if (parts.size() != 3) {
+ return std::string();
+ }
+
+ std::string payload;
+ if (!base::Base64UrlDecode(
+ parts[1], base::Base64UrlDecodePolicy::DISALLOW_PADDING, &payload)) {
+ return std::string();
+ }
+ absl::optional<base::Value::Dict> payload_dict =
+ base::JSONReader::ReadDict(payload);
+ if (!payload_dict) {
+ return std::string();
+ }
+ std::string* challenge = payload_dict->FindString("jti");
+ return challenge ? *challenge : std::string();
+}
+
+std::string CreateChallengeHeaderValue(const std::string& challenge) {
+ return base::StringPrintf("session-id=%s; challenge=%s", kSessionId,
+ challenge.c_str());
+}
+} // namespace
class BoundSessionRefreshCookieFetcherImplTest : public ::testing::Test {
public:
- BoundSessionRefreshCookieFetcherImplTest() {
- test_url_loader_factory_ = signin_client_.GetTestURLLoaderFactory();
- fetcher_ =
- std::make_unique<BoundSessionRefreshCookieFetcherImpl>(&signin_client_);
+ BoundSessionRefreshCookieFetcherImplTest()
+ : unexportable_key_service_(unexportable_key_task_manager_) {
+ binding_key_id_ = GenerateNewKey(unexportable_key_service_);
+ session_binding_helper_ = std::make_unique<SessionBindingHelper>(
+ unexportable_key_service_,
+ *unexportable_key_service_.GetWrappedKey(binding_key_id_), kSessionId);
+ fetcher_ = std::make_unique<BoundSessionRefreshCookieFetcherImpl>(
+ test_url_loader_factory_.GetSafeWeakWrapper(),
+ wait_for_network_callback_helper_, *session_binding_helper_, kGairaUrl,
+ base::flat_set<std::string>{k1PSIDTSCookieName, k3PSIDTSCookieName});
+ UpdateCookieList();
}
protected:
- base::test::TaskEnvironment task_environment_;
- sync_preferences::TestingPrefServiceSyncable prefs_;
- TestSigninClient signin_client_{&prefs_};
- raw_ptr<network::TestURLLoaderFactory> test_url_loader_factory_ = nullptr;
+ const GURL kGairaUrl = GURL("https://google.com/");
+ const std::string k1PSIDTSCookieName = "__Secure-1PSIDTS";
+ const std::string k3PSIDTSCookieName = "__Secure-3PSIDTS";
+
+ void UpdateCookieList(
+ const base::flat_set<std::string>& excluded_cookies = {}) {
+ const std::string kCookiesNames[4] = {
+ "__Secure-cookie1", k1PSIDTSCookieName, "__Secure-cookie2",
+ k3PSIDTSCookieName};
+ cookies_.clear();
+ for (const auto& cookie_name : kCookiesNames) {
+ if (excluded_cookies.contains(cookie_name)) {
+ continue;
+ }
+ cookies_.emplace_back(
+ BoundSessionTestCookieManager::CreateCookie(kGairaUrl, cookie_name));
+ }
+ }
+
+ std::vector<network::mojom::CookieOrLineWithAccessResultPtr>
+ CreateReportedCookies(const net::CookieList& cookie_list) {
+ std::vector<network::mojom::CookieOrLineWithAccessResultPtr>
+ reported_cookies;
+ for (auto cookie : cookie_list) {
+ network::mojom::CookieOrLinePtr cookie_or_line =
+ network::mojom::CookieOrLine::NewCookie(cookie);
+ reported_cookies.push_back(
+ network::mojom::CookieOrLineWithAccessResult::New(
+ std::move(cookie_or_line), net::CookieAccessResult()));
+ }
+ return reported_cookies;
+ }
+
+ void SimulateChallengeRequired(const std::string& challenge_header) {
+ EXPECT_EQ(test_url_loader_factory_.NumPending(), 1);
+ network::TestURLLoaderFactory::PendingRequest* pending_request =
+ test_url_loader_factory_.GetPendingRequest(0);
+ network::URLLoaderCompletionStatus ok_completion_status(net::OK);
+ auto response = network::CreateURLResponseHead(net::HTTP_UNAUTHORIZED);
+
+ response->headers->AddHeader("Sec-Session-Google-Challenge",
+ challenge_header);
+ EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest(
+ pending_request->request.url, ok_completion_status, std::move(response),
+ ""));
+ }
+
+ void SimulateOnCookiesAccessed(
+ network::mojom::CookieAccessDetails::Type access_type) {
+ std::vector<network::mojom::CookieAccessDetailsPtr> cookie_access_details;
+ cookie_access_details.emplace_back(network::mojom::CookieAccessDetails::New(
+ access_type, kGairaUrl, net::SiteForCookies(),
+ CreateReportedCookies(cookies_), absl::nullopt));
+ fetcher_->OnCookiesAccessed(std::move(cookie_access_details));
+ }
+
+ bool reported_cookies_notified() {
+ return fetcher_->reported_cookies_notified_;
+ }
+
+ bool expected_cookies_set() { return fetcher_->expected_cookies_set_; }
+
+ void VerifyMetricRecorded(
+ BoundSessionRefreshCookieFetcher::Result expected_result) {
+ EXPECT_THAT(histogram_tester_.GetAllSamples(
+ "Signin.BoundSessionCredentials.CookieRotationResult"),
+ ElementsAre(base::Bucket(expected_result, /*count=*/1)));
+ }
+
+ base::test::TaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ crypto::ScopedMockUnexportableKeyProvider scoped_key_provider_;
+ unexportable_keys::UnexportableKeyTaskManager unexportable_key_task_manager_;
+ unexportable_keys::UnexportableKeyServiceImpl unexportable_key_service_;
+ UnexportableKeyId binding_key_id_;
+ std::unique_ptr<SessionBindingHelper> session_binding_helper_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ TestWaitForNetworkCallbackHelper wait_for_network_callback_helper_;
std::unique_ptr<BoundSessionRefreshCookieFetcherImpl> fetcher_;
+ net::CookieList cookies_;
+ base::HistogramTester histogram_tester_;
};
-TEST_F(BoundSessionRefreshCookieFetcherImplTest, Success) {
- EXPECT_FALSE(signin_client_.AreNetworkCallsDelayed());
- base::test::TestFuture<BoundSessionRefreshCookieFetcher::Result> future;
+TEST_F(BoundSessionRefreshCookieFetcherImplTest, SuccessExpectedCookieSet) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
fetcher_->Start(future.GetCallback());
- EXPECT_EQ(test_url_loader_factory_->total_requests(), 1u);
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
network::TestURLLoaderFactory::PendingRequest* pending_request =
- test_url_loader_factory_->GetPendingRequest(0);
+ test_url_loader_factory_.GetPendingRequest(0);
EXPECT_EQ(pending_request->request.url,
"https://accounts.google.com/RotateBoundCookies");
EXPECT_EQ(pending_request->request.method, "GET");
EXPECT_EQ(pending_request->request.credentials_mode,
network::mojom::CredentialsMode::kInclude);
- test_url_loader_factory_->SimulateResponseForPendingRequest(
+ SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kChange);
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_TRUE(reported_cookies_notified());
+ EXPECT_TRUE(expected_cookies_set());
+
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
pending_request->request.url.spec(), "");
- EXPECT_TRUE(future.Wait());
- BoundSessionRefreshCookieFetcher::Result result = future.Get();
- EXPECT_EQ(result, BoundSessionRefreshCookieFetcher::Result::kSuccess);
+
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_EQ(future.Get(), Result::kSuccess);
+ VerifyMetricRecorded(Result::kSuccess);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+ SuccessCookiesReportedDelayed) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
+ network::TestURLLoaderFactory::PendingRequest* pending_request =
+ test_url_loader_factory_.GetPendingRequest(0);
+
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
+ pending_request->request.url.spec(), "");
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_FALSE(reported_cookies_notified());
+
+ SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kChange);
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_TRUE(reported_cookies_notified());
+ EXPECT_TRUE(expected_cookies_set());
+
+ EXPECT_EQ(future.Get(), Result::kSuccess);
+ VerifyMetricRecorded(Result::kSuccess);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+ ResultNotReportedOnCookieRead) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
+ network::TestURLLoaderFactory::PendingRequest* pending_request =
+ test_url_loader_factory_.GetPendingRequest(0);
+
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
+ pending_request->request.url.spec(), "");
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_FALSE(reported_cookies_notified());
+
+ SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kRead);
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_FALSE(reported_cookies_notified());
+
+ SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kChange);
+ EXPECT_EQ(future.Get(), Result::kSuccess);
+ VerifyMetricRecorded(Result::kSuccess);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest, CookiesNotReported) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
+ network::TestURLLoaderFactory::PendingRequest* pending_request =
+ test_url_loader_factory_.GetPendingRequest(0);
+
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
+ pending_request->request.url.spec(), "");
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_FALSE(reported_cookies_notified());
+
+ task_environment_.FastForwardBy(base::Milliseconds(100));
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_FALSE(reported_cookies_notified());
+ EXPECT_EQ(future.Get(), Result::kServerUnexepectedResponse);
+ VerifyMetricRecorded(Result::kServerUnexepectedResponse);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+ CookiesReportedExpectedCookieNotSet) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
+ network::TestURLLoaderFactory::PendingRequest* pending_request =
+ test_url_loader_factory_.GetPendingRequest(0);
+
+ UpdateCookieList(
+ /*excluded_cookies=*/{k1PSIDTSCookieName, k3PSIDTSCookieName});
+ SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kChange);
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_TRUE(reported_cookies_notified());
+ EXPECT_FALSE(expected_cookies_set());
+
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
+ pending_request->request.url.spec(), "");
+
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_EQ(future.Get(), Result::kServerUnexepectedResponse);
+ VerifyMetricRecorded(Result::kServerUnexepectedResponse);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+ CookiesReportedNotAllExpectedCookiesSet) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
+ network::TestURLLoaderFactory::PendingRequest* pending_request =
+ test_url_loader_factory_.GetPendingRequest(0);
+
+ // Remove one of the expected cookies
+ UpdateCookieList(/*excluded_cookies=*/{k3PSIDTSCookieName});
+ SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kChange);
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_TRUE(reported_cookies_notified());
+ EXPECT_FALSE(expected_cookies_set());
+
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
+ pending_request->request.url.spec(), "");
+
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_EQ(future.Get(), Result::kServerUnexepectedResponse);
+ VerifyMetricRecorded(Result::kServerUnexepectedResponse);
}
TEST_F(BoundSessionRefreshCookieFetcherImplTest, FailureNetError) {
- EXPECT_FALSE(signin_client_.AreNetworkCallsDelayed());
- base::test::TestFuture<BoundSessionRefreshCookieFetcher::Result> future;
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
fetcher_->Start(future.GetCallback());
- EXPECT_EQ(test_url_loader_factory_->total_requests(), 1u);
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
network::TestURLLoaderFactory::PendingRequest* pending_request =
- test_url_loader_factory_->GetPendingRequest(0);
+ test_url_loader_factory_.GetPendingRequest(0);
network::URLLoaderCompletionStatus status(net::ERR_UNEXPECTED);
- test_url_loader_factory_->SimulateResponseForPendingRequest(
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
pending_request->request.url, status,
network::mojom::URLResponseHead::New(), std::string());
- EXPECT_TRUE(future.Wait());
- BoundSessionRefreshCookieFetcher::Result result = future.Get();
- EXPECT_EQ(result, BoundSessionRefreshCookieFetcher::Result::kConnectionError);
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_FALSE(reported_cookies_notified());
+ BoundSessionRefreshCookieFetcher::Result result = future.Get<0>();
+ EXPECT_EQ(result, Result::kConnectionError);
+ VerifyMetricRecorded(Result::kConnectionError);
}
TEST_F(BoundSessionRefreshCookieFetcherImplTest, FailureHttpError) {
- EXPECT_FALSE(signin_client_.AreNetworkCallsDelayed());
- base::test::TestFuture<BoundSessionRefreshCookieFetcher::Result> future;
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
fetcher_->Start(future.GetCallback());
- EXPECT_EQ(test_url_loader_factory_->total_requests(), 1u);
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
network::TestURLLoaderFactory::PendingRequest* pending_request =
- test_url_loader_factory_->GetPendingRequest(0);
+ test_url_loader_factory_.GetPendingRequest(0);
- test_url_loader_factory_->SimulateResponseForPendingRequest(
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
pending_request->request.url.spec(), "", net::HTTP_UNAUTHORIZED);
- EXPECT_TRUE(future.Wait());
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_FALSE(reported_cookies_notified());
BoundSessionRefreshCookieFetcher::Result result = future.Get();
- EXPECT_EQ(result,
- BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
+ EXPECT_EQ(result, Result::kServerPersistentError);
+ VerifyMetricRecorded(Result::kServerPersistentError);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest, ChallengeRequired) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+
+ SimulateChallengeRequired(CreateChallengeHeaderValue(kChallenge));
+ task_environment_.RunUntilIdle();
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_EQ(test_url_loader_factory_.NumPending(), 1);
+ network::TestURLLoaderFactory::PendingRequest* pending_request =
+ test_url_loader_factory_.GetPendingRequest(0);
+ auto headers = pending_request->request.headers;
+ std::string assertion;
+ EXPECT_TRUE(headers.GetHeader("Sec-Session-Google-Response", &assertion));
+
+ EXPECT_TRUE(signin::VerifyJwtSignature(
+ assertion, *unexportable_key_service_.GetAlgorithm(binding_key_id_),
+ *unexportable_key_service_.GetSubjectPublicKeyInfo(binding_key_id_)));
+ EXPECT_EQ(GetChallengeFromJwt(assertion), kChallenge);
+
+ // Set required cookies and complete the request.
+ SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kChange);
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
+ pending_request->request.url.spec(), "");
+
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_EQ(future.Get(), Result::kSuccess);
+ VerifyMetricRecorded(Result::kSuccess);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+ ChallengeRequiredNonUTF8Characters) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+
+ SimulateChallengeRequired(CreateChallengeHeaderValue("\xF0\x8F\xBF\xBE"));
+ EXPECT_EQ(future.Get(), Result::kChallengeRequiredUnexpectedFormat);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+ BadChallengeHeaderFormatEmpty) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+ SimulateChallengeRequired("");
+ EXPECT_EQ(future.Get(), Result::kChallengeRequiredUnexpectedFormat);
+ VerifyMetricRecorded(Result::kChallengeRequiredUnexpectedFormat);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+ BadChallengeHeaderFormatChallengeMissing) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+ SimulateChallengeRequired("session_id=12345;");
+ EXPECT_EQ(future.Get(), Result::kChallengeRequiredUnexpectedFormat);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+ BadChallengeHeaderFormatChallengeFieldEmpty) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+ SimulateChallengeRequired(CreateChallengeHeaderValue(""));
+ EXPECT_EQ(future.Get(), Result::kChallengeRequiredUnexpectedFormat);
+ VerifyMetricRecorded(Result::kChallengeRequiredUnexpectedFormat);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest,
+ AssertionRequestsLimitExceeded) {
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+
+ size_t assertion_requests = 0;
+ const size_t max_assertion_requests_allowed = 5;
+ do {
+ SimulateChallengeRequired(CreateChallengeHeaderValue(kChallenge));
+ task_environment_.RunUntilIdle();
+ assertion_requests++;
+ ASSERT_EQ(future.IsReady(),
+ assertion_requests > max_assertion_requests_allowed);
+ } while (!future.IsReady());
+ EXPECT_EQ(future.Get(), Result::kChallengeRequiredLimitExceeded);
+ VerifyMetricRecorded(Result::kChallengeRequiredLimitExceeded);
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest, SignChallengeFailed) {
+ // Use fake wrapped key.
+ std::vector<uint8_t> wrapped_key{1, 2, 3, 4, 5};
+ fetcher_.reset();
+ session_binding_helper_ = std::make_unique<SessionBindingHelper>(
+ unexportable_key_service_, wrapped_key, kSessionId);
+ fetcher_ = std::make_unique<BoundSessionRefreshCookieFetcherImpl>(
+ test_url_loader_factory_.GetSafeWeakWrapper(),
+ wait_for_network_callback_helper_, *session_binding_helper_, kGairaUrl,
+ base::flat_set<std::string>{k1PSIDTSCookieName, k3PSIDTSCookieName});
+ ASSERT_FALSE(wait_for_network_callback_helper_.AreNetworkCallsDelayed());
+ RefreshTestFuture future;
+ fetcher_->Start(future.GetCallback());
+
+ SimulateChallengeRequired(CreateChallengeHeaderValue(kChallenge));
+ EXPECT_EQ(future.Get(), Result::kSignChallengeFailed);
+ VerifyMetricRecorded(Result::kSignChallengeFailed);
}
TEST_F(BoundSessionRefreshCookieFetcherImplTest,
@@ -98,45 +488,78 @@ TEST_F(BoundSessionRefreshCookieFetcherImplTest,
// Connection error.
EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
net::ERR_CONNECTION_TIMED_OUT, absl::nullopt),
- BoundSessionRefreshCookieFetcher::Result::kConnectionError);
+ Result::kConnectionError);
// net::OK.
EXPECT_EQ(
fetcher_->GetResultFromNetErrorAndHttpStatusCode(net::OK, net::HTTP_OK),
- BoundSessionRefreshCookieFetcher::Result::kSuccess);
+ Result::kSuccess);
// net::ERR_HTTP_RESPONSE_CODE_FAILURE
EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
net::ERR_HTTP_RESPONSE_CODE_FAILURE, net::HTTP_BAD_REQUEST),
- BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
+ Result::kServerPersistentError);
// Persistent error.
EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
net::OK, net::HTTP_BAD_REQUEST),
- BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
+ Result::kServerPersistentError);
EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
net::OK, net::HTTP_NOT_FOUND),
- BoundSessionRefreshCookieFetcher::Result::kServerPersistentError);
+ Result::kServerPersistentError);
// Transient error.
EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
net::OK, net::HTTP_INTERNAL_SERVER_ERROR),
- BoundSessionRefreshCookieFetcher::Result::kServerTransientError);
+ Result::kServerTransientError);
EXPECT_EQ(fetcher_->GetResultFromNetErrorAndHttpStatusCode(
net::OK, net::HTTP_GATEWAY_TIMEOUT),
- BoundSessionRefreshCookieFetcher::Result::kServerTransientError);
+ Result::kServerTransientError);
}
TEST_F(BoundSessionRefreshCookieFetcherImplTest, NetworkDelayed) {
- signin_client_.SetNetworkCallsDelayed(true);
- base::test::TestFuture<BoundSessionRefreshCookieFetcher::Result> future;
+ wait_for_network_callback_helper_.SetNetworkCallsDelayed(true);
+ RefreshTestFuture future;
fetcher_->Start(future.GetCallback());
- EXPECT_EQ(test_url_loader_factory_->total_requests(), 0u);
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 0u);
- signin_client_.SetNetworkCallsDelayed(false);
- EXPECT_EQ(test_url_loader_factory_->total_requests(), 1u);
+ wait_for_network_callback_helper_.SetNetworkCallsDelayed(false);
+ EXPECT_EQ(test_url_loader_factory_.total_requests(), 1u);
network::TestURLLoaderFactory::PendingRequest* pending_request =
- test_url_loader_factory_->GetPendingRequest(0);
+ test_url_loader_factory_.GetPendingRequest(0);
EXPECT_EQ(pending_request->request.url,
"https://accounts.google.com/RotateBoundCookies");
- test_url_loader_factory_->SimulateResponseForPendingRequest(
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
pending_request->request.url.spec(), "");
EXPECT_TRUE(future.Wait());
}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest, OnCookiesAccessedRead) {
+ EXPECT_FALSE(reported_cookies_notified());
+ SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kRead);
+ EXPECT_FALSE(reported_cookies_notified());
+ EXPECT_FALSE(expected_cookies_set());
+}
+
+TEST_F(BoundSessionRefreshCookieFetcherImplTest, OnCookiesAccessedChange) {
+ EXPECT_FALSE(reported_cookies_notified());
+ SimulateOnCookiesAccessed(network::mojom::CookieAccessDetails::Type::kChange);
+ EXPECT_TRUE(reported_cookies_notified());
+ EXPECT_TRUE(expected_cookies_set());
+}
+
+TEST(BoundSessionRefreshCookieFetcherImplParseChallengeHeaderTest,
+ ParseChallengeHeader) {
+ // Empty header.
+ EXPECT_EQ(BoundSessionRefreshCookieFetcherImpl::ParseChallengeHeader(""), "");
+ EXPECT_EQ(BoundSessionRefreshCookieFetcherImpl::ParseChallengeHeader("xyz"),
+ "");
+ // Non-UTF8 characters.
+ EXPECT_EQ(BoundSessionRefreshCookieFetcherImpl::ParseChallengeHeader(
+ CreateChallengeHeaderValue("\xF0\x8F\xBF\xBE")),
+ "");
+ // Empty challenge field.
+ EXPECT_EQ(BoundSessionRefreshCookieFetcherImpl::ParseChallengeHeader(
+ CreateChallengeHeaderValue("")),
+ "");
+ EXPECT_EQ(BoundSessionRefreshCookieFetcherImpl::ParseChallengeHeader(
+ CreateChallengeHeaderValue(kChallenge)),
+ kChallenge);
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher.h
new file mode 100644
index 00000000000..2697846f8cf
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher.h
@@ -0,0 +1,30 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_REGISTRATION_FETCHER_H_
+#define CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_REGISTRATION_FETCHER_H_
+
+#include "base/functional/callback_forward.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "url/gurl.h"
+
+// This class makes the network request to the DBSC registration endpoint to
+// get the registration instructions. A new fetcher instance should be created
+// per request.
+class BoundSessionRegistrationFetcher {
+ public:
+ virtual ~BoundSessionRegistrationFetcher() = default;
+
+ using RegistrationCompleteCallback = base::OnceCallback<void(
+ absl::optional<bound_session_credentials::BoundSessionParams>)>;
+
+ // Starts the network request to the DBSC registration endpoint. `callback`
+ // is called with the fetch results upon completion. Should be called no more
+ // than once per instance.
+ virtual void Start(RegistrationCompleteCallback callback) = 0;
+};
+
+#endif // CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_REGISTRATION_FETCHER_H_
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.cc
new file mode 100644
index 00000000000..83e2c96e647
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.cc
@@ -0,0 +1,198 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.h"
+
+#include "base/base64.h"
+#include "base/containers/span.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/values.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params.pb.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_params_util.h"
+#include "components/unexportable_keys/background_task_priority.h"
+#include "components/unexportable_keys/service_error.h"
+#include "components/unexportable_keys/unexportable_key_id.h"
+#include "components/unexportable_keys/unexportable_key_service.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "net/base/schemeful_site.h"
+#include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/header_util.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+
+namespace {
+constexpr char kSessionIdentifier[] = "session_identifier";
+const char kXSSIPrefix[] = ")]}'";
+
+bound_session_credentials::BoundSessionParams CreateBoundSessionParams(
+ const std::string& url,
+ const std::string& session_id,
+ const std::string& wrapped_key) {
+ bound_session_credentials::BoundSessionParams params;
+ params.set_site(url);
+ params.set_session_id(session_id);
+ params.set_wrapped_key(wrapped_key);
+ *params.mutable_creation_time() =
+ bound_session_credentials::TimeToTimestamp(base::Time::Now());
+ return params;
+}
+} // namespace
+
+BoundSessionRegistrationFetcherImpl::BoundSessionRegistrationFetcherImpl(
+ BoundSessionRegistrationFetcherParam registration_params,
+ scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
+ unexportable_keys::UnexportableKeyService& key_service)
+ : registration_params_(std::move(registration_params)),
+ key_service_(key_service),
+ url_loader_factory_(std::move(loader_factory)) {}
+
+BoundSessionRegistrationFetcherImpl::~BoundSessionRegistrationFetcherImpl() =
+ default;
+
+void BoundSessionRegistrationFetcherImpl::Start(
+ RegistrationCompleteCallback callback) {
+ callback_ = std::move(callback);
+ // base::Unretained() is safe since `this` owns
+ // `registration_token_helper_`.
+ registration_token_helper_ = RegistrationTokenHelper::CreateForSessionBinding(
+ key_service_.get(), registration_params_.Challenge(),
+ registration_params_.RegistrationEndpoint(),
+ base::BindOnce(
+ &BoundSessionRegistrationFetcherImpl::OnRegistrationTokenCreated,
+ base::Unretained(this)));
+ registration_token_helper_->Start();
+}
+
+void BoundSessionRegistrationFetcherImpl::OnURLLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
+ const network::mojom::URLResponseHead* head = url_loader_->ResponseInfo();
+ net::Error net_error = static_cast<net::Error>(url_loader_->NetError());
+
+ absl::optional<int> http_response_code;
+ absl::optional<bound_session_credentials::BoundSessionParams> return_value =
+ absl::nullopt;
+
+ if (head && head->headers) {
+ http_response_code = head->headers->response_code();
+ }
+
+ bool net_success = (net_error == net::OK ||
+ net_error == net::ERR_HTTP_RESPONSE_CODE_FAILURE) &&
+ http_response_code;
+
+ // Parse JSON response
+ if (response_body && net_success &&
+ network::IsSuccessfulStatus(*http_response_code)) {
+ // JSON responses normally should start with XSSI-protection prefix which
+ // should be removed prior to parsing.
+ base::StringPiece response_json = *response_body;
+ if (base::StartsWith(*response_body, kXSSIPrefix,
+ base::CompareCase::SENSITIVE)) {
+ response_json = response_json.substr(strlen(kXSSIPrefix));
+ }
+
+ absl::optional<base::Value::Dict> maybe_root =
+ base::JSONReader::ReadDict(response_json);
+
+ std::string* session_id = nullptr;
+ if (maybe_root) {
+ // TODO(b/293985274): Also parse credentials field
+ session_id = maybe_root->FindString(kSessionIdentifier);
+ }
+ if (!session_id) {
+ // Incorrect registration params.
+ std::move(callback_).Run(absl::nullopt);
+ return;
+ }
+
+ return_value = CreateBoundSessionParams(
+ net::SchemefulSite(registration_params_.RegistrationEndpoint())
+ .Serialize(),
+ *session_id, wrapped_key_str_);
+ }
+
+ // Finish the request, object is invalid after this
+ std::move(callback_).Run(return_value);
+}
+
+void BoundSessionRegistrationFetcherImpl::OnRegistrationTokenCreated(
+ absl::optional<RegistrationTokenHelper::Result> result) {
+ if (!result.has_value()) {
+ std::move(callback_).Run(absl::nullopt);
+ return;
+ }
+
+ const std::vector<uint8_t>& wrapped_key = result->wrapped_binding_key;
+ wrapped_key_str_ = std::string(wrapped_key.begin(), wrapped_key.end());
+
+ StartFetchingRegistration(result->registration_token);
+}
+
+void BoundSessionRegistrationFetcherImpl::StartFetchingRegistration(
+ const std::string& registration_token) {
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("device_bound_session_register",
+ R"(
+ semantics {
+ sender: "Chrome - Google authentication API"
+ description:
+ "This request is used to rotate bound Google authentication "
+ "cookies."
+ trigger:
+ "This request is triggered in a bound session when the bound Google"
+ " authentication cookies are soon to expire."
+ user_data {
+ type: ACCESS_TOKEN
+ }
+ data: "Request includes cookies and a signed token proving that a"
+ " request comes from the same device as was registered before."
+ destination: GOOGLE_OWNED_SERVICE
+ internal {
+ contacts {
+ email: "chrome-signin-team@google.com"
+ }
+ }
+ last_reviewed: "2023-06-15"
+ }
+ policy {
+ cookies_allowed: YES
+ cookies_store: "user"
+ setting:
+ "This is a new feature being developed behind a flag that is"
+ " disabled by default (kEnableBoundSessionCredentials). This"
+ " request will only be sent if the feature is enabled and once"
+ " a server requests it with a special header."
+ policy_exception_justification:
+ "Not implemented. "
+ "If the feature is on, this request must be made to ensure the user"
+ " maintains their signed in status on the web for Google owned"
+ " domains."
+ })");
+
+ auto request = std::make_unique<network::ResourceRequest>();
+ request->url = registration_params_.RegistrationEndpoint();
+ request->method = "POST";
+ request->site_for_cookies =
+ net::SiteForCookies::FromUrl(registration_params_.RegistrationEndpoint());
+ request->trusted_params = network::ResourceRequest::TrustedParams();
+ request->trusted_params->isolation_info =
+ net::IsolationInfo::CreateForInternalRequest(
+ url::Origin::Create(registration_params_.RegistrationEndpoint()));
+
+ std::string content_type = "application/jwt";
+
+ url_loader_ =
+ network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+ url_loader_->AttachStringForUpload(registration_token, content_type);
+ url_loader_->SetRetryOptions(
+ 3, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+ url_loader_->DownloadToString(
+ url_loader_factory_.get(),
+ base::BindOnce(&BoundSessionRegistrationFetcherImpl::OnURLLoaderComplete,
+ base::Unretained(this)),
+ 10 * 1024);
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.h
new file mode 100644
index 00000000000..015b146cc47
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.h
@@ -0,0 +1,73 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_REGISTRATION_FETCHER_IMPL_H_
+#define CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_REGISTRATION_FETCHER_IMPL_H_
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher.h"
+
+#include <memory>
+#include <string>
+
+#include "base/functional/callback.h"
+#include "base/memory/raw_ref.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h"
+#include "chrome/browser/signin/bound_session_credentials/registration_token_helper.h"
+#include "components/unexportable_keys/service_error.h"
+#include "components/unexportable_keys/unexportable_key_id.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "url/gurl.h"
+
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+} // namespace network
+
+namespace unexportable_keys {
+class UnexportableKeyService;
+}
+
+class BoundSessionRegistrationFetcherImpl
+ : public BoundSessionRegistrationFetcher {
+ public:
+ explicit BoundSessionRegistrationFetcherImpl(
+ BoundSessionRegistrationFetcherParam registration_params,
+ scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
+ unexportable_keys::UnexportableKeyService& key_service);
+
+ BoundSessionRegistrationFetcherImpl(
+ BoundSessionRegistrationFetcherImpl&& other) = delete;
+ BoundSessionRegistrationFetcherImpl& operator=(
+ BoundSessionRegistrationFetcherImpl&& other) noexcept = delete;
+
+ BoundSessionRegistrationFetcherImpl(
+ const BoundSessionRegistrationFetcherImpl&) = delete;
+ BoundSessionRegistrationFetcherImpl& operator=(
+ const BoundSessionRegistrationFetcherImpl&) = delete;
+ ~BoundSessionRegistrationFetcherImpl() override;
+
+ // BoundSessionRegistrationFetcher:
+ void Start(RegistrationCompleteCallback callback) override;
+
+ private:
+ void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
+ void OnRegistrationTokenCreated(
+ absl::optional<RegistrationTokenHelper::Result> result);
+
+ void StartFetchingRegistration(const std::string& registration_token);
+
+ BoundSessionRegistrationFetcherParam registration_params_;
+ const raw_ref<unexportable_keys::UnexportableKeyService> key_service_;
+ std::string wrapped_key_str_;
+
+ // Non-null after a fetch has started.
+ std::unique_ptr<network::SimpleURLLoader> url_loader_;
+ const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+ std::unique_ptr<RegistrationTokenHelper> registration_token_helper_;
+
+ RegistrationCompleteCallback callback_;
+};
+#endif // CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_REGISTRATION_FETCHER_IMPL_H_
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl_unittest.cc
new file mode 100644
index 00000000000..6b13af0502c
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl_unittest.cc
@@ -0,0 +1,224 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.h"
+#include "base/containers/span.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher.h"
+#include "components/signin/public/base/session_binding_test_utils.h"
+#include "components/unexportable_keys/service_error.h"
+#include "components/unexportable_keys/unexportable_key_service.h"
+#include "components/unexportable_keys/unexportable_key_service_impl.h"
+#include "components/unexportable_keys/unexportable_key_task_manager.h"
+#include "crypto/scoped_mock_unexportable_key_provider.h"
+#include "crypto/signature_verifier.h"
+#include "net/http/http_response_headers.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+using unexportable_keys::ServiceErrorOr;
+using unexportable_keys::UnexportableKeyId;
+const char kXSSIPrefix[] = ")]}'";
+const char kJSONBoundSessionParams[] = R"(
+ {
+ "session_identifier": "007",
+ "credentials": [
+ {
+ "type": "cookie",
+ "name": "auth_cookie",
+ "scope": {
+ "domain": "test.me/",
+ "path": "/"
+ }
+ }
+ ]
+ }
+)";
+constexpr char kChallenge[] = "test_challenge";
+
+std::vector<crypto::SignatureVerifier::SignatureAlgorithm> CreateAlgArray() {
+ return {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256,
+ crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256};
+}
+
+bound_session_credentials::BoundSessionParams CreateTestBoundSessionParams() {
+ bound_session_credentials::BoundSessionParams params;
+ params.set_site("https://google.com");
+ params.set_session_id("007");
+ return params;
+}
+
+void ConfigureURLLoaderFactoryForRegistrationResponse(
+ network::TestURLLoaderFactory* url_loader_factory,
+ const std::string response_body,
+ bool* out_made_download,
+ std::string* request_body = nullptr) {
+ url_loader_factory->SetInterceptor(
+ base::BindLambdaForTesting([=](const network::ResourceRequest& request) {
+ *out_made_download = true;
+ ASSERT_TRUE(request.url.is_valid());
+ ASSERT_FALSE(request.request_body.get()->elements()->empty());
+ if (request_body) {
+ *request_body = std::string(request.request_body.get()
+ ->elements()
+ ->at(0)
+ .As<network::DataElementBytes>()
+ .AsStringPiece());
+ }
+ auto response_head = network::mojom::URLResponseHead::New();
+ response_head->headers =
+ base::MakeRefCounted<net::HttpResponseHeaders>("");
+ url_loader_factory->AddResponse(
+ GURL(request.url), std::move(response_head),
+ std::move(response_body),
+ network::URLLoaderCompletionStatus(net::OK));
+ }));
+}
+
+MATCHER_P(ParamMatching, expected, "") {
+ return arg->site() == expected.site() &&
+ arg->session_id() == expected.session_id();
+}
+
+} // namespace
+
+class BoundSessionRegistrationFetcherImplTest : public testing::Test {
+ public:
+ BoundSessionRegistrationFetcherImplTest()
+ : unexportable_key_service_(task_manager_) {}
+
+ unexportable_keys::UnexportableKeyService& unexportable_key_service() {
+ return unexportable_key_service_;
+ }
+
+ void RunBackgroundTasks() { task_environment_.RunUntilIdle(); }
+
+ private:
+ base::test::TaskEnvironment task_environment_{
+ base::test::TaskEnvironment::ThreadPoolExecutionMode::
+ QUEUED}; // QUEUED - tasks don't run until `RunUntilIdle()` is
+ // called.
+ unexportable_keys::UnexportableKeyTaskManager task_manager_;
+ unexportable_keys::UnexportableKeyServiceImpl unexportable_key_service_;
+};
+
+TEST_F(BoundSessionRegistrationFetcherImplTest, ValidInput) {
+ crypto::ScopedMockUnexportableKeyProvider scoped_mock_key_provider_;
+ network::TestURLLoaderFactory url_loader_factory;
+ bool made_download = false;
+ std::string request_body;
+
+ ConfigureURLLoaderFactoryForRegistrationResponse(
+ &url_loader_factory,
+ std::string(kXSSIPrefix) + std::string(kJSONBoundSessionParams),
+ &made_download, &request_body);
+
+ BoundSessionRegistrationFetcherParam params =
+ BoundSessionRegistrationFetcherParam::CreateInstanceForTesting(
+ GURL("https://www.google.com/startsession"), CreateAlgArray(),
+ kChallenge);
+ std::unique_ptr<BoundSessionRegistrationFetcher> fetcher =
+ std::make_unique<BoundSessionRegistrationFetcherImpl>(
+ std::move(params), url_loader_factory.GetSafeWeakWrapper(),
+ unexportable_key_service());
+ base::test::TestFuture<
+ absl::optional<bound_session_credentials::BoundSessionParams>>
+ future;
+
+ fetcher->Start(future.GetCallback());
+
+ ASSERT_FALSE(made_download);
+ EXPECT_FALSE(future.IsReady());
+ RunBackgroundTasks();
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_THAT(future.Get<>(), ParamMatching(CreateTestBoundSessionParams()));
+ ASSERT_TRUE(made_download);
+
+ // Verify the wrapped key.
+ std::string wrapped_key = future.Get<>()->wrapped_key();
+ base::test::TestFuture<ServiceErrorOr<UnexportableKeyId>>
+ wrapped_key_to_key_id;
+ unexportable_key_service().FromWrappedSigningKeySlowlyAsync(
+ base::make_span(
+ std::vector<uint8_t>(wrapped_key.begin(), wrapped_key.end())),
+ unexportable_keys::BackgroundTaskPriority::kBestEffort,
+ wrapped_key_to_key_id.GetCallback());
+ EXPECT_TRUE(wrapped_key_to_key_id.IsReady());
+ EXPECT_TRUE(wrapped_key_to_key_id.Get().has_value());
+
+ // Verify that the request body contains a valid registration token.
+ UnexportableKeyId key_id = wrapped_key_to_key_id.Get().value();
+ EXPECT_TRUE(signin::VerifyJwtSignature(
+ request_body, *unexportable_key_service().GetAlgorithm(key_id),
+ *unexportable_key_service().GetSubjectPublicKeyInfo(key_id)));
+}
+
+TEST_F(BoundSessionRegistrationFetcherImplTest, MissingXSSIPrefix) {
+ crypto::ScopedMockUnexportableKeyProvider scoped_mock_key_provider_;
+ network::TestURLLoaderFactory url_loader_factory;
+ bool made_download = false;
+
+ ConfigureURLLoaderFactoryForRegistrationResponse(
+ &url_loader_factory, std::string(kJSONBoundSessionParams),
+ &made_download);
+
+ BoundSessionRegistrationFetcherParam params =
+ BoundSessionRegistrationFetcherParam::CreateInstanceForTesting(
+ GURL("https://www.google.com/startsession"), CreateAlgArray(),
+ kChallenge);
+ std::unique_ptr<BoundSessionRegistrationFetcher> fetcher =
+ std::make_unique<BoundSessionRegistrationFetcherImpl>(
+ std::move(params), url_loader_factory.GetSafeWeakWrapper(),
+ unexportable_key_service());
+ base::test::TestFuture<
+ absl::optional<bound_session_credentials::BoundSessionParams>>
+ future;
+
+ ASSERT_FALSE(made_download);
+ EXPECT_FALSE(future.IsReady());
+ fetcher->Start(future.GetCallback());
+ RunBackgroundTasks();
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_THAT(future.Get<>(), ParamMatching(CreateTestBoundSessionParams()));
+ ASSERT_TRUE(made_download);
+}
+
+TEST_F(BoundSessionRegistrationFetcherImplTest, MissingJSONBoundSessionParams) {
+ crypto::ScopedMockUnexportableKeyProvider scoped_mock_key_provider_;
+ network::TestURLLoaderFactory url_loader_factory;
+ bool made_download = false;
+
+ // Response body contains XSSI prefix but JSON of bound session params
+ // missing. Expecting early termination and callback to be called with
+ // absl::nullopt.
+ ConfigureURLLoaderFactoryForRegistrationResponse(
+ &url_loader_factory, std::string(kXSSIPrefix), &made_download);
+
+ BoundSessionRegistrationFetcherParam params =
+ BoundSessionRegistrationFetcherParam::CreateInstanceForTesting(
+ GURL("https://www.google.com/startsession"), CreateAlgArray(),
+ kChallenge);
+ std::unique_ptr<BoundSessionRegistrationFetcher> fetcher =
+ std::make_unique<BoundSessionRegistrationFetcherImpl>(
+ std::move(params), url_loader_factory.GetSafeWeakWrapper(),
+ unexportable_key_service());
+ base::test::TestFuture<
+ absl::optional<bound_session_credentials::BoundSessionParams>>
+ future;
+
+ ASSERT_FALSE(made_download);
+ EXPECT_FALSE(future.IsReady());
+ fetcher->Start(future.GetCallback());
+ RunBackgroundTasks();
+ EXPECT_TRUE(future.IsReady());
+ EXPECT_EQ(future.Get<>(), absl::nullopt);
+ ASSERT_TRUE(made_download);
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.cc
new file mode 100644
index 00000000000..2f3375588af
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.cc
@@ -0,0 +1,137 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h"
+
+#include "base/base64url.h"
+#include "base/strings/escape.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "net/base/schemeful_site.h"
+
+namespace {
+constexpr char kAlgoItemKey[] = "supported-alg";
+constexpr char kRegistrationHeaderName[] = "Sec-Session-Google-Registration";
+constexpr char kRegistrationItemKey[] = "registration";
+constexpr char kChallengeItemKey[] = "challenge";
+
+absl::optional<crypto::SignatureVerifier::SignatureAlgorithm> AlgoFromString(
+ const std::string algo) {
+ if (base::EqualsCaseInsensitiveASCII(algo, "ES256")) {
+ return crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256;
+ }
+
+ if (base::EqualsCaseInsensitiveASCII(algo, "RS256")) {
+ return crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256;
+ }
+
+ return absl::nullopt;
+}
+} // namespace
+
+BoundSessionRegistrationFetcherParam::BoundSessionRegistrationFetcherParam(
+ BoundSessionRegistrationFetcherParam&& other) = default;
+
+BoundSessionRegistrationFetcherParam&
+BoundSessionRegistrationFetcherParam::operator=(
+ BoundSessionRegistrationFetcherParam&& other) noexcept = default;
+
+BoundSessionRegistrationFetcherParam::~BoundSessionRegistrationFetcherParam() =
+ default;
+
+BoundSessionRegistrationFetcherParam::BoundSessionRegistrationFetcherParam(
+ GURL registration_endpoint,
+ std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos,
+ std::string challenge)
+ : registration_endpoint_(std::move(registration_endpoint)),
+ supported_algos_(std::move(supported_algos)),
+ challenge_(std::move(challenge)) {}
+
+absl::optional<BoundSessionRegistrationFetcherParam>
+BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ const GURL& request_url,
+ const net::HttpResponseHeaders* headers) {
+ if (!request_url.is_valid()) {
+ return absl::nullopt;
+ }
+
+ std::string header_value;
+ if (!headers->GetNormalizedHeader(kRegistrationHeaderName, &header_value)) {
+ return absl::nullopt;
+ }
+
+ GURL registration_endpoint;
+ std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos;
+ std::string challenge;
+ base::StringPairs items;
+ base::SplitStringIntoKeyValuePairs(header_value, '=', ';', &items);
+ for (const auto& [key, value] : items) {
+ if (base::EqualsCaseInsensitiveASCII(key, kRegistrationItemKey)) {
+ std::string unescaped = base::UnescapeURLComponent(
+ value,
+ base::UnescapeRule::PATH_SEPARATORS |
+ base::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
+ GURL potential_registration_endpoint =
+ request_url.GetWithoutFilename().Resolve(unescaped);
+ if (net::SchemefulSite(potential_registration_endpoint) ==
+ net::SchemefulSite(request_url)) {
+ registration_endpoint = potential_registration_endpoint;
+ }
+ }
+
+ if (base::EqualsCaseInsensitiveASCII(key, kAlgoItemKey)) {
+ auto list = base::SplitString(value, ",",
+ base::WhitespaceHandling::TRIM_WHITESPACE,
+ base::SplitResult::SPLIT_WANT_NONEMPTY);
+ for (const auto& alg_string : list) {
+ absl::optional<crypto::SignatureVerifier::SignatureAlgorithm> alg =
+ AlgoFromString(alg_string);
+ if (alg.has_value()) {
+ supported_algos.push_back(alg.value());
+ }
+ }
+ }
+
+ if (base::EqualsCaseInsensitiveASCII(key, kChallengeItemKey)) {
+ // Challenge will be eventually written into a `base::Value`, when
+ // generating the registration token, which is restricted to UTF8.
+ if (!base::IsStringUTF8AllowingNoncharacters(value)) {
+ return absl::nullopt;
+ }
+ challenge = value;
+ }
+ }
+
+ if (registration_endpoint.is_valid() && !supported_algos.empty() &&
+ !challenge.empty()) {
+ return BoundSessionRegistrationFetcherParam(
+ std::move(registration_endpoint), std::move(supported_algos),
+ std::move(challenge));
+ } else {
+ return absl::nullopt;
+ }
+}
+
+BoundSessionRegistrationFetcherParam
+BoundSessionRegistrationFetcherParam::CreateInstanceForTesting(
+ GURL registration_endpoint,
+ std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos,
+ std::string challenge) {
+ return BoundSessionRegistrationFetcherParam(std::move(registration_endpoint),
+ std::move(supported_algos),
+ std::move(challenge));
+}
+
+const GURL& BoundSessionRegistrationFetcherParam::RegistrationEndpoint() const {
+ return registration_endpoint_;
+}
+
+base::span<const crypto::SignatureVerifier::SignatureAlgorithm>
+BoundSessionRegistrationFetcherParam::SupportedAlgos() const {
+ return supported_algos_;
+}
+
+const std::string& BoundSessionRegistrationFetcherParam::Challenge() const {
+ return challenge_;
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h
new file mode 100644
index 00000000000..3c854fe8671
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h
@@ -0,0 +1,58 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_REGISTRATION_FETCHER_PARAM_H_
+#define CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_REGISTRATION_FETCHER_PARAM_H_
+
+#include <vector>
+
+#include "base/containers/span.h"
+#include "crypto/signature_verifier.h"
+#include "net/http/http_response_headers.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+
+class BoundSessionRegistrationFetcherParam {
+ public:
+ BoundSessionRegistrationFetcherParam(
+ BoundSessionRegistrationFetcherParam&& other);
+ BoundSessionRegistrationFetcherParam& operator=(
+ BoundSessionRegistrationFetcherParam&& other) noexcept;
+
+ BoundSessionRegistrationFetcherParam(
+ const BoundSessionRegistrationFetcherParam& other) = delete;
+ BoundSessionRegistrationFetcherParam& operator=(
+ const BoundSessionRegistrationFetcherParam&) = delete;
+ ~BoundSessionRegistrationFetcherParam();
+
+ // Will return a valid instance or return absl::nullopt;
+ static absl::optional<BoundSessionRegistrationFetcherParam>
+ MaybeCreateInstance(const GURL& request_url,
+ const net::HttpResponseHeaders* headers);
+
+ // Convenience constructor for testing.
+ static BoundSessionRegistrationFetcherParam CreateInstanceForTesting(
+ GURL registration_endpoint,
+ std::vector<crypto::SignatureVerifier::SignatureAlgorithm>
+ supported_algos,
+ std::string challenge);
+
+ const GURL& RegistrationEndpoint() const;
+ base::span<const crypto::SignatureVerifier::SignatureAlgorithm>
+ SupportedAlgos() const;
+ const std::string& Challenge() const;
+
+ private:
+ BoundSessionRegistrationFetcherParam(
+ GURL registration_endpoint,
+ std::vector<crypto::SignatureVerifier::SignatureAlgorithm>
+ supported_algos,
+ std::string challenge);
+
+ GURL registration_endpoint_;
+ std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos_;
+ std::string challenge_;
+};
+
+#endif // CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_REGISTRATION_FETCHER_PARAM_H_
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param_unittest.cc
new file mode 100644
index 00000000000..4791487ae5c
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param_unittest.cc
@@ -0,0 +1,220 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h"
+#include "base/strings/strcat.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher.h"
+#include "net/http/http_response_headers.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+constexpr char kChallenge[] = "test_challenge";
+} // namespace
+
+class BoundSessionRegistrationFetcherParamTest : public testing::Test {
+ public:
+ BoundSessionRegistrationFetcherParamTest() = default;
+ BoundSessionRegistrationFetcherParamTest(
+ const BoundSessionRegistrationFetcherParamTest&) = delete;
+ BoundSessionRegistrationFetcherParamTest& operator=(
+ const BoundSessionRegistrationFetcherParamTest&) = delete;
+ ~BoundSessionRegistrationFetcherParamTest() override = default;
+};
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, AllInvalid) {
+ std::vector<crypto::SignatureVerifier::SignatureAlgorithm> supported_algos;
+ BoundSessionRegistrationFetcherParam params =
+ BoundSessionRegistrationFetcherParam::CreateInstanceForTesting(
+ GURL(), supported_algos, "");
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, AllValid) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ base::StrCat(
+ {"registration=startsession; supported-alg=ES256,RS256; challenge=",
+ kChallenge, ";"}));
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_TRUE(maybe_params.has_value());
+ BoundSessionRegistrationFetcherParam params = std::move(maybe_params.value());
+ ASSERT_EQ(params.RegistrationEndpoint(),
+ GURL("https://www.google.com/startsession"));
+ ASSERT_EQ(params.SupportedAlgos()[0],
+ crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256);
+ ASSERT_EQ(params.SupportedAlgos()[1],
+ crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256);
+ ASSERT_EQ(params.Challenge(), kChallenge);
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, AllValidFullUrl) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ base::StrCat({"registration=https://accounts.google.com/"
+ "startsession; supported-alg=ES256,RS256; challenge=",
+ kChallenge, ";"}));
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_TRUE(maybe_params.has_value());
+ BoundSessionRegistrationFetcherParam params = std::move(maybe_params.value());
+ ASSERT_EQ(params.RegistrationEndpoint(),
+ GURL("https://accounts.google.com/startsession"));
+ ASSERT_EQ(params.SupportedAlgos()[0],
+ crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256);
+ ASSERT_EQ(params.SupportedAlgos()[1],
+ crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256);
+ ASSERT_EQ(params.Challenge(), kChallenge);
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, AllValidFullDifferentUrl) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ base::StrCat({"registration=https://accounts.different.url/"
+ "startsession; supported-alg=ES256,RS256; challenge=",
+ kChallenge, ";"}));
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_FALSE(maybe_params.has_value());
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, AllValidSwapAlgo) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ base::StrCat(
+ {"registration=startsession; supported-alg=RS256,ES256; challenge=",
+ kChallenge, ";"}));
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_TRUE(maybe_params.has_value());
+ BoundSessionRegistrationFetcherParam params = std::move(maybe_params.value());
+ ASSERT_EQ(params.RegistrationEndpoint(),
+ GURL("https://www.google.com/startsession"));
+ ASSERT_EQ(params.SupportedAlgos()[0],
+ crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256);
+ ASSERT_EQ(params.SupportedAlgos()[1],
+ crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256);
+ ASSERT_EQ(params.Challenge(), kChallenge);
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, AllValidOneAlgo) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ base::StrCat(
+ {"registration=startsession; supported-alg=RS256; challenge=",
+ kChallenge, ";"}));
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_TRUE(maybe_params.has_value());
+ BoundSessionRegistrationFetcherParam params = std::move(maybe_params.value());
+ ASSERT_EQ(params.RegistrationEndpoint(),
+ GURL("https://www.google.com/startsession"));
+ ASSERT_EQ(params.SupportedAlgos()[0],
+ crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256);
+ ASSERT_EQ(params.Challenge(), kChallenge);
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, MissingHeader) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ // Note: not adding the right header, causing absl::nullopt to be returned.
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_FALSE(maybe_params.has_value());
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, MissingUrl) {
+ GURL registration_request = GURL();
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ base::StrCat(
+ {"registration=startsession; supported-alg=ES256,RS256; challenge=",
+ kChallenge, ";"}));
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_FALSE(maybe_params.has_value());
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, MissingAlgo) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ base::StrCat({"registration=startsession; supported-alg=; challenge=",
+ kChallenge, ";"}));
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_FALSE(maybe_params.has_value());
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, MissingRegistration) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ base::StrCat({"supported-alg=ES256,RS256; challenge=", kChallenge, ";"}));
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_FALSE(maybe_params.has_value());
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, MissingChallenge) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ "registration=startsession; supported-alg=ES256,RS256");
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_FALSE(maybe_params.has_value());
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, EmptyChallenge) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ "registration=startsession; supported-alg=ES256,RS256; challenge=;");
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_FALSE(maybe_params.has_value());
+}
+
+TEST_F(BoundSessionRegistrationFetcherParamTest, ChallengeInvalidUtf8) {
+ GURL registration_request = GURL("https://www.google.com/registration");
+ auto response_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_headers->SetHeader(
+ "Sec-Session-Google-Registration",
+ "registration=startsession; supported-alg=ES256,RS256; "
+ "challenge=ab\xC0\x80;");
+ absl::optional<BoundSessionRegistrationFetcherParam> maybe_params =
+ BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ registration_request, response_headers.get());
+ ASSERT_FALSE(maybe_params.has_value());
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_request_throttled_listener_browser_impl_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_request_throttled_listener_browser_impl_unittest.cc
index b5d7e8d0535..dd00d9be2ae 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_request_throttled_listener_browser_impl_unittest.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_request_throttled_listener_browser_impl_unittest.cc
@@ -10,10 +10,16 @@
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_param.h"
#include "chrome/common/bound_session_request_throttled_listener.h"
#include "chrome/common/renderer_configuration.mojom.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace network {
+class SimpleURLLoader;
+} // namespace network
+
namespace {
class FakeBoundSessionCookieRefreshService
: public BoundSessionCookieRefreshService {
@@ -23,22 +29,28 @@ class FakeBoundSessionCookieRefreshService
void Initialize() override {}
void RegisterNewBoundSession(
- const bound_session_credentials::RegistrationParams& params) override {}
+ const bound_session_credentials::BoundSessionParams& params) override {}
- bool IsBoundSession() const override { return true; }
+ void MaybeTerminateSession(const net::HttpResponseHeaders* headers) override {
+ }
- chrome::mojom::BoundSessionParamsPtr GetBoundSessionParams() const override {
- return chrome::mojom::BoundSessionParams::New();
+ chrome::mojom::BoundSessionThrottlerParamsPtr GetBoundSessionThrottlerParams()
+ const override {
+ return chrome::mojom::BoundSessionThrottlerParams::New();
}
- void SetRendererBoundSessionParamsUpdaterDelegate(
- RendererBoundSessionParamsUpdaterDelegate renderer_updater) override {}
+ void SetRendererBoundSessionThrottlerParamsUpdaterDelegate(
+ RendererBoundSessionThrottlerParamsUpdaterDelegate renderer_updater)
+ override {}
void OnRequestBlockedOnCookie(
OnRequestBlockedOnCookieCallback resume_blocked_request) override {
resume_blocked_request_ = std::move(resume_blocked_request);
}
+ void CreateRegistrationRequest(
+ BoundSessionRegistrationFetcherParam registration_params) override {}
+
base::WeakPtr<BoundSessionCookieRefreshService> GetWeakPtr() override {
return weak_ptr_factory_.GetWeakPtr();
}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.cc b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.cc
index d1d5f6b6e73..3ef4524792f 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.cc
@@ -7,6 +7,9 @@
#include "net/cookies/cookie_access_result.h"
#include "net/cookies/cookie_change_dispatcher.h"
+BoundSessionTestCookieManager::BoundSessionTestCookieManager() = default;
+BoundSessionTestCookieManager::~BoundSessionTestCookieManager() = default;
+
// static
net::CanonicalCookie BoundSessionTestCookieManager::CreateCookie(
const GURL& url,
@@ -29,8 +32,8 @@ void BoundSessionTestCookieManager::SetCanonicalCookie(
const GURL& source_url,
const net::CookieOptions& cookie_options,
SetCanonicalCookieCallback callback) {
- cookie_ = cookie;
- DispatchCookieChange(net::CookieChangeInfo(cookie_, net::CookieAccessResult(),
+ cookies_.insert(cookie);
+ DispatchCookieChange(net::CookieChangeInfo(cookie, net::CookieAccessResult(),
net::CookieChangeCause::INSERTED));
if (callback) {
std::move(callback).Run(net::CookieAccessResult());
@@ -42,5 +45,28 @@ void BoundSessionTestCookieManager::GetCookieList(
const net::CookieOptions& cookie_options,
const net::CookiePartitionKeyCollection& cookie_partition_key_collection,
GetCookieListCallback callback) {
- std::move(callback).Run({{cookie_, net::CookieAccessResult()}}, {});
+ std::vector<net::CookieWithAccessResult> cookie_result;
+ for (const auto& cookie : cookies_) {
+ cookie_result.emplace_back(cookie, net::CookieAccessResult());
+ }
+ std::move(callback).Run(cookie_result, {});
+}
+
+void BoundSessionTestCookieManager::AddCookieChangeListener(
+ const GURL& url,
+ const absl::optional<std::string>& name,
+ mojo::PendingRemote<network::mojom::CookieChangeListener> listener) {
+ mojo::Remote<network::mojom::CookieChangeListener> listener_remote(
+ std::move(listener));
+ // In the context of bound session credentials we always pass a cookie name.
+ CHECK(name.has_value());
+ cookie_to_listener_.insert({*name, std::move(listener_remote)});
+}
+
+void BoundSessionTestCookieManager::DispatchCookieChange(
+ const net::CookieChangeInfo& change) {
+ if (!cookie_to_listener_.contains(change.cookie.Name())) {
+ return;
+ }
+ cookie_to_listener_[change.cookie.Name()]->OnCookieChange(change);
}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h
index 29eb89d80dd..69c55e62a26 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_TEST_COOKIE_MANAGER_H_
#define CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_TEST_COOKIE_MANAGER_H_
+#include "base/containers/flat_map.h"
#include "services/network/test/test_cookie_manager.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -12,6 +13,9 @@
class BoundSessionTestCookieManager : public network::TestCookieManager {
public:
+ BoundSessionTestCookieManager();
+ ~BoundSessionTestCookieManager() override;
+
static net::CanonicalCookie CreateCookie(
const GURL& url,
const std::string& cookie_name,
@@ -22,7 +26,7 @@ class BoundSessionTestCookieManager : public network::TestCookieManager {
const net::CookieOptions& cookie_options,
SetCanonicalCookieCallback callback) override;
- net::CanonicalCookie& cookie() { return cookie_; }
+ const base::flat_set<net::CanonicalCookie>& cookies() { return cookies_; }
void GetCookieList(
const GURL& url,
@@ -30,8 +34,19 @@ class BoundSessionTestCookieManager : public network::TestCookieManager {
const net::CookiePartitionKeyCollection& cookie_partition_key_collection,
GetCookieListCallback callback) override;
+ void AddCookieChangeListener(
+ const GURL& url,
+ const absl::optional<std::string>& name,
+ mojo::PendingRemote<network::mojom::CookieChangeListener> listener)
+ override;
+
+ void DispatchCookieChange(const net::CookieChangeInfo& change) override;
+
private:
- net::CanonicalCookie cookie_;
+ base::flat_set<net::CanonicalCookie> cookies_;
+ base::flat_map<std::string,
+ mojo::Remote<network::mojom::CookieChangeListener>>
+ cookie_to_listener_;
};
#endif // CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_BOUND_SESSION_TEST_COOKIE_MANAGER_H_
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.cc b/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.cc
index e740691e43d..b961dc54b74 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.h"
+#include "base/check.h"
#include "base/functional/callback.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
@@ -18,14 +19,16 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
FakeBoundSessionRefreshCookieFetcher::FakeBoundSessionRefreshCookieFetcher(
- SigninClient* client,
+ network::mojom::CookieManager* cookie_manager,
const GURL& url,
- const std::string& cookie_name,
+ base::flat_set<std::string> cookie_names,
absl::optional<base::TimeDelta> unlock_automatically_in)
- : client_(client),
+ : cookie_manager_(cookie_manager),
url_(url),
- cookie_name_(cookie_name),
- unlock_automatically_in_(unlock_automatically_in) {}
+ cookie_names_(std::move(cookie_names)),
+ unlock_automatically_in_(unlock_automatically_in) {
+ CHECK(cookie_manager_);
+}
FakeBoundSessionRefreshCookieFetcher::~FakeBoundSessionRefreshCookieFetcher() =
default;
@@ -51,23 +54,31 @@ void FakeBoundSessionRefreshCookieFetcher::Start(
void FakeBoundSessionRefreshCookieFetcher::SimulateCompleteRefreshRequest(
BoundSessionRefreshCookieFetcher::Result result,
absl::optional<base::Time> cookie_expiration) {
- if (result == BoundSessionRefreshCookieFetcher::Result::kSuccess &&
- cookie_expiration) {
+ if (result == BoundSessionRefreshCookieFetcher::Result::kSuccess) {
+ CHECK(cookie_expiration);
// Synchronous since tests use `BoundSessionTestCookieManager`.
- OnRefreshCookieCompleted(CreateFakeCookie(cookie_expiration.value()));
+ std::vector<std::unique_ptr<net::CanonicalCookie>> new_cookies;
+ for (const auto& cookie_name : cookie_names_) {
+ new_cookies.emplace_back(
+ CreateFakeCookie(cookie_name, cookie_expiration.value()));
+ }
+ OnRefreshCookieCompleted(std::move(new_cookies));
} else {
std::move(callback_).Run(result);
}
}
void FakeBoundSessionRefreshCookieFetcher::OnRefreshCookieCompleted(
- std::unique_ptr<net::CanonicalCookie> cookie) {
- InsertCookieInCookieJar(std::move(cookie));
+ std::vector<std::unique_ptr<net::CanonicalCookie>> cookies) {
+ ResetCallbackCounter();
+ for (auto& cookie : cookies) {
+ InsertCookieInCookieJar(std::move(cookie));
+ }
}
void FakeBoundSessionRefreshCookieFetcher::InsertCookieInCookieJar(
std::unique_ptr<net::CanonicalCookie> cookie) {
- DCHECK(client_);
+ DCHECK(cookie_manager_);
base::OnceCallback<void(net::CookieAccessResult)> callback =
base::BindOnce(&FakeBoundSessionRefreshCookieFetcher::OnCookieSet,
weak_ptr_factory_.GetWeakPtr());
@@ -76,7 +87,7 @@ void FakeBoundSessionRefreshCookieFetcher::InsertCookieInCookieJar(
// Permit it to set a SameSite cookie if it wants to.
options.set_same_site_cookie_context(
net::CookieOptions::SameSiteCookieContext::MakeInclusive());
- client_->GetCookieManager()->SetCanonicalCookie(
+ cookie_manager_->SetCanonicalCookie(
*cookie, url_, options,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(callback),
@@ -86,6 +97,11 @@ void FakeBoundSessionRefreshCookieFetcher::InsertCookieInCookieJar(
void FakeBoundSessionRefreshCookieFetcher::OnCookieSet(
net::CookieAccessResult access_result) {
+ callback_counter_++;
+ if (callback_counter_ != cookie_names_.size()) {
+ return;
+ }
+
bool success = access_result.status.IsInclude();
if (!success) {
std::move(callback_).Run(
@@ -97,8 +113,13 @@ void FakeBoundSessionRefreshCookieFetcher::OnCookieSet(
// |This| may be destroyed
}
+void FakeBoundSessionRefreshCookieFetcher::ResetCallbackCounter() {
+ callback_counter_ = 0;
+}
+
std::unique_ptr<net::CanonicalCookie>
FakeBoundSessionRefreshCookieFetcher::CreateFakeCookie(
+ const std::string& cookie_name,
base::Time cookie_expiration) {
constexpr char kFakeCookieValue[] = "FakeCookieValue";
@@ -106,7 +127,7 @@ FakeBoundSessionRefreshCookieFetcher::CreateFakeCookie(
// Create fake SIDTS cookie until the server endpoint is available.
std::unique_ptr<net::CanonicalCookie> new_cookie =
net::CanonicalCookie::CreateSanitizedCookie(
- /*url=*/url_, /*name=*/cookie_name_,
+ /*url=*/url_, /*name=*/cookie_name,
/*value=*/kFakeCookieValue,
/*domain=*/url_.host(), /*path=*/"/",
/*creation_time=*/now, /*expiration_time=*/cookie_expiration,
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.h b/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.h
index dae0479ae66..ae5659ae2e7 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.h
@@ -7,6 +7,7 @@
#include "chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.h"
+#include "base/containers/flat_set.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
@@ -14,15 +15,17 @@
#include "net/cookies/canonical_cookie.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-class SigninClient;
+namespace network::mojom {
+class CookieManager;
+}
class FakeBoundSessionRefreshCookieFetcher
: public BoundSessionRefreshCookieFetcher {
public:
FakeBoundSessionRefreshCookieFetcher(
- SigninClient* client,
+ network::mojom::CookieManager* cookie_manager,
const GURL& url,
- const std::string& cookie_name,
+ base::flat_set<std::string> cookie_names,
absl::optional<base::TimeDelta> unlock_automatically_in = absl::nullopt);
~FakeBoundSessionRefreshCookieFetcher() override;
@@ -37,15 +40,19 @@ class FakeBoundSessionRefreshCookieFetcher
protected:
std::unique_ptr<net::CanonicalCookie> CreateFakeCookie(
+ const std::string& cookie_name,
base::Time cookie_expiration);
- void OnRefreshCookieCompleted(std::unique_ptr<net::CanonicalCookie> cookie);
+ void OnRefreshCookieCompleted(
+ std::vector<std::unique_ptr<net::CanonicalCookie>> cookies);
void InsertCookieInCookieJar(std::unique_ptr<net::CanonicalCookie> cookie);
void OnCookieSet(net::CookieAccessResult access_result);
+ void ResetCallbackCounter();
- const raw_ptr<SigninClient> client_;
+ const raw_ptr<network::mojom::CookieManager> cookie_manager_;
const GURL url_;
- const std::string cookie_name_;
+ const base::flat_set<std::string> cookie_names_;
RefreshCookieCompleteCallback callback_;
+ size_t callback_counter_ = 0;
// `this` might be used temporarily for local development until the server
// endpoint is fully developed and is stable. In production,
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher_unittest.cc
deleted file mode 100644
index 9aca7d25d24..00000000000
--- a/chromium/chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher_unittest.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/signin/bound_session_credentials/fake_bound_session_refresh_cookie_fetcher.h"
-
-#include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
-#include "chrome/browser/signin/bound_session_credentials/bound_session_test_cookie_manager.h"
-#include "components/signin/public/base/test_signin_client.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "net/cookies/canonical_cookie.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-constexpr char kSIDTSCookieName[] = "__Secure-1PSIDTS";
-
-class FakeBoundSessionRefreshCookieFetcherTest : public testing::Test {
- public:
- FakeBoundSessionRefreshCookieFetcherTest()
- : task_environment_(
- base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME) {
- std::unique_ptr<BoundSessionTestCookieManager> fake_cookie_manager =
- std::make_unique<BoundSessionTestCookieManager>();
- cookie_manager_ = fake_cookie_manager.get();
- signin_client_.set_cookie_manager(std::move(fake_cookie_manager));
- }
-
- ~FakeBoundSessionRefreshCookieFetcherTest() override = default;
-
- void InitializeFetcher(base::OnceClosure on_done) {
- fetcher_ = std::make_unique<FakeBoundSessionRefreshCookieFetcher>(
- &signin_client_, GaiaUrls::GetInstance()->secure_google_url(),
- kSIDTSCookieName, /*unlock_automatically_in=*/base::Milliseconds(100));
-
- fetcher_->Start(
- base::BindOnce(&FakeBoundSessionRefreshCookieFetcherTest::OnCookieSet,
- base::Unretained(this), std::move(on_done)));
- }
-
- void OnCookieSet(base::OnceClosure on_done,
- BoundSessionRefreshCookieFetcher::Result result) {
- std::move(on_done).Run();
- }
-
- void VerifyCookie() {
- EXPECT_TRUE(cookie_manager_);
- constexpr char kFakeCookieValue[] = "FakeCookieValue";
-
- net::CanonicalCookie& cookie = cookie_manager_->cookie();
- EXPECT_TRUE(cookie.IsCanonical());
- EXPECT_EQ(cookie.Domain(), ".google.com");
- EXPECT_EQ(cookie.Name(), kSIDTSCookieName);
- EXPECT_EQ(cookie.Value(), kFakeCookieValue);
- EXPECT_GT(cookie.ExpiryDate(), base::Time::Now());
- EXPECT_TRUE(cookie.IsExpired(base::Time::Now() + base::Minutes(10)));
- }
-
- protected:
- base::test::SingleThreadTaskEnvironment task_environment_{
- base::test::TaskEnvironment::TimeSource::MOCK_TIME};
- std::unique_ptr<FakeBoundSessionRefreshCookieFetcher> fetcher_;
- sync_preferences::TestingPrefServiceSyncable prefs_;
- TestSigninClient signin_client_{&prefs_};
- raw_ptr<BoundSessionTestCookieManager> cookie_manager_ = nullptr;
-};
-
-TEST_F(FakeBoundSessionRefreshCookieFetcherTest, SetSIDTSCookie) {
- base::RunLoop run_loop;
- InitializeFetcher(run_loop.QuitClosure());
- task_environment_.FastForwardBy(base::Milliseconds(100));
- run_loop.Run();
- VerifyCookie();
-}
-} // namespace
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.cc b/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.cc
index 5ff9261db2f..fe44b3e322d 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.cc
@@ -7,6 +7,7 @@
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
+#include "base/memory/ptr_util.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
#include "components/signin/public/base/session_binding_utils.h"
@@ -33,25 +34,44 @@ RegistrationTokenHelper::Result::Result(
std::vector<uint8_t> in_wrapped_binding_key,
std::string in_registration_token)
: binding_key_id(in_binding_key_id),
- wrapped_binding_key(in_wrapped_binding_key),
- registration_token(in_registration_token) {}
+ wrapped_binding_key(std::move(in_wrapped_binding_key)),
+ registration_token(std::move(in_registration_token)) {}
RegistrationTokenHelper::Result::~Result() = default;
RegistrationTokenHelper::Result::Result(Result&& other) = default;
RegistrationTokenHelper::Result& RegistrationTokenHelper::Result::operator=(
Result&& other) = default;
-RegistrationTokenHelper::RegistrationTokenHelper(
+// static
+std::unique_ptr<RegistrationTokenHelper>
+RegistrationTokenHelper::CreateForSessionBinding(
+ unexportable_keys::UnexportableKeyService& unexportable_key_service,
+ base::StringPiece challenge,
+ const GURL& registration_url,
+ base::OnceCallback<void(absl::optional<Result>)> callback) {
+ HeaderAndPayloadGenerator header_and_payload_generator = base::BindRepeating(
+ &signin::CreateKeyRegistrationHeaderAndPayloadForSessionBinding,
+ std::string(challenge), registration_url);
+ return base::WrapUnique(new RegistrationTokenHelper(
+ unexportable_key_service, std::move(header_and_payload_generator),
+ std::move(callback)));
+}
+
+// static
+std::unique_ptr<RegistrationTokenHelper>
+RegistrationTokenHelper::CreateForTokenBinding(
unexportable_keys::UnexportableKeyService& unexportable_key_service,
base::StringPiece client_id,
base::StringPiece auth_code,
const GURL& registration_url,
- base::OnceCallback<void(absl::optional<Result>)> callback)
- : unexportable_key_service_(unexportable_key_service),
- client_id_(client_id),
- auth_code_(auth_code),
- registration_url_(registration_url),
- callback_(std::move(callback)) {}
+ base::OnceCallback<void(absl::optional<Result>)> callback) {
+ HeaderAndPayloadGenerator header_and_payload_generator = base::BindRepeating(
+ &signin::CreateKeyRegistrationHeaderAndPayloadForTokenBinding,
+ std::string(client_id), std::string(auth_code), registration_url);
+ return base::WrapUnique(new RegistrationTokenHelper(
+ unexportable_key_service, std::move(header_and_payload_generator),
+ std::move(callback)));
+}
RegistrationTokenHelper::~RegistrationTokenHelper() = default;
@@ -64,6 +84,14 @@ void RegistrationTokenHelper::Start() {
weak_ptr_factory_.GetWeakPtr()));
}
+RegistrationTokenHelper::RegistrationTokenHelper(
+ unexportable_keys::UnexportableKeyService& unexportable_key_service,
+ HeaderAndPayloadGenerator header_and_payload_generator,
+ base::OnceCallback<void(absl::optional<Result>)> callback)
+ : unexportable_key_service_(unexportable_key_service),
+ header_and_payload_generator_(std::move(header_and_payload_generator)),
+ callback_(std::move(callback)) {}
+
void RegistrationTokenHelper::OnKeyGenerated(
unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>
result) {
@@ -75,10 +103,10 @@ void RegistrationTokenHelper::OnKeyGenerated(
key_id_ = *result;
absl::optional<std::string> header_and_payload =
- signin::CreateKeyRegistrationHeaderAndPayload(
+ header_and_payload_generator_.Run(
*unexportable_key_service_->GetAlgorithm(key_id_),
*unexportable_key_service_->GetSubjectPublicKeyInfo(key_id_),
- client_id_, auth_code_, registration_url_, base::Time::Now());
+ base::Time::Now());
if (!header_and_payload.has_value()) {
// TODO(alexilin): Record a histogram.
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.h b/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.h
index d3562d25b68..8c241d62c7b 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.h
+++ b/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper.h
@@ -7,15 +7,21 @@
#include <string>
+#include "base/containers/span.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_piece_forward.h"
#include "components/unexportable_keys/service_error.h"
#include "components/unexportable_keys/unexportable_key_id.h"
+#include "crypto/signature_verifier.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
+namespace base {
+class Time;
+}
+
namespace unexportable_keys {
class UnexportableKeyService;
}
@@ -46,12 +52,17 @@ class RegistrationTokenHelper {
std::string registration_token;
};
- // `unexportable_key_service` must outlive `this`.
// Invokes `callback` with a `Result` containing a new binding key ID and a
// corresponding registration token on success. Otherwise, invokes `callback`
// with `absl::nullopt`.
+ // `unexportable_key_service` must outlive `this`.
// TODO(alexilin): support timeout.
- explicit RegistrationTokenHelper(
+ static std::unique_ptr<RegistrationTokenHelper> CreateForSessionBinding(
+ unexportable_keys::UnexportableKeyService& unexportable_key_service,
+ base::StringPiece challenge,
+ const GURL& registration_url,
+ base::OnceCallback<void(absl::optional<Result>)> callback);
+ static std::unique_ptr<RegistrationTokenHelper> CreateForTokenBinding(
unexportable_keys::UnexportableKeyService& unexportable_key_service,
base::StringPiece client_id,
base::StringPiece auth_code,
@@ -63,9 +74,23 @@ class RegistrationTokenHelper {
virtual ~RegistrationTokenHelper();
- // virtual for testing.
+ // Virtual for testing.
virtual void Start();
+ protected:
+ using HeaderAndPayloadGenerator =
+ base::RepeatingCallback<absl::optional<std::string>(
+ crypto::SignatureVerifier::SignatureAlgorithm,
+ base::span<const uint8_t>,
+ base::Time)>;
+
+ // The clients should use static factory methods `CreateFor*` instead.
+ // Protected for testing.
+ explicit RegistrationTokenHelper(
+ unexportable_keys::UnexportableKeyService& unexportable_key_service,
+ HeaderAndPayloadGenerator header_and_payload_generator,
+ base::OnceCallback<void(absl::optional<Result>)> callback);
+
private:
// Callback for `GenerateSigningKeySlowlyAsync()`.
void OnKeyGenerated(
@@ -78,9 +103,7 @@ class RegistrationTokenHelper {
const raw_ref<unexportable_keys::UnexportableKeyService>
unexportable_key_service_;
- const std::string client_id_;
- const std::string auth_code_;
- const GURL registration_url_;
+ HeaderAndPayloadGenerator header_and_payload_generator_;
base::OnceCallback<void(absl::optional<Result>)> callback_;
bool started_ = false;
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper_unittest.cc
index 8cdf824d127..a7052497218 100644
--- a/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper_unittest.cc
+++ b/chromium/chrome/browser/signin/bound_session_credentials/registration_token_helper_unittest.cc
@@ -25,6 +25,19 @@ class RegistrationTokenHelperTest : public testing::Test {
void RunBackgroundTasks() { task_environment_.RunUntilIdle(); }
+ void VerifyResult(const RegistrationTokenHelper::Result& result) {
+ crypto::SignatureVerifier::SignatureAlgorithm algorithm =
+ *unexportable_key_service().GetAlgorithm(result.binding_key_id);
+ std::vector<uint8_t> pubkey =
+ *unexportable_key_service().GetSubjectPublicKeyInfo(
+ result.binding_key_id);
+
+ EXPECT_TRUE(signin::VerifyJwtSignature(result.registration_token, algorithm,
+ pubkey));
+ EXPECT_EQ(result.wrapped_binding_key,
+ unexportable_key_service().GetWrappedKey(result.binding_key_id));
+ }
+
private:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::ThreadPoolExecutionMode::
@@ -34,29 +47,32 @@ class RegistrationTokenHelperTest : public testing::Test {
unexportable_keys::UnexportableKeyServiceImpl unexportable_key_service_;
};
-TEST_F(RegistrationTokenHelperTest, Success) {
+TEST_F(RegistrationTokenHelperTest, SuccessForTokenBinding) {
crypto::ScopedMockUnexportableKeyProvider scoped_mock_key_provider_;
base::test::TestFuture<absl::optional<RegistrationTokenHelper::Result>>
future;
- RegistrationTokenHelper helper(
- unexportable_key_service(), "test_client_id", "test_auth_code",
- GURL("https://accounts.google.com/Register"), future.GetCallback());
- helper.Start();
+ std::unique_ptr<RegistrationTokenHelper> helper =
+ RegistrationTokenHelper::CreateForTokenBinding(
+ unexportable_key_service(), "test_client_id", "test_auth_code",
+ GURL("https://accounts.google.com/Register"), future.GetCallback());
+ helper->Start();
RunBackgroundTasks();
ASSERT_TRUE(future.Get().has_value());
+ VerifyResult(future.Get().value());
+}
- const auto& registration_token = future.Get()->registration_token;
- const auto& key_id = future.Get()->binding_key_id;
- crypto::SignatureVerifier::SignatureAlgorithm algorithm =
- *unexportable_key_service().GetAlgorithm(key_id);
- std::vector<uint8_t> pubkey =
- *unexportable_key_service().GetSubjectPublicKeyInfo(key_id);
-
- EXPECT_TRUE(
- signin::VerifyJwtSignature(registration_token, algorithm, pubkey));
-
- const auto& wrapped_key = future.Get()->wrapped_binding_key;
- EXPECT_EQ(wrapped_key, unexportable_key_service().GetWrappedKey(key_id));
+TEST_F(RegistrationTokenHelperTest, SuccessForSessionBinding) {
+ crypto::ScopedMockUnexportableKeyProvider scoped_mock_key_provider_;
+ base::test::TestFuture<absl::optional<RegistrationTokenHelper::Result>>
+ future;
+ std::unique_ptr<RegistrationTokenHelper> helper =
+ RegistrationTokenHelper::CreateForSessionBinding(
+ unexportable_key_service(), "test_challenge",
+ GURL("https://accounts.google.com/Register"), future.GetCallback());
+ helper->Start();
+ RunBackgroundTasks();
+ ASSERT_TRUE(future.Get().has_value());
+ VerifyResult(future.Get().value());
}
TEST_F(RegistrationTokenHelperTest, Failure) {
@@ -64,10 +80,11 @@ TEST_F(RegistrationTokenHelperTest, Failure) {
crypto::ScopedNullUnexportableKeyProvider scoped_null_key_provider_;
base::test::TestFuture<absl::optional<RegistrationTokenHelper::Result>>
future;
- RegistrationTokenHelper helper(
- unexportable_key_service(), "test_client_id", "test_auth_code",
- GURL("https://accounts.google.com/Register"), future.GetCallback());
- helper.Start();
+ std::unique_ptr<RegistrationTokenHelper> helper =
+ RegistrationTokenHelper::CreateForTokenBinding(
+ unexportable_key_service(), "test_client_id", "test_auth_code",
+ GURL("https://accounts.google.com/Register"), future.GetCallback());
+ helper->Start();
RunBackgroundTasks();
EXPECT_FALSE(future.Get().has_value());
}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper.cc b/chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper.cc
new file mode 100644
index 00000000000..6eaab99f788
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper.cc
@@ -0,0 +1,94 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/session_binding_helper.h"
+
+#include <string>
+
+#include "base/containers/span.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "components/signin/public/base/session_binding_utils.h"
+#include "components/unexportable_keys/service_error.h"
+#include "components/unexportable_keys/unexportable_key_loader.h"
+#include "components/unexportable_keys/unexportable_key_service.h"
+#include "url/gurl.h"
+
+namespace {
+
+unexportable_keys::BackgroundTaskPriority kSessionBindingPriority =
+ unexportable_keys::BackgroundTaskPriority::kUserBlocking;
+
+std::string CreateAssertionToken(
+ const std::string& header_and_payload,
+ unexportable_keys::ServiceErrorOr<std::vector<uint8_t>> signature) {
+ if (!signature.has_value()) {
+ return std::string();
+ }
+
+ return signin::AppendSignatureToHeaderAndPayload(header_and_payload,
+ *signature);
+}
+} // namespace
+
+SessionBindingHelper::SessionBindingHelper(
+ unexportable_keys::UnexportableKeyService& unexportable_key_service,
+ base::span<const uint8_t> wrapped_binding_key,
+ std::string session_id)
+ : unexportable_key_service_(unexportable_key_service),
+ wrapped_binding_key_(wrapped_binding_key.begin(),
+ wrapped_binding_key.end()),
+ session_id_(std::move(session_id)) {}
+
+SessionBindingHelper::~SessionBindingHelper() = default;
+
+void SessionBindingHelper::MaybeLoadBindingKey() {
+ if (!key_loader_) {
+ key_loader_ =
+ unexportable_keys::UnexportableKeyLoader::CreateFromWrappedKey(
+ unexportable_key_service_.get(), wrapped_binding_key_,
+ kSessionBindingPriority);
+ }
+}
+
+void SessionBindingHelper::GenerateBindingKeyAssertion(
+ base::StringPiece challenge,
+ const GURL& destination_url,
+ base::OnceCallback<void(std::string)> callback) {
+ MaybeLoadBindingKey();
+ // `base::Unretained(this)` is safe because `this` owns the
+ // `UnexportableKeyLoader`.
+ key_loader_->InvokeCallbackAfterKeyLoaded(base::BindOnce(
+ &SessionBindingHelper::SignAssertionToken, base::Unretained(this),
+ std::string(challenge), destination_url, std::move(callback)));
+}
+
+void SessionBindingHelper::SignAssertionToken(
+ base::StringPiece challenge,
+ const GURL& destination_url,
+ base::OnceCallback<void(std::string)> callback,
+ unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>
+ binding_key) {
+ if (!binding_key.has_value()) {
+ std::move(callback).Run(std::string());
+ return;
+ }
+
+ absl::optional<std::string> header_and_payload =
+ signin::CreateKeyAssertionHeaderAndPayload(
+ *unexportable_key_service_->GetAlgorithm(*binding_key),
+ *unexportable_key_service_->GetSubjectPublicKeyInfo(*binding_key),
+ session_id_, challenge, destination_url);
+
+ if (!header_and_payload.has_value()) {
+ std::move(callback).Run(std::string());
+ return;
+ }
+
+ unexportable_key_service_->SignSlowlyAsync(
+ *binding_key, base::as_bytes(base::make_span(*header_and_payload)),
+ kSessionBindingPriority,
+ base::BindOnce(&CreateAssertionToken, *header_and_payload)
+ .Then(std::move(callback)));
+}
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper.h b/chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper.h
new file mode 100644
index 00000000000..e3bd9b951eb
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper.h
@@ -0,0 +1,69 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_SESSION_BINDING_HELPER_H_
+#define CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_SESSION_BINDING_HELPER_H_
+
+#include <cstdint>
+#include "base/containers/span.h"
+#include "base/functional/callback_forward.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/raw_ref.h"
+#include "base/strings/string_piece_forward.h"
+#include "components/unexportable_keys/service_error.h"
+#include "components/unexportable_keys/unexportable_key_id.h"
+
+namespace unexportable_keys {
+class UnexportableKeyService;
+class UnexportableKeyLoader;
+} // namespace unexportable_keys
+
+class GURL;
+
+// `SessionBindingHelper` loads in-memory session binding key
+// and provides an asynchronous method of creating a binding key assertion.
+class SessionBindingHelper {
+ public:
+ SessionBindingHelper(
+ unexportable_keys::UnexportableKeyService& unexportable_key_service,
+ base::span<const uint8_t> wrapped_binding_key,
+ std::string session_id);
+
+ SessionBindingHelper(const SessionBindingHelper&) = delete;
+ SessionBindingHelper& operator=(const SessionBindingHelper&) = delete;
+
+ ~SessionBindingHelper();
+
+ // Asynchronously loads the `wrapped_binding_key_` if loading it hasn't
+ // started yet.
+ void MaybeLoadBindingKey();
+
+ // Asynchronously generates a binding key assertion with a key associated with
+ // `wrapped_binding_key` passed in the constructor. The result is returned
+ // through `callback`. Returns an empty string if the generation fails.
+ void GenerateBindingKeyAssertion(
+ base::StringPiece challenge,
+ const GURL& destination_url,
+ base::OnceCallback<void(std::string)> callback);
+
+ private:
+ friend class BoundSessionCookieControllerImplTest;
+ FRIEND_TEST_ALL_PREFIXES(SessionBindingHelperTest, MaybeLoadBindingKey);
+
+ void SignAssertionToken(
+ base::StringPiece challenge,
+ const GURL& destination_url,
+ base::OnceCallback<void(std::string)> callback,
+ unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>
+ binding_key);
+
+ const raw_ref<unexportable_keys::UnexportableKeyService>
+ unexportable_key_service_;
+ const std::vector<uint8_t> wrapped_binding_key_;
+ const std::string session_id_;
+
+ std::unique_ptr<unexportable_keys::UnexportableKeyLoader> key_loader_;
+};
+
+#endif // CHROME_BROWSER_SIGNIN_BOUND_SESSION_CREDENTIALS_SESSION_BINDING_HELPER_H_
diff --git a/chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper_unittest.cc b/chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper_unittest.cc
new file mode 100644
index 00000000000..cb259ebefe8
--- /dev/null
+++ b/chromium/chrome/browser/signin/bound_session_credentials/session_binding_helper_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/bound_session_credentials/session_binding_helper.h"
+
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "components/signin/public/base/session_binding_test_utils.h"
+#include "components/unexportable_keys/service_error.h"
+#include "components/unexportable_keys/unexportable_key_id.h"
+#include "components/unexportable_keys/unexportable_key_loader.h"
+#include "components/unexportable_keys/unexportable_key_service_impl.h"
+#include "components/unexportable_keys/unexportable_key_task_manager.h"
+#include "crypto/scoped_mock_unexportable_key_provider.h"
+#include "crypto/signature_verifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+constexpr crypto::SignatureVerifier::SignatureAlgorithm
+ kAcceptableAlgorithms[] = {crypto::SignatureVerifier::ECDSA_SHA256};
+constexpr unexportable_keys::BackgroundTaskPriority kTaskPriority =
+ unexportable_keys::BackgroundTaskPriority::kUserBlocking;
+} // namespace
+
+class SessionBindingHelperTest : public testing::Test {
+ public:
+ SessionBindingHelperTest()
+ : unexportable_key_service_(unexportable_key_task_manager_) {}
+
+ unexportable_keys::UnexportableKeyService& unexportable_key_service() {
+ return unexportable_key_service_;
+ }
+
+ unexportable_keys::UnexportableKeyId GenerateNewKey() {
+ base::test::TestFuture<
+ unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>>
+ generate_future;
+ unexportable_key_service_.GenerateSigningKeySlowlyAsync(
+ kAcceptableAlgorithms, kTaskPriority, generate_future.GetCallback());
+ unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>
+ key_id = generate_future.Get();
+ CHECK(key_id.has_value());
+ return *key_id;
+ }
+
+ std::vector<uint8_t> GetWrappedKey(
+ const unexportable_keys::UnexportableKeyId& key_id) {
+ unexportable_keys::ServiceErrorOr<std::vector<uint8_t>> wrapped_key =
+ unexportable_key_service_.GetWrappedKey(key_id);
+ CHECK(wrapped_key.has_value());
+ return *wrapped_key;
+ }
+
+ private:
+ base::test::TaskEnvironment task_environment_;
+ crypto::ScopedMockUnexportableKeyProvider scoped_key_provider_;
+ unexportable_keys::UnexportableKeyTaskManager unexportable_key_task_manager_;
+ unexportable_keys::UnexportableKeyServiceImpl unexportable_key_service_;
+};
+
+TEST_F(SessionBindingHelperTest, MaybeLoadBindingKey) {
+ unexportable_keys::UnexportableKeyId key_id = GenerateNewKey();
+ SessionBindingHelper helper(unexportable_key_service(), GetWrappedKey(key_id),
+ "session_id");
+ EXPECT_FALSE(helper.key_loader_);
+ helper.MaybeLoadBindingKey();
+ unexportable_keys::UnexportableKeyLoader* key_loader =
+ helper.key_loader_.get();
+ EXPECT_TRUE(key_loader);
+ EXPECT_NE(key_loader->GetStateForTesting(),
+ unexportable_keys::UnexportableKeyLoader::State::kNotStarted);
+ base::test::TestFuture<
+ unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>>
+ key_future;
+ key_loader->InvokeCallbackAfterKeyLoaded(key_future.GetCallback());
+ EXPECT_EQ(*key_future.Get(), key_id);
+}
+
+TEST_F(SessionBindingHelperTest, GenerateBindingKeyAssertion) {
+ unexportable_keys::UnexportableKeyId key_id = GenerateNewKey();
+ SessionBindingHelper helper(unexportable_key_service(), GetWrappedKey(key_id),
+ "session_id");
+ base::test::TestFuture<std::string> sign_future;
+ helper.GenerateBindingKeyAssertion(
+ "challenge", GURL("https://accounts.google.com/RotateBoundCookies"),
+ sign_future.GetCallback());
+ std::string assertion = sign_future.Get();
+ EXPECT_FALSE(assertion.empty());
+
+ EXPECT_TRUE(signin::VerifyJwtSignature(
+ assertion, *unexportable_key_service().GetAlgorithm(key_id),
+ *unexportable_key_service().GetSubjectPublicKeyInfo(key_id)));
+}
+
+TEST_F(SessionBindingHelperTest, GenerateBindingKeyAssertionInvalidBindingKey) {
+ const std::vector<uint8_t> kInvalidWrappedKey = {1, 2, 3};
+ SessionBindingHelper helper(unexportable_key_service(), kInvalidWrappedKey,
+ "session_id");
+
+ base::test::TestFuture<std::string> sign_future;
+ helper.GenerateBindingKeyAssertion(
+ "challenge", GURL("https://accounts.google.com/RotateBoundCookies"),
+ sign_future.GetCallback());
+ std::string assertion = sign_future.Get();
+ EXPECT_TRUE(assertion.empty());
+}
diff --git a/chromium/chrome/browser/signin/chrome_device_id_helper.cc b/chromium/chrome/browser/signin/chrome_device_id_helper.cc
index ffe0f430899..be9badcd625 100644
--- a/chromium/chrome/browser/signin/chrome_device_id_helper.cc
+++ b/chromium/chrome/browser/signin/chrome_device_id_helper.cc
@@ -25,11 +25,6 @@
std::string GetSigninScopedDeviceIdForProfile(Profile* profile) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableSigninScopedDeviceId)) {
- return std::string();
- }
-
// UserManager may not exist in unit_tests.
if (!user_manager::UserManager::IsInitialized())
return std::string();
diff --git a/chromium/chrome/browser/signin/chrome_signin_client.cc b/chromium/chrome/browser/signin/chrome_signin_client.cc
index 98b6dfbeb22..3936ce7b381 100644
--- a/chromium/chrome/browser/signin/chrome_signin_client.cc
+++ b/chromium/chrome/browser/signin/chrome_signin_client.cc
@@ -44,8 +44,8 @@
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/scope_set.h"
#include "components/supervised_user/core/common/buildflags.h"
+#include "components/version_info/channel.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
@@ -56,7 +56,7 @@
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/ash/net/delay_network_call.h"
+#include "chrome/browser/signin/wait_for_network_callback_helper_ash.h"
#include "chromeos/ash/components/network/network_handler.h"
#endif
@@ -75,9 +75,12 @@
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#endif
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/signin/wait_for_network_callback_helper_chrome.h"
+#endif
namespace {
// List of sources for which sign out is always allowed.
@@ -106,17 +109,18 @@ signin_metrics::ProfileSignout kAlwaysAllowedSignoutSources[] = {
} // namespace
-ChromeSigninClient::ChromeSigninClient(Profile* profile) : profile_(profile) {
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
- content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
+ChromeSigninClient::ChromeSigninClient(Profile* profile)
+ : wait_for_network_callback_helper_(
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ std::make_unique<WaitForNetworkCallbackHelperAsh>()
+#else
+ std::make_unique<WaitForNetworkCallbackHelperChrome>()
#endif
+ ),
+ profile_(profile) {
}
-ChromeSigninClient::~ChromeSigninClient() {
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
- content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
-#endif
-}
+ChromeSigninClient::~ChromeSigninClient() = default;
void ChromeSigninClient::DoFinalInit() {
VerifySyncToken();
@@ -124,9 +128,9 @@ void ChromeSigninClient::DoFinalInit() {
// static
bool ChromeSigninClient::ProfileAllowsSigninCookies(Profile* profile) {
- content_settings::CookieSettings* cookie_settings =
- CookieSettingsFactory::GetForProfile(profile).get();
- return signin::SettingsAllowSigninCookies(cookie_settings);
+ scoped_refptr<content_settings::CookieSettings> cookie_settings =
+ CookieSettingsFactory::GetForProfile(profile);
+ return signin::SettingsAllowSigninCookies(cookie_settings.get());
}
PrefService* ChromeSigninClient::GetPrefs() { return profile_->GetPrefs(); }
@@ -150,9 +154,9 @@ bool ChromeSigninClient::AreSigninCookiesAllowed() {
}
bool ChromeSigninClient::AreSigninCookiesDeletedOnExit() {
- content_settings::CookieSettings* cookie_settings =
- CookieSettingsFactory::GetForProfile(profile_).get();
- return signin::SettingsDeleteSigninCookiesOnExit(cookie_settings);
+ scoped_refptr<content_settings::CookieSettings> cookie_settings =
+ CookieSettingsFactory::GetForProfile(profile_);
+ return signin::SettingsDeleteSigninCookiesOnExit(cookie_settings.get());
}
void ChromeSigninClient::AddContentSettingsObserver(
@@ -225,56 +229,12 @@ void ChromeSigninClient::PreSignOut(
}
}
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-void ChromeSigninClient::OnConnectionChanged(
- network::mojom::ConnectionType type) {
- if (type == network::mojom::ConnectionType::CONNECTION_NONE)
- return;
-
- for (base::OnceClosure& callback : delayed_callbacks_)
- std::move(callback).Run();
-
- delayed_callbacks_.clear();
-}
-#endif
-
bool ChromeSigninClient::AreNetworkCallsDelayed() {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- // Do not make network requests in unit tests. ash::NetworkHandler should
- // not be used and is not expected to have been initialized in unit tests.
- if (url_loader_factory_for_testing_ &&
- !ash::NetworkHandler::IsInitialized()) {
- return false;
- }
-
- return ash::AreNetworkCallsDelayed();
-#else
- // Don't bother if we don't have any kind of network connection.
- network::mojom::ConnectionType type;
- bool sync = content::GetNetworkConnectionTracker()->GetConnectionType(
- &type, base::BindOnce(&ChromeSigninClient::OnConnectionChanged,
- weak_ptr_factory_.GetWeakPtr()));
- if (!sync || type == network::mojom::ConnectionType::CONNECTION_NONE) {
- // Connection type cannot be retrieved synchronously so delay the callback.
- return true;
- }
-
- return false;
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ return wait_for_network_callback_helper_->AreNetworkCallsDelayed();
}
void ChromeSigninClient::DelayNetworkCall(base::OnceClosure callback) {
- if (!AreNetworkCallsDelayed()) {
- std::move(callback).Run();
- return;
- }
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- ash::DelayNetworkCall(std::move(callback));
-#else
- // This queue will be processed in `OnConnectionChanged()`.
- delayed_callbacks_.push_back(std::move(callback));
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ wait_for_network_callback_helper_->DelayNetworkCall(std::move(callback));
}
std::unique_ptr<GaiaAuthFetcher> ChromeSigninClient::CreateGaiaAuthFetcher(
@@ -284,6 +244,10 @@ std::unique_ptr<GaiaAuthFetcher> ChromeSigninClient::CreateGaiaAuthFetcher(
GetURLLoaderFactory());
}
+version_info::Channel ChromeSigninClient::GetClientChannel() {
+ return chrome::GetChannel();
+}
+
SigninClient::SignoutDecision ChromeSigninClient::GetSignoutDecision(
bool has_sync_account,
const absl::optional<signin_metrics::ProfileSignout> signout_source) const {
@@ -417,6 +381,14 @@ void ChromeSigninClient::RemoveAllAccounts() {
void ChromeSigninClient::SetURLLoaderFactoryForTest(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
url_loader_factory_for_testing_ = url_loader_factory;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // Do not make network requests in unit tests. ash::NetworkHandler should
+ // not be used and is not expected to have been initialized in unit tests.
+ wait_for_network_callback_helper_
+ ->DisableNetworkCallsDelayedForTesting( // IN-TEST
+ url_loader_factory_for_testing_ &&
+ !ash::NetworkHandler::IsInitialized());
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
void ChromeSigninClient::OnCloseBrowsersSuccess(
diff --git a/chromium/chrome/browser/signin/chrome_signin_client.h b/chromium/chrome/browser/signin/chrome_signin_client.h
index dd213526bb2..9a5172848e6 100644
--- a/chromium/chrome/browser/signin/chrome_signin_client.h
+++ b/chromium/chrome/browser/signin/chrome_signin_client.h
@@ -11,29 +11,23 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/signin/public/base/signin_client.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/mojom/network_change_manager.mojom-forward.h"
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-#include "services/network/public/cpp/network_connection_tracker.h"
-#endif
+class WaitForNetworkCallbackHelper;
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
class ForceSigninVerifier;
#endif
class Profile;
-class ChromeSigninClient
- : public SigninClient
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
- ,
- public network::NetworkConnectionTracker::NetworkConnectionObserver
-#endif
-{
+namespace version_info {
+enum class Channel;
+}
+
+class ChromeSigninClient : public SigninClient {
public:
explicit ChromeSigninClient(Profile* profile);
@@ -81,12 +75,7 @@ class ChromeSigninClient
std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
GaiaAuthConsumer* consumer,
gaia::GaiaSource source) override;
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
- // network::NetworkConnectionTracker::NetworkConnectionObserver
- // implementation.
- void OnConnectionChanged(network::mojom::ConnectionType type) override;
-#endif
+ version_info::Channel GetClientChannel() override;
#if BUILDFLAG(IS_CHROMEOS_LACROS)
absl::optional<account_manager::Account> GetInitialPrimaryAccount() override;
@@ -120,15 +109,13 @@ class ChromeSigninClient
const base::FilePath& profile_path);
void OnCloseBrowsersAborted(const base::FilePath& profile_path);
+ const std::unique_ptr<WaitForNetworkCallbackHelper>
+ wait_for_network_callback_helper_;
raw_ptr<Profile, DanglingUntriaged> profile_;
// Stored callback from PreSignOut();
base::OnceCallback<void(SignoutDecision)> on_signout_decision_reached_;
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
- std::list<base::OnceClosure> delayed_callbacks_;
-#endif
-
bool should_display_user_manager_ = true;
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
std::unique_ptr<ForceSigninVerifier> force_signin_verifier_;
@@ -136,8 +123,6 @@ class ChromeSigninClient
scoped_refptr<network::SharedURLLoaderFactory>
url_loader_factory_for_testing_;
-
- base::WeakPtrFactory<ChromeSigninClient> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_SIGNIN_CHROME_SIGNIN_CLIENT_H_
diff --git a/chromium/chrome/browser/signin/chrome_signin_client_unittest.cc b/chromium/chrome/browser/signin/chrome_signin_client_unittest.cc
index 685250085c1..428d932f031 100644
--- a/chromium/chrome/browser/signin/chrome_signin_client_unittest.cc
+++ b/chromium/chrome/browser/signin/chrome_signin_client_unittest.cc
@@ -8,16 +8,11 @@
#include <utility>
#include "base/functional/bind.h"
-#include "base/memory/raw_ptr.h"
-#include "base/notreached.h"
-#include "base/run_loop.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/enterprise/util/managed_browser_utils.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_attributes_entry.h"
-#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/signin/chrome_signin_client_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_util.h"
@@ -26,14 +21,10 @@
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/prefs/pref_service.h"
-#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/supervised_user/core/common/buildflags.h"
-#include "content/public/browser/network_service_instance.h"
-#include "content/public/test/browser_task_environment.h"
-#include "services/network/test/test_network_connection_tracker.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -41,107 +32,7 @@
#include "chrome/test/base/browser_with_test_window_test.h"
#endif
-// ChromeOS has its own network delay logic.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-
-namespace {
-
-class CallbackTester {
- public:
- CallbackTester() : called_(0) {}
-
- void Increment();
- void IncrementAndUnblock(base::RunLoop* run_loop);
- bool WasCalledExactlyOnce();
-
- private:
- int called_;
-};
-
-void CallbackTester::Increment() {
- called_++;
-}
-
-void CallbackTester::IncrementAndUnblock(base::RunLoop* run_loop) {
- Increment();
- run_loop->QuitWhenIdle();
-}
-
-bool CallbackTester::WasCalledExactlyOnce() {
- return called_ == 1;
-}
-
-} // namespace
-
-class ChromeSigninClientTest : public testing::Test {
- public:
- ChromeSigninClientTest() {
- // Create a signed-in profile.
- TestingProfile::Builder builder;
- profile_ = builder.Build();
-
- signin_client_ = ChromeSigninClientFactory::GetForProfile(profile());
- }
-
- protected:
- void SetUpNetworkConnection(bool respond_synchronously,
- network::mojom::ConnectionType connection_type) {
- auto* tracker = network::TestNetworkConnectionTracker::GetInstance();
- tracker->SetRespondSynchronously(respond_synchronously);
- tracker->SetConnectionType(connection_type);
- }
-
- void SetConnectionType(network::mojom::ConnectionType connection_type) {
- network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
- connection_type);
- }
-
- Profile* profile() { return profile_.get(); }
- SigninClient* signin_client() { return signin_client_; }
-
- private:
- content::BrowserTaskEnvironment task_environment_;
- std::unique_ptr<Profile> profile_;
- raw_ptr<SigninClient> signin_client_;
-};
-
-TEST_F(ChromeSigninClientTest, DelayNetworkCallRunsImmediatelyWithNetwork) {
- SetUpNetworkConnection(true, network::mojom::ConnectionType::CONNECTION_3G);
- CallbackTester tester;
- signin_client()->DelayNetworkCall(
- base::BindOnce(&CallbackTester::Increment, base::Unretained(&tester)));
- ASSERT_TRUE(tester.WasCalledExactlyOnce());
-}
-
-TEST_F(ChromeSigninClientTest, DelayNetworkCallRunsAfterGetConnectionType) {
- SetUpNetworkConnection(false, network::mojom::ConnectionType::CONNECTION_3G);
-
- base::RunLoop run_loop;
- CallbackTester tester;
- signin_client()->DelayNetworkCall(
- base::BindOnce(&CallbackTester::IncrementAndUnblock,
- base::Unretained(&tester), &run_loop));
- ASSERT_FALSE(tester.WasCalledExactlyOnce());
- run_loop.Run(); // Wait for IncrementAndUnblock().
- ASSERT_TRUE(tester.WasCalledExactlyOnce());
-}
-
-TEST_F(ChromeSigninClientTest, DelayNetworkCallRunsAfterNetworkChange) {
- SetUpNetworkConnection(true, network::mojom::ConnectionType::CONNECTION_NONE);
-
- base::RunLoop run_loop;
- CallbackTester tester;
- signin_client()->DelayNetworkCall(
- base::BindOnce(&CallbackTester::IncrementAndUnblock,
- base::Unretained(&tester), &run_loop));
-
- ASSERT_FALSE(tester.WasCalledExactlyOnce());
- SetConnectionType(network::mojom::ConnectionType::CONNECTION_3G);
- run_loop.Run(); // Wait for IncrementAndUnblock().
- ASSERT_TRUE(tester.WasCalledExactlyOnce());
-}
-
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID)
class MockChromeSigninClient : public ChromeSigninClient {
public:
@@ -306,6 +197,8 @@ bool IsAlwaysAllowedSignoutSources(
case signin_metrics::ProfileSignout::kAccountEmailUpdated:
case signin_metrics::ProfileSignout::kSigninManagerUpdateUPA:
case signin_metrics::ProfileSignout::kUserTappedUndoRightAfterSignIn:
+ case signin_metrics::ProfileSignout::
+ kUserDeclinedHistorySyncAfterDedicatedSignIn:
return false;
case signin_metrics::ProfileSignout::kAccountRemovedFromDevice:
@@ -455,6 +348,8 @@ const signin_metrics::ProfileSignout kSignoutSources[] = {
signin_metrics::ProfileSignout::kAccountReconcilorReconcile,
signin_metrics::ProfileSignout::kSigninManagerUpdateUPA,
signin_metrics::ProfileSignout::kUserTappedUndoRightAfterSignIn,
+ signin_metrics::ProfileSignout::
+ kUserDeclinedHistorySyncAfterDedicatedSignIn,
};
// kNumberOfObsoleteSignoutSources should be updated when a ProfileSignout
// value is deprecated.
@@ -469,5 +364,4 @@ INSTANTIATE_TEST_SUITE_P(AllSignoutSources,
ChromeSigninClientSignoutSourceTest,
testing::ValuesIn(kSignoutSources));
-#endif // !BUILDFLAG(IS_ANDROID)
-#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID)
diff --git a/chromium/chrome/browser/signin/chrome_signin_helper.cc b/chromium/chrome/browser/signin/chrome_signin_helper.cc
index 3be5a98f9d6..26645b5a76b 100644
--- a/chromium/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chromium/chrome/browser/signin/chrome_signin_helper.cc
@@ -25,6 +25,7 @@
#include "chrome/browser/signin/signin_ui_util.h"
#include "chrome/browser/ui/webui/signin/signin_url_utils.h"
#include "components/account_manager_core/account_manager_facade.h"
+#include "components/google/core/common/google_util.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/signin/core/browser/account_reconcilor.h"
#include "components/signin/public/base/account_consistency_method.h"
@@ -70,8 +71,6 @@
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
#include "chrome/browser/signin/dice_response_handler.h"
#include "chrome/browser/signin/process_dice_header_delegate_impl.h"
-#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
-#include "chrome/browser/ui/webui/signin/turn_sync_on_helper.h"
#endif
namespace signin {
@@ -187,7 +186,8 @@ class ManageAccountsHeaderReceivedUserData
// opens an incognito window/tab.
void ProcessMirrorHeader(
ManageAccountsParams manage_accounts_params,
- const content::WebContents::Getter& web_contents_getter) {
+ const content::WebContents::Getter& web_contents_getter,
+ const absl::optional<url::Origin>& request_initiator) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
GAIAServiceType service_type = manage_accounts_params.service_type;
@@ -202,6 +202,26 @@ void ProcessMirrorHeader(
DCHECK(AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile))
<< "Gaia should not send the X-Chrome-Manage-Accounts header "
<< "when Mirror is disabled.";
+
+ // Do not allow non-Google origins to open incognito windows.
+ // TODO(crbug.com/1449357): Expand this check to all Mirror headers,
+ // regardless of `service_type`.
+ if (service_type == GAIA_SERVICE_TYPE_INCOGNITO &&
+ base::FeatureList::IsEnabled(kVerifyRequestInitiatorForMirrorHeaders)) {
+ GURL initiator_url =
+ request_initiator ? request_initiator->GetURL() : GURL();
+ const bool is_request_initiated_by_google_domain =
+ google_util::IsGoogleAssociatedDomainUrl(initiator_url);
+ base::UmaHistogramBoolean(
+ "Signin.ProcessMirrorHeaders.AllowedFromInitiator.GoIncognito",
+ is_request_initiated_by_google_domain);
+ if (!is_request_initiated_by_google_domain) {
+ VLOG(1) << "Mirror header with GAIA_SERVICE_TYPE_INCOGNITO from "
+ << "untrusted domain (" << initiator_url << "), ignoring";
+ return;
+ }
+ }
+
AccountReconcilor* account_reconcilor =
AccountReconcilorFactory::GetForProfile(profile);
account_reconcilor->OnReceivedManageAccountsResponse(service_type);
@@ -210,21 +230,9 @@ void ProcessMirrorHeader(
signin_metrics::LogAccountReconcilorStateOnGaiaResponse(
account_reconcilor->GetState());
- bool should_process_guest_webview_request = false;
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- // The mirror headers from some guest web views need to be processed.
- bool is_guest = extensions::WebViewRendererState::GetInstance()->IsGuest(
- web_contents->GetPrimaryMainFrame()->GetProcess()->GetID());
- should_process_guest_webview_request =
- is_guest &&
- !HeaderModificationDelegateImpl::ShouldIgnoreGuestWebViewRequest(
- web_contents);
-#endif // BUILDFLAG(ENABLE_EXTENSIONS)
-
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
// Do not do anything if the navigation happened in the "background".
- if ((!browser || !browser->window()->IsActive()) &&
- !should_process_guest_webview_request) {
+ if (!browser || !browser->window()->IsActive()) {
return;
}
@@ -363,36 +371,6 @@ void ProcessMirrorHeader(
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
-// Creates a TurnSyncOnHelper.
-void CreateTurnSyncOnHelper(Profile* profile,
- signin_metrics::AccessPoint access_point,
- signin_metrics::PromoAction promo_action,
- signin_metrics::Reason reason,
- content::WebContents* web_contents,
- const CoreAccountId& account_id) {
- DCHECK(profile);
- Browser* browser = web_contents
- ? chrome::FindBrowserWithWebContents(web_contents)
- : chrome::FindBrowserWithProfile(profile);
- // TurnSyncOnHelper is suicidal (it will kill itself once it finishes enabling
- // sync).
- new TurnSyncOnHelper(profile, browser, access_point, promo_action, reason,
- account_id,
- TurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
-}
-
-// Shows UI for signin errors.
-void ShowDiceSigninError(Profile* profile,
- content::WebContents* web_contents,
- const SigninUIError& error) {
- DCHECK(profile);
- Browser* browser = web_contents
- ? chrome::FindBrowserWithWebContents(web_contents)
- : chrome::FindBrowserWithProfile(profile);
- LoginUIServiceFactory::GetForProfile(profile)->DisplayLoginResult(
- browser, error, /*from_profile_picker=*/false);
-}
-
void ProcessDiceHeader(
const DiceResponseParams& dice_params,
const content::WebContents::Getter& web_contents_getter) {
@@ -413,9 +391,7 @@ void ProcessDiceHeader(
DiceResponseHandler* dice_response_handler =
DiceResponseHandler::GetForProfile(profile);
dice_response_handler->ProcessDiceHeader(
- dice_params, ProcessDiceHeaderDelegateImpl::Create(
- web_contents, base::BindOnce(&CreateTurnSyncOnHelper),
- base::BindOnce(&ShowDiceSigninError)));
+ dice_params, ProcessDiceHeaderDelegateImpl::Create(web_contents));
}
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -468,7 +444,8 @@ void ProcessMirrorResponseHeaderIfExists(ResponseAdapter* response,
// requests while processing a throttle event.
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(ProcessMirrorHeader, params,
- response->GetWebContentsGetter()));
+ response->GetWebContentsGetter(),
+ response->GetRequestInitiator()));
}
#endif
diff --git a/chromium/chrome/browser/signin/chrome_signin_helper.h b/chromium/chrome/browser/signin/chrome_signin_helper.h
index c4bd6d706c9..4833e031564 100644
--- a/chromium/chrome/browser/signin/chrome_signin_helper.h
+++ b/chromium/chrome/browser/signin/chrome_signin_helper.h
@@ -81,6 +81,7 @@ class ResponseAdapter {
virtual content::WebContents::Getter GetWebContentsGetter() const = 0;
virtual bool IsOutermostMainFrame() const = 0;
virtual GURL GetUrl() const = 0;
+ virtual absl::optional<url::Origin> GetRequestInitiator() const = 0;
virtual const net::HttpResponseHeaders* GetHeaders() const = 0;
virtual void RemoveHeader(const std::string& name) = 0;
diff --git a/chromium/chrome/browser/signin/chrome_signin_helper_unittest.cc b/chromium/chrome/browser/signin/chrome_signin_helper_unittest.cc
index 6c410e689a0..549f93a4ad2 100644
--- a/chromium/chrome/browser/signin/chrome_signin_helper_unittest.cc
+++ b/chromium/chrome/browser/signin/chrome_signin_helper_unittest.cc
@@ -83,6 +83,10 @@ class TestResponseAdapter : public signin::ResponseAdapter,
return is_outermost_main_frame_;
}
GURL GetUrl() const override { return GURL("https://accounts.google.com"); }
+ absl::optional<url::Origin> GetRequestInitiator() const override {
+ // Pretend the request came from the same origin.
+ return url::Origin::Create(GetUrl());
+ }
const net::HttpResponseHeaders* GetHeaders() const override {
return headers_.get();
}
diff --git a/chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc b/chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc
index 270c95e99e2..da647d31524 100644
--- a/chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc
+++ b/chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc
@@ -193,7 +193,11 @@ class ProxyingURLLoaderFactory::InProgressRequest
// Information about the current request.
GURL request_url_;
GURL response_url_;
+ // Refers to the "last" referrer in the redirect chain.
GURL referrer_;
+ // The origin that initiated the request. May be empty for browser-initiated
+ // requests. See network::ResourceRequest::request_initiator for details.
+ absl::optional<url::Origin> request_initiator_;
net::HttpRequestHeaders headers_;
net::HttpRequestHeaders cors_exempt_headers_;
net::RedirectInfo redirect_info_;
@@ -287,6 +291,10 @@ class ProxyingURLLoaderFactory::InProgressRequest::ProxyResponseAdapter
GURL GetUrl() const override { return in_progress_request_->response_url_; }
+ absl::optional<url::Origin> GetRequestInitiator() const override {
+ return in_progress_request_->request_initiator_;
+ }
+
const net::HttpResponseHeaders* GetHeaders() const override {
return headers_;
}
@@ -322,6 +330,7 @@ ProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
request_url_(request.url),
response_url_(request.url),
referrer_(request.referrer),
+ request_initiator_(request.request_initiator),
request_destination_(request.destination),
is_outermost_main_frame_(request.is_outermost_main_frame),
is_fetch_like_api_(request.is_fetch_like_api),
diff --git a/chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.cc b/chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.cc
index 0d83c6b971b..47f880f8511 100644
--- a/chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.cc
+++ b/chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.cc
@@ -81,6 +81,10 @@ class URLLoaderThrottle::ThrottleResponseAdapter : public ResponseAdapter {
GURL GetUrl() const override { return throttle_->request_url_; }
+ absl::optional<url::Origin> GetRequestInitiator() const override {
+ return throttle_->request_initiator_;
+ }
+
const net::HttpResponseHeaders* GetHeaders() const override {
return headers_;
}
@@ -124,6 +128,7 @@ void URLLoaderThrottle::WillStartRequest(network::ResourceRequest* request,
bool* defer) {
request_url_ = request->url;
request_referrer_ = request->referrer;
+ request_initiator_ = request->request_initiator;
request_destination_ = request->destination;
is_outermost_main_frame_ = request->is_outermost_main_frame;
request_is_fetch_like_api_ = request->is_fetch_like_api;
diff --git a/chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.h b/chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.h
index b8fcab8c4e7..ebb89cf7684 100644
--- a/chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.h
+++ b/chromium/chrome/browser/signin/chrome_signin_url_loader_throttle.h
@@ -57,7 +57,11 @@ class URLLoaderThrottle : public blink::URLLoaderThrottle,
// Information about the current request.
GURL request_url_;
+ // Refers to the "last" referrer in the redirect chain.
GURL request_referrer_;
+ // The origin that initiated the request. May be empty for browser-initiated
+ // requests. See network::ResourceRequest::request_initiator for details.
+ absl::optional<url::Origin> request_initiator_;
net::HttpRequestHeaders request_headers_;
net::HttpRequestHeaders request_cors_exempt_headers_;
network::mojom::RequestDestination request_destination_ =
diff --git a/chromium/chrome/browser/signin/dice_browsertest.cc b/chromium/chrome/browser/signin/dice_browsertest.cc
index 768a7ad433c..33b6f5f2932 100644
--- a/chromium/chrome/browser/signin/dice_browsertest.cc
+++ b/chromium/chrome/browser/signin/dice_browsertest.cc
@@ -20,6 +20,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
+#include "base/test/bind.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -38,7 +39,6 @@
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/sync/user_event_service_factory.h"
#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/profile_chooser_constants.h"
#include "chrome/browser/ui/simple_message_box_internal.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
@@ -64,14 +64,14 @@
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h"
#include "components/sync/base/pref_names.h"
-#include "components/sync/base/sync_prefs.h"
+#include "components/sync/service/sync_prefs.h"
#include "components/sync_user_events/user_event_service.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/load_notification_details.h"
+#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "google_apis/gaia/gaia_switches.h"
#include "google_apis/gaia/gaia_urls.h"
@@ -726,7 +726,7 @@ class DiceBrowserTestWithBoundSessionCredentialsEnabled
private:
base::test::ScopedFeatureList scoped_feature_list_{
- switches::kEnableBoundSessionCrendentials};
+ switches::kEnableBoundSessionCredentials};
};
// Checks that signin on Gaia triggers the fetch for a refresh token.
@@ -932,85 +932,6 @@ IN_PROC_BROWSER_TEST_F(DiceBrowserTest, MAYBE_NoDiceFromWebUI) {
WaitForReconcilorUnblockedCount(0);
}
-// These tests should be removed once `features::kWebAuthFlowInBrowserTab` is
-// launched.
-class DiceBrowserTestWithWebAuthFlowInBrowserTabOff : public DiceBrowserTest {
- public:
- DiceBrowserTestWithWebAuthFlowInBrowserTabOff() {
- scoped_feature_list_.InitAndDisableFeature(
- features::kWebAuthFlowInBrowserTab);
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(DiceBrowserTestWithWebAuthFlowInBrowserTabOff,
- NoDiceExtensionConsent_LaunchWebAuthFlow) {
- auto web_auth_flow = std::make_unique<extensions::WebAuthFlow>(
- nullptr, browser()->profile(), https_server_.GetURL(kSigninURL),
- extensions::WebAuthFlow::INTERACTIVE,
- extensions::WebAuthFlow::LAUNCH_WEB_AUTH_FLOW,
- /*user_gesture=*/true);
- web_auth_flow->Start();
-
- if (dice_request_header_.empty()) {
- WaitForClosure(&signin_requested_quit_closure_);
- }
-
- EXPECT_EQ(kNoDiceRequestHeader, dice_request_header_);
- EXPECT_EQ(0, reconcilor_blocked_count_);
- WaitForReconcilorUnblockedCount(0);
-
- // Delete the web auth flow (uses DeleteSoon).
- web_auth_flow.release()->DetachDelegateAndDelete();
- base::RunLoop().RunUntilIdle();
-}
-
-IN_PROC_BROWSER_TEST_F(DiceBrowserTestWithWebAuthFlowInBrowserTabOff,
- DiceExtensionConsent_GetAuthToken) {
- // Signin from extension consent flow.
- class DummyDelegate : public extensions::WebAuthFlow::Delegate {
- public:
- void OnAuthFlowFailure(extensions::WebAuthFlow::Failure failure) override {}
- ~DummyDelegate() override = default;
- };
-
- DummyDelegate delegate;
- auto web_auth_flow = std::make_unique<extensions::WebAuthFlow>(
- &delegate, browser()->profile(), https_server_.GetURL(kSigninURL),
- extensions::WebAuthFlow::INTERACTIVE,
- extensions::WebAuthFlow::GET_AUTH_TOKEN,
- /*user_gesture=*/true);
- web_auth_flow->Start();
-
- // Check that the token was requested and added to the token service.
- SendRefreshTokenResponse();
- EXPECT_TRUE(
- GetIdentityManager()->HasAccountWithRefreshToken(GetMainAccountID()));
-
- // Check that the Dice request header was sent.
- std::string client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
- EXPECT_EQ(base::StringPrintf("version=%s,client_id=%s,device_id=%s,"
- "signin_mode=all_accounts,"
- "signout_mode=show_confirmation",
- signin::kDiceProtocolVersion, client_id.c_str(),
- GetDeviceId().c_str()),
- dice_request_header_);
-
- // Sync should not be enabled.
- EXPECT_EQ(absl::nullopt,
- signin::GetPrimaryAccountConsentLevel(GetIdentityManager()));
-
- EXPECT_EQ(1, reconcilor_blocked_count_);
- WaitForReconcilorUnblockedCount(1);
- EXPECT_EQ(1, reconcilor_started_count_);
-
- // Delete the web auth flow (uses DeleteSoon).
- web_auth_flow.release()->DetachDelegateAndDelete();
- base::RunLoop().RunUntilIdle();
-}
-
// Tests that Sync is enabled if the ENABLE_SYNC response is received after the
// refresh token.
IN_PROC_BROWSER_TEST_F(DiceBrowserTest, EnableSyncAfterToken) {
@@ -1020,8 +941,10 @@ IN_PROC_BROWSER_TEST_F(DiceBrowserTest, EnableSyncAfterToken) {
// Signin using the Chrome Sync endpoint.
signin_metrics::AccessPoint access_point =
signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS;
- browser()->signin_view_controller()->ShowSignin(
- profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN, access_point);
+ browser()->signin_view_controller()->ShowDiceEnableSyncTab(
+ access_point,
+ signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
+ /*email_hint=*/std::string());
// Receive token.
EXPECT_FALSE(
@@ -1042,17 +965,22 @@ IN_PROC_BROWSER_TEST_F(DiceBrowserTest, EnableSyncAfterToken) {
GetDeviceId().c_str()),
dice_request_header_);
- content::WindowedNotificationObserver ntp_url_observer(
- content::NOTIFICATION_LOAD_STOP,
- base::BindRepeating([](const content::NotificationSource&,
- const content::NotificationDetails& details) {
- auto url =
- content::Details<content::LoadNotificationDetails>(details)->url;
- // Some test flags (e.g. ForceWebRequestProxyForTest) can change whether
- // the reported NTP URL is chrome://newtab or chrome://new-tab-page.
- return url == GURL(chrome::kChromeUINewTabPageURL) ||
- url == GURL(chrome::kChromeUINewTabURL);
- }));
+ content::WebContents* tab_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ base::RunLoop ntp_run_loop;
+ content::DidFinishNavigationObserver ntp_url_observer(
+ tab_contents,
+ base::BindLambdaForTesting(
+ [&ntp_run_loop](content::NavigationHandle* navigation_handle) {
+ const GURL& url = navigation_handle->GetURL();
+ // Some test flags (e.g. ForceWebRequestProxyForTest) can change
+ // whether the reported NTP URL is chrome://newtab or
+ // chrome://new-tab-page.
+ if (url == GURL(chrome::kChromeUINewTabPageURL) ||
+ url == GURL(chrome::kChromeUINewTabURL)) {
+ ntp_run_loop.Quit();
+ }
+ }));
WaitForSigninSucceeded();
EXPECT_EQ(GetMainAccountID(), GetIdentityManager()->GetPrimaryAccountId(
@@ -1067,7 +995,7 @@ IN_PROC_BROWSER_TEST_F(DiceBrowserTest, EnableSyncAfterToken) {
EXPECT_EQ(1, reconcilor_started_count_);
// Check that the tab was navigated to the NTP.
- ntp_url_observer.Wait();
+ ntp_run_loop.Run();
// Dismiss the Sync confirmation UI.
EXPECT_TRUE(login_ui_test_utils::ConfirmSyncConfirmationDialog(browser()));
@@ -1091,7 +1019,6 @@ IN_PROC_BROWSER_TEST_F(DiceBrowserTest, MAYBE_EnableSyncBeforeToken) {
// Signin using the Chrome Sync endpoint.
browser()->signin_view_controller()->ShowSignin(
- profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN,
signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
// Receive ENABLE_SYNC.
@@ -1145,7 +1072,6 @@ IN_PROC_BROWSER_TEST_F(DiceBrowserTest,
CloseBrowserWhileInitializingSyncConfirmation) {
// Signin using the Chrome Sync endpoint.
browser()->signin_view_controller()->ShowSignin(
- profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN,
signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
content::TestNavigationObserver sync_confirmation_url_observer(
diff --git a/chromium/chrome/browser/signin/dice_response_handler.cc b/chromium/chrome/browser/signin/dice_response_handler.cc
index 9cf1f5675d6..4ed9eedc5cb 100644
--- a/chromium/chrome/browser/signin/dice_response_handler.cc
+++ b/chromium/chrome/browser/signin/dice_response_handler.cc
@@ -21,6 +21,7 @@
#include "chrome/browser/signin/account_reconcilor_factory.h"
#include "chrome/browser/signin/chrome_signin_client_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/signin_features.h"
#include "components/signin/core/browser/about_signin_internals.h"
#include "components/signin/core/browser/signin_header_helper.h"
#include "components/signin/public/base/consent_level.h"
@@ -84,6 +85,12 @@ enum DiceTokenFetchResult {
};
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
+bool IsBoundSessionCredentialsEnabledForDice() {
+ return switches::IsBoundSessionCredentialsEnabled() &&
+ base::FeatureList::IsEnabled(
+ kEnableBoundSessionCredentialsOnDiceProfiles);
+}
+
std::unique_ptr<RegistrationTokenHelper> BuildRegistrationTokenHelper(
unexportable_keys::UnexportableKeyService& unexportable_key_service,
base::StringPiece client_id,
@@ -91,7 +98,7 @@ std::unique_ptr<RegistrationTokenHelper> BuildRegistrationTokenHelper(
const GURL& registration_url,
base::OnceCallback<void(absl::optional<RegistrationTokenHelper::Result>)>
callback) {
- return std::make_unique<RegistrationTokenHelper>(
+ return RegistrationTokenHelper::CreateForTokenBinding(
unexportable_key_service, client_id, auth_code, registration_url,
std::move(callback));
}
@@ -103,7 +110,10 @@ CreateRegistrationTokenHelperFactory(
return {};
}
- CHECK(switches::IsBoundSessionCredentialsEnabled());
+ if (!IsBoundSessionCredentialsEnabledForDice()) {
+ return {};
+ }
+
// The factory holds a non-owning reference to `unexportable_key_service`.
return base::BindRepeating(&BuildRegistrationTokenHelper,
std::ref(*unexportable_key_service));
@@ -200,7 +210,7 @@ DiceResponseHandler::DiceTokenFetcher::DiceTokenFetcher(
std::make_unique<AccountReconcilor::Lock>(account_reconcilor);
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
if (!registration_token_helper_factory.is_null()) {
- CHECK(switches::IsBoundSessionCredentialsEnabled());
+ CHECK(IsBoundSessionCredentialsEnabledForDice());
StartBindingKeyGeneration(registration_token_helper_factory);
// Wait until the binding key is generated before fetching a token.
return;
@@ -226,8 +236,7 @@ void DiceResponseHandler::DiceTokenFetcher::OnClientOAuthSuccess(
gaia_auth_fetcher_.reset();
timeout_closure_.Cancel();
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
- if (!switches::IsBoundSessionCredentialsEnabled() ||
- !result.is_bound_to_key) {
+ if (!IsBoundSessionCredentialsEnabledForDice() || !result.is_bound_to_key) {
// Pass an empty binding key if conditions don't apply. This key won't be
// needed for anything else, so we can just clear it in place.
wrapped_binding_key_.clear();
@@ -272,7 +281,7 @@ void DiceResponseHandler::DiceTokenFetcher::StartTokenFetch() {
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
void DiceResponseHandler::DiceTokenFetcher::StartBindingKeyGeneration(
const RegistrationTokenHelperFactory& registration_token_helper_factory) {
- CHECK(switches::IsBoundSessionCredentialsEnabled());
+ CHECK(IsBoundSessionCredentialsEnabledForDice());
// `base::Unretained()` is safe because `this` owns
// `registration_token_helper_`.
registration_token_helper_ = registration_token_helper_factory.Run(
@@ -285,7 +294,7 @@ void DiceResponseHandler::DiceTokenFetcher::StartBindingKeyGeneration(
void DiceResponseHandler::DiceTokenFetcher::OnRegistrationTokenGenerated(
absl::optional<RegistrationTokenHelper::Result> result) {
- CHECK(switches::IsBoundSessionCredentialsEnabled());
+ CHECK(IsBoundSessionCredentialsEnabledForDice());
if (result.has_value()) {
binding_registration_token_ = std::move(result->registration_token);
wrapped_binding_key_ = std::move(result->wrapped_binding_key);
@@ -370,7 +379,7 @@ void DiceResponseHandler::SetTaskRunner(
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
void DiceResponseHandler::SetRegistrationTokenHelperFactoryForTesting(
RegistrationTokenHelperFactory factory) {
- CHECK(switches::IsBoundSessionCredentialsEnabled());
+ CHECK(IsBoundSessionCredentialsEnabledForDice());
registration_token_helper_factory_ = std::move(factory);
}
#endif // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
diff --git a/chromium/chrome/browser/signin/dice_response_handler_unittest.cc b/chromium/chrome/browser/signin/dice_response_handler_unittest.cc
index c6344fd9002..4facacb6dcd 100644
--- a/chromium/chrome/browser/signin/dice_response_handler_unittest.cc
+++ b/chromium/chrome/browser/signin/dice_response_handler_unittest.cc
@@ -15,6 +15,7 @@
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
+#include "chrome/browser/signin/signin_features.h"
#include "chrome/test/base/testing_profile.h"
#include "components/signin/core/browser/about_signin_internals.h"
#include "components/signin/core/browser/account_reconcilor.h"
@@ -104,11 +105,15 @@ class DiceTestSigninClient : public TestSigninClient, public GaiaAuthConsumer {
class FakeRegistrationTokenHelper : public RegistrationTokenHelper {
public:
FakeRegistrationTokenHelper()
- : RegistrationTokenHelper(fake_unexportable_key_service_,
- "test_client_id",
- "test_auth_code",
- GURL("https://accounts.google.com/Register"),
- base::DoNothing()) {}
+ : RegistrationTokenHelper(
+ fake_unexportable_key_service_,
+ base::BindRepeating(
+ [](crypto::SignatureVerifier::SignatureAlgorithm,
+ base::span<const uint8_t>,
+ base::Time) -> absl::optional<std::string> {
+ return absl::nullopt;
+ }),
+ base::DoNothing()) {}
~FakeRegistrationTokenHelper() override = default;
@@ -149,11 +154,16 @@ class DiceResponseHandlerTest : public testing::Test,
signin_client_(&pref_service_),
identity_test_env_(/*test_url_loader_factory=*/nullptr,
&pref_service_,
- signin::AccountConsistencyMethod::kDice,
&signin_client_),
signin_error_controller_(
SigninErrorController::AccountMode::PRIMARY_ACCOUNT,
identity_test_env_.identity_manager()) {
+#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
+ feature_list_.InitWithFeatures(
+ {switches::kEnableBoundSessionCredentials,
+ kEnableBoundSessionCredentialsOnDiceProfiles},
+ {});
+#endif
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
AboutSigninInternals::RegisterPrefs(pref_service_.registry());
auto account_reconcilor_delegate =
@@ -256,8 +266,7 @@ class DiceResponseHandlerTest : public testing::Test,
GoogleServiceAuthError auth_error_;
std::string auth_error_email_;
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
- base::test::ScopedFeatureList feature_list_{
- switches::kEnableBoundSessionCrendentials};
+ base::test::ScopedFeatureList feature_list_;
StrictMock<
base::MockCallback<DiceResponseHandler::RegistrationTokenHelperFactory>>
mock_registration_token_helper_factory_;
diff --git a/chromium/chrome/browser/signin/dice_signed_in_profile_creator.cc b/chromium/chrome/browser/signin/dice_signed_in_profile_creator.cc
index b8f6ad84ec3..c4586fc14d0 100644
--- a/chromium/chrome/browser/signin/dice_signed_in_profile_creator.cc
+++ b/chromium/chrome/browser/signin/dice_signed_in_profile_creator.cc
@@ -13,12 +13,19 @@
#include "base/scoped_observation.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/enterprise/profile_management/profile_management_features.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/signin_util.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/cookies/canonical_cookie.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
// Waits until the tokens are loaded and calls the callback. The callback is
// called immediately if the tokens are already loaded, and called with nullptr
@@ -162,11 +169,27 @@ void DiceSignedInProfileCreator::OnNewProfileInitialized(Profile* new_profile) {
return;
}
+ cookies_mover_ = std::make_unique<signin_util::CookiesMover>(
+ source_profile_->GetWeakPtr(), new_profile->GetWeakPtr(),
+ base::BindOnce(&DiceSignedInProfileCreator::LoadNewProfileTokens,
+ weak_pointer_factory_.GetWeakPtr(),
+ new_profile->GetWeakPtr()));
+ cookies_mover_->StartMovingCookies();
+}
+
+void DiceSignedInProfileCreator::LoadNewProfileTokens(
+ base::WeakPtr<Profile> new_profile) {
+ if (new_profile.WasInvalidated()) {
+ if (callback_) {
+ std::move(callback_).Run(nullptr);
+ }
+ return;
+ }
DCHECK(!tokens_loaded_callback_runner_);
// base::Unretained is fine because the runner is owned by this.
auto tokens_loaded_callback_runner =
TokensLoadedCallbackRunner::RunWhenLoaded(
- new_profile,
+ new_profile.get(),
base::BindOnce(&DiceSignedInProfileCreator::OnNewProfileTokensLoaded,
base::Unretained(this)));
// If the callback was called synchronously, |this| may have been deleted.
diff --git a/chromium/chrome/browser/signin/dice_signed_in_profile_creator.h b/chromium/chrome/browser/signin/dice_signed_in_profile_creator.h
index bc88473d885..10e889995c4 100644
--- a/chromium/chrome/browser/signin/dice_signed_in_profile_creator.h
+++ b/chromium/chrome/browser/signin/dice_signed_in_profile_creator.h
@@ -16,6 +16,10 @@
class TokensLoadedCallbackRunner;
+namespace signin_util {
+class CookiesMover;
+}
+
// Extracts an account from an existing profile and moves it to a new profile.
class DiceSignedInProfileCreator {
public:
@@ -51,6 +55,12 @@ class DiceSignedInProfileCreator {
// Called when the profile is initialized.
void OnNewProfileInitialized(Profile* profile);
+ // Called when cookies have been moved from `source_profile_` to
+ // `new_profile`.
+ void OnCookiesMoved(Profile* new_profile);
+
+ void LoadNewProfileTokens(base::WeakPtr<Profile> new_profile);
+
// Callback invoked once the token service is ready for the new profile.
void OnNewProfileTokensLoaded(Profile* new_profile);
@@ -59,6 +69,7 @@ class DiceSignedInProfileCreator {
base::OnceCallback<void(Profile*)> callback_;
std::unique_ptr<TokensLoadedCallbackRunner> tokens_loaded_callback_runner_;
+ std::unique_ptr<signin_util::CookiesMover> cookies_mover_;
base::WeakPtrFactory<DiceSignedInProfileCreator> weak_pointer_factory_{this};
};
diff --git a/chromium/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc b/chromium/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc
index c420c30caa9..98e3e8c4eed 100644
--- a/chromium/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc
+++ b/chromium/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc
@@ -4,14 +4,18 @@
#include "chrome/browser/signin/dice_signed_in_profile_creator.h"
+#include "base/barrier_closure.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind.h"
#include "base/test/test_file_util.h"
+#include "chrome/browser/enterprise/profile_management/profile_management_features.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
@@ -23,14 +27,57 @@
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
+#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_task_environment.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char16_t kProfileTestName[] = u"profile_test_name";
+struct CookieTestParam {
+ bool enable_third_party_management_feature;
+ bool setup_cookies_to_move;
+};
+
+const CookieTestParam kTestCases[] = {{true, true},
+ {true, false},
+ {false, false},
+ {false, true}};
+
+void CreateCookies(
+ Profile* profile,
+ const std::map<std::string, std::string> cookie_url_and_name) {
+ network::mojom::CookieManager* cookie_manager =
+ profile->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess();
+
+ base::RunLoop run_loop;
+ base::RepeatingClosure barrier = base::BarrierClosure(
+ cookie_url_and_name.size(),
+ base::BindLambdaForTesting([&]() { run_loop.Quit(); }));
+
+ for (const auto& url_name : cookie_url_and_name) {
+ GURL url(url_name.first);
+ std::unique_ptr<net::CanonicalCookie> cookie =
+ net::CanonicalCookie::CreateSanitizedCookie(
+ url, url_name.second, "A=" + url_name.second, url.host(),
+ url.path(), base::Time::Now(), base::Time::Max(), base::Time::Now(),
+ url.SchemeIsCryptographic(), false,
+ net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT,
+ false, absl::nullopt);
+ cookie_manager->SetCanonicalCookie(
+ *cookie, url, net::CookieOptions::MakeAllInclusive(),
+ base::BindLambdaForTesting(
+ [&](net::CookieAccessResult access_result) { barrier.Run(); }));
+ }
+
+ run_loop.Run();
+}
+
std::unique_ptr<TestingProfile> BuildTestingProfile(const base::FilePath& path,
Profile::Delegate* delegate,
bool tokens_loaded) {
@@ -69,11 +116,18 @@ class UnittestProfileManager : public FakeProfileManager {
} // namespace
-class DiceSignedInProfileCreatorTest : public testing::Test,
- public ProfileManagerObserver {
+class DiceSignedInProfileCreatorTest
+ : public testing::Test,
+ public testing::WithParamInterface<CookieTestParam>,
+ public ProfileManagerObserver {
public:
DiceSignedInProfileCreatorTest()
: local_state_(TestingBrowserProcess::GetGlobal()) {
+#if !BUILDFLAG(IS_FUCHSIA)
+ scoped_feature_list_.InitWithFeatureState(
+ profile_management::features::kThirdPartyProfileManagement,
+ GetParam().enable_third_party_management_feature);
+#endif
auto profile_manager_unique = std::make_unique<UnittestProfileManager>(
base::CreateUniqueTempDirectoryScopedToTest());
profile_manager_ = profile_manager_unique.get();
@@ -142,6 +196,75 @@ class DiceSignedInProfileCreatorTest : public testing::Test,
std::move(profile_added_closure_).Run();
}
+ void SetupCookiesToMove() {
+ if (!GetParam().setup_cookies_to_move) {
+ return;
+ }
+ // Add some cookies
+ CreateCookies(profile_.get(), {{"https://google.com", "oldgoogle0"},
+ {"https://example.com", "oldexample0"}});
+ CreateCookies(profile_.get(), {{"https://google.com", "validgoogle1"},
+ {"https://example.com", "validexample1"}});
+ CreateCookies(profile_.get(), {{"https://google.com", "newgoogle2"},
+ {"https://example.com", "newexample2"}});
+
+ profile_->GetPrefs()->SetString(prefs::kSigninInterceptionIDPCookiesUrl,
+ "https://www.google.com/");
+ }
+
+ void VerifyCookiesMoved() {
+ if (!GetParam().setup_cookies_to_move) {
+ return;
+ }
+ GURL url("https://www.google.com/");
+ net::CookieList cookies_source_profile;
+ net::CookieList cookies_new_profile;
+ {
+ network::mojom::CookieManager* cookie_manager =
+ profile_->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess();
+ base::RunLoop loop;
+ cookie_manager->GetAllCookies(
+ base::BindLambdaForTesting([&](const net::CookieList& cookies) {
+ cookies_source_profile = cookies;
+ loop.Quit();
+ }));
+ loop.Run();
+ }
+ {
+ network::mojom::CookieManager* cookie_manager =
+ added_profile_->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess();
+ base::RunLoop loop;
+ cookie_manager->GetAllCookies(
+ base::BindLambdaForTesting([&](const net::CookieList& cookies) {
+ cookies_new_profile = cookies;
+ loop.Quit();
+ }));
+ loop.Run();
+ }
+
+ if (!GetParam().enable_third_party_management_feature) {
+ EXPECT_EQ(6u, cookies_source_profile.size());
+ EXPECT_TRUE(cookies_new_profile.empty());
+ return;
+ }
+ // Third party management feature not enabled on fuchsia.
+#if !BUILDFLAG(IS_FUCHSIA)
+ return;
+#endif
+
+ EXPECT_EQ(3u, cookies_source_profile.size());
+ EXPECT_EQ(3u, cookies_new_profile.size());
+
+ for (const auto& cookie : cookies_new_profile) {
+ EXPECT_TRUE(cookie.IsDomainMatch(url.host()));
+ EXPECT_TRUE(cookie.Name() == "oldgoogle0" ||
+ cookie.Name() == "validgoogle1" ||
+ cookie.Name() == "newgoogle1");
+ }
+ }
+
private:
content::BrowserTaskEnvironment task_environment_;
ScopedTestingLocalState local_state_;
@@ -157,11 +280,12 @@ class DiceSignedInProfileCreatorTest : public testing::Test,
bool use_guest_profile_ = false;
};
-TEST_F(DiceSignedInProfileCreatorTest, CreateWithTokensLoaded) {
+TEST_P(DiceSignedInProfileCreatorTest, CreateWithTokensLoaded) {
AccountInfo account_info =
identity_test_env()->MakeAccountAvailable("bob@example.com");
size_t kTestIcon = profiles::GetModernAvatarIconStartIndex();
+ SetupCookiesToMove();
base::RunLoop loop;
std::unique_ptr<DiceSignedInProfileCreator> creator =
std::make_unique<DiceSignedInProfileCreator>(
@@ -197,9 +321,10 @@ TEST_F(DiceSignedInProfileCreatorTest, CreateWithTokensLoaded) {
EXPECT_EQ(kProfileTestName, entry->GetLocalProfileName());
EXPECT_EQ(kTestIcon, entry->GetAvatarIconIndex());
}
+ VerifyCookiesMoved();
}
-TEST_F(DiceSignedInProfileCreatorTest, CreateWithTokensNotLoaded) {
+TEST_P(DiceSignedInProfileCreatorTest, CreateWithTokensNotLoaded) {
AccountInfo account_info =
identity_test_env()->MakeAccountAvailable("bob@example.com");
profile_manager()->set_tokens_loaded_at_creation(false);
@@ -207,6 +332,7 @@ TEST_F(DiceSignedInProfileCreatorTest, CreateWithTokensNotLoaded) {
base::RunLoop creator_loop;
base::RunLoop profile_added_loop;
set_profile_added_closure(profile_added_loop.QuitClosure());
+ SetupCookiesToMove();
std::unique_ptr<DiceSignedInProfileCreator> creator =
std::make_unique<DiceSignedInProfileCreator>(
profile(), account_info.account_id, std::u16string(), absl::nullopt,
@@ -237,12 +363,14 @@ TEST_F(DiceSignedInProfileCreatorTest, CreateWithTokensNotLoaded) {
.size());
EXPECT_TRUE(IdentityManagerFactory::GetForProfile(signed_in_profile())
->HasAccountWithRefreshToken(account_info.account_id));
+ VerifyCookiesMoved();
}
// Deleting the creator while it is running does not crash.
-TEST_F(DiceSignedInProfileCreatorTest, DeleteWhileCreating) {
+TEST_P(DiceSignedInProfileCreatorTest, DeleteWhileCreating) {
AccountInfo account_info =
identity_test_env()->MakeAccountAvailable("bob@example.com");
+ SetupCookiesToMove();
std::unique_ptr<DiceSignedInProfileCreator> creator =
std::make_unique<DiceSignedInProfileCreator>(
profile(), account_info.account_id, std::u16string(), absl::nullopt,
@@ -255,7 +383,7 @@ TEST_F(DiceSignedInProfileCreatorTest, DeleteWhileCreating) {
}
// Deleting the profile while waiting for the tokens.
-TEST_F(DiceSignedInProfileCreatorTest, DeleteProfile) {
+TEST_P(DiceSignedInProfileCreatorTest, DeleteProfile) {
AccountInfo account_info =
identity_test_env()->MakeAccountAvailable("bob@example.com");
profile_manager()->set_tokens_loaded_at_creation(false);
@@ -263,6 +391,7 @@ TEST_F(DiceSignedInProfileCreatorTest, DeleteProfile) {
base::RunLoop creator_loop;
base::RunLoop profile_added_loop;
set_profile_added_closure(profile_added_loop.QuitClosure());
+ SetupCookiesToMove();
std::unique_ptr<DiceSignedInProfileCreator> creator =
std::make_unique<DiceSignedInProfileCreator>(
profile(), account_info.account_id, std::u16string(), absl::nullopt,
@@ -277,6 +406,7 @@ TEST_F(DiceSignedInProfileCreatorTest, DeleteProfile) {
EXPECT_FALSE(creator_callback_called());
EXPECT_TRUE(added_profile());
EXPECT_NE(profile(), added_profile());
+ VerifyCookiesMoved();
DeleteProfiles();
creator_loop.Run();
@@ -285,3 +415,7 @@ TEST_F(DiceSignedInProfileCreatorTest, DeleteProfile) {
EXPECT_TRUE(creator_callback_called());
EXPECT_FALSE(signed_in_profile());
}
+
+INSTANTIATE_TEST_SUITE_P(All,
+ DiceSignedInProfileCreatorTest,
+ ::testing::ValuesIn(kTestCases));
diff --git a/chromium/chrome/browser/signin/dice_tab_helper.cc b/chromium/chrome/browser/signin/dice_tab_helper.cc
index 1754e8e2f27..59a3e3a1f81 100644
--- a/chromium/chrome/browser/signin/dice_tab_helper.cc
+++ b/chromium/chrome/browser/signin/dice_tab_helper.cc
@@ -5,21 +5,68 @@
#include "chrome/browser/signin/dice_tab_helper.h"
#include "base/check_op.h"
+#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
-#include "chrome/browser/signin/dice_tab_helper.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
+#include "chrome/browser/ui/webui/signin/turn_sync_on_helper.h"
+#include "components/signin/public/base/signin_metrics.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "google_apis/gaia/gaia_auth_util.h"
+// static
+DiceTabHelper::EnableSyncCallback
+DiceTabHelper::GetEnableSyncCallbackForBrowser() {
+ return base::BindRepeating(
+ [](Profile* profile, signin_metrics::AccessPoint access_point,
+ signin_metrics::PromoAction promo_action,
+ signin_metrics::Reason reason, content::WebContents* web_contents,
+ const CoreAccountId& account_id) {
+ DCHECK(profile);
+ Browser* browser =
+ web_contents ? chrome::FindBrowserWithWebContents(web_contents)
+ : chrome::FindBrowserWithProfile(profile);
+ if (!browser) {
+ return;
+ }
+ // TurnSyncOnHelper is suicidal (it will kill itself once it
+ // finishes enabling sync).
+ new TurnSyncOnHelper(
+ profile, browser, access_point, promo_action, reason, account_id,
+ TurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ });
+}
+
+// static
+DiceTabHelper::ShowSigninErrorCallback
+DiceTabHelper::GetShowSigninErrorCallbackForBrowser() {
+ return base::BindRepeating([](Profile* profile,
+ content::WebContents* web_contents,
+ const SigninUIError& error) {
+ if (!profile) {
+ return;
+ }
+ Browser* browser = web_contents
+ ? chrome::FindBrowserWithWebContents(web_contents)
+ : chrome::FindBrowserWithProfile(profile);
+ if (!browser) {
+ return;
+ }
+ LoginUIServiceFactory::GetForProfile(profile)->DisplayLoginResult(
+ browser, error, /*from_profile_picker=*/false);
+ });
+}
+
DiceTabHelper::ResetableState::ResetableState() = default;
-DiceTabHelper::ResetableState::ResetableState(const ResetableState& other) =
- default;
+DiceTabHelper::ResetableState::~ResetableState() = default;
+DiceTabHelper::ResetableState::ResetableState(ResetableState&& other) = default;
DiceTabHelper::ResetableState& DiceTabHelper::ResetableState::operator=(
- const ResetableState& other) = default;
+ ResetableState&& other) = default;
DiceTabHelper::DiceTabHelper(content::WebContents* web_contents)
: content::WebContentsUserData<DiceTabHelper>(*web_contents),
@@ -32,7 +79,10 @@ void DiceTabHelper::InitializeSigninFlow(
signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
signin_metrics::PromoAction promo_action,
- const GURL& redirect_url) {
+ const GURL& redirect_url,
+ bool record_signin_started_metrics,
+ EnableSyncCallback enable_sync_callback,
+ ShowSigninErrorCallback show_signin_error_callback) {
DCHECK(signin_url.is_valid());
DCHECK(state_.signin_url.is_empty() || state_.signin_url == signin_url);
@@ -42,23 +92,30 @@ void DiceTabHelper::InitializeSigninFlow(
state_.signin_access_point = access_point;
state_.signin_promo_action = promo_action;
state_.signin_reason = reason;
+ state_.enable_sync_callback = std::move(enable_sync_callback);
+ state_.show_signin_error_callback = std::move(show_signin_error_callback);
is_chrome_signin_page_ = true;
signin_page_load_recorded_ = false;
+ if (reason == signin_metrics::Reason::kSigninPrimaryAccount) {
+ state_.sync_signin_flow_status = SyncSigninFlowStatus::kStarted;
+ }
+
+ if (!record_signin_started_metrics) {
+ return;
+ }
+
// Note: if a Dice signin tab is reused, `InitializeSigninFlow()` is not
// called again, and the tab reuse does not generate new metrics.
if (reason == signin_metrics::Reason::kSigninPrimaryAccount ||
reason == signin_metrics::Reason::kAddSecondaryAccount) {
// See details at go/chrome-signin-metrics-revamp.
- base::UmaHistogramEnumeration(
- "Signin.SignIn.Started", access_point,
- signin_metrics::AccessPoint::ACCESS_POINT_MAX);
+ signin_metrics::LogSignInStarted(access_point);
}
if (reason == signin_metrics::Reason::kSigninPrimaryAccount) {
- state_.sync_signin_flow_status = SyncSigninFlowStatus::kStarted;
signin_metrics::LogSigninAccessPointStarted(access_point, promo_action);
signin_metrics::RecordSigninUserActionForAccessPoint(access_point);
base::RecordAction(base::UserMetricsAction("Signin_SigninPage_Loading"));
@@ -80,8 +137,9 @@ void DiceTabHelper::OnSyncSigninFlowComplete() {
void DiceTabHelper::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
- if (!is_chrome_signin_page_)
+ if (!is_chrome_signin_page_) {
return;
+ }
// Ignore internal navigations.
if (!navigation_handle->IsInPrimaryMainFrame() ||
@@ -100,8 +158,9 @@ void DiceTabHelper::DidStartNavigation(
void DiceTabHelper::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
- if (!is_chrome_signin_page_)
+ if (!is_chrome_signin_page_) {
return;
+ }
// Ignore internal navigations.
if (!navigation_handle->IsInPrimaryMainFrame() ||
diff --git a/chromium/chrome/browser/signin/dice_tab_helper.h b/chromium/chrome/browser/signin/dice_tab_helper.h
index 3cd62ce10eb..422923b61a1 100644
--- a/chromium/chrome/browser/signin/dice_tab_helper.h
+++ b/chromium/chrome/browser/signin/dice_tab_helper.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_SIGNIN_DICE_TAB_HELPER_H_
#define CHROME_BROWSER_SIGNIN_DICE_TAB_HELPER_H_
+#include "base/functional/callback_forward.h"
#include "components/signin/public/base/signin_metrics.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
@@ -13,10 +14,37 @@ namespace content {
class NavigationHandle;
}
+struct CoreAccountId;
+class SigninUIError;
+
// Tab helper used for DICE to tag signin tabs. Signin tabs can be reused.
class DiceTabHelper : public content::WebContentsUserData<DiceTabHelper>,
public content::WebContentsObserver {
public:
+ // Callback starting Sync. This is a repeating callback, because multiple
+ // `ProcessDiceHeaderDelegateImpl` may make copies of it.
+ using EnableSyncCallback =
+ base::RepeatingCallback<void(Profile*,
+ signin_metrics::AccessPoint,
+ signin_metrics::PromoAction,
+ signin_metrics::Reason,
+ content::WebContents*,
+ const CoreAccountId&)>;
+
+ // Callback displaying a signin error to the user. This is a repeating
+ // callback, because multiple `ProcessDiceHeaderDelegateImpl` may make copies
+ // of it.
+ using ShowSigninErrorCallback = base::RepeatingCallback<
+ void(Profile*, content::WebContents*, const SigninUIError&)>;
+
+ // Returns the default callback to enable sync in a browser window. Does
+ // nothing if there is no browser associated with the web contents.
+ static EnableSyncCallback GetEnableSyncCallbackForBrowser();
+
+ // Returns the default callback to show a signin error in a browser window.
+ // Does nothing if there is no browser associated with the web contents.
+ static ShowSigninErrorCallback GetShowSigninErrorCallbackForBrowser();
+
DiceTabHelper(const DiceTabHelper&) = delete;
DiceTabHelper& operator=(const DiceTabHelper&) = delete;
@@ -36,13 +64,27 @@ class DiceTabHelper : public content::WebContentsUserData<DiceTabHelper>,
const GURL& signin_url() const { return state_.signin_url; }
+ const EnableSyncCallback& GetEnableSyncCallback() {
+ return state_.enable_sync_callback;
+ }
+
+ const ShowSigninErrorCallback& GetShowSigninErrorCallback() {
+ return state_.show_signin_error_callback;
+ }
+
// Initializes the DiceTabHelper for a new signin flow. Must be called once
// per signin flow happening in the tab, when the signin URL is being loaded.
+ // The `redirect_url` is used after enabling Sync or in case of errors ; it is
+ // not used after a successful signin without sync, and in this case the
+ // page will navigate to the `continue_url` parameter from `signin_url`.
void InitializeSigninFlow(const GURL& signin_url,
signin_metrics::AccessPoint access_point,
signin_metrics::Reason reason,
signin_metrics::PromoAction promo_action,
- const GURL& redirect_url);
+ const GURL& redirect_url,
+ bool record_signin_started_metrics,
+ EnableSyncCallback enable_sync_callback,
+ ShowSigninErrorCallback show_signin_error_callback);
// Returns true if this the tab is a re-usable chrome sign-in page (the signin
// page is loading or loaded in the tab).
@@ -70,11 +112,17 @@ class DiceTabHelper : public content::WebContentsUserData<DiceTabHelper>,
struct ResetableState {
ResetableState();
- ResetableState(const ResetableState& other);
- ResetableState& operator=(const ResetableState& other);
+ ~ResetableState();
+ ResetableState(const ResetableState& other) = delete;
+ ResetableState& operator=(const ResetableState& other) = delete;
+
+ ResetableState(ResetableState&& other);
+ ResetableState& operator=(ResetableState&& other);
GURL redirect_url;
GURL signin_url;
+ EnableSyncCallback enable_sync_callback;
+ ShowSigninErrorCallback show_signin_error_callback;
// By default the access point refers to web signin, as after a reset the
// user may sign in again in the same tab.
diff --git a/chromium/chrome/browser/signin/dice_tab_helper_unittest.cc b/chromium/chrome/browser/signin/dice_tab_helper_unittest.cc
index 2c6edc7330a..66d11b10a1a 100644
--- a/chromium/chrome/browser/signin/dice_tab_helper_unittest.cc
+++ b/chromium/chrome/browser/signin/dice_tab_helper_unittest.cc
@@ -41,7 +41,9 @@ class DiceTabHelperTest : public ChromeRenderViewHostTestHarness {
helper->InitializeSigninFlow(
signin_url_, access_point, reason,
signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO,
- GURL::EmptyGURL());
+ GURL::EmptyGURL(), /*record_signin_started_metrics=*/true,
+ DiceTabHelper::EnableSyncCallback(),
+ DiceTabHelper::ShowSigninErrorCallback());
EXPECT_TRUE(helper->IsChromeSigninPage());
simulator->Commit();
}
@@ -152,7 +154,9 @@ TEST_F(DiceTabHelperTest, SigninPrimaryAccountMetrics) {
signin_url_, signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS,
signin_metrics::Reason::kSigninPrimaryAccount,
signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
- GURL::EmptyGURL());
+ GURL::EmptyGURL(), /*record_signin_started_metrics=*/true,
+ DiceTabHelper::EnableSyncCallback(),
+ DiceTabHelper::ShowSigninErrorCallback());
EXPECT_EQ(1, ua_tester.GetActionCount("Signin_Signin_FromSettings"));
EXPECT_EQ(1, ua_tester.GetActionCount("Signin_SigninPage_Loading"));
EXPECT_EQ(0, ua_tester.GetActionCount("Signin_SigninPage_Shown"));
@@ -184,8 +188,34 @@ TEST_F(DiceTabHelperTest, SigninPrimaryAccountMetrics) {
dice_tab_helper->InitializeSigninFlow(
signin_url_, signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS,
signin_metrics::Reason::kSigninPrimaryAccount,
- signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT,
- GURL::EmptyGURL());
+ signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT, GURL::EmptyGURL(),
+ /*record_signin_started_metrics=*/true,
+ DiceTabHelper::EnableSyncCallback(),
+ DiceTabHelper::ShowSigninErrorCallback());
+ EXPECT_EQ(2, ua_tester.GetActionCount("Signin_Signin_FromSettings"));
+ EXPECT_EQ(2, ua_tester.GetActionCount("Signin_SigninPage_Loading"));
+ h_tester.ExpectUniqueSample(
+ "Signin.SigninStartedAccessPoint",
+ signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS, 2);
+ h_tester.ExpectUniqueSample(
+ "Signin.SigninStartedAccessPoint.WithDefault",
+ signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS, 1);
+ h_tester.ExpectUniqueSample(
+ "Signin.SignIn.Started",
+ signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS, 2);
+
+ // Check metrics are NOT logged again when `record_signin_started_metrics`is
+ // false.
+ simulator = content::NavigationSimulator::CreateRendererInitiated(signin_url_,
+ main_rfh());
+ simulator->Start();
+ dice_tab_helper->InitializeSigninFlow(
+ signin_url_, signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS,
+ signin_metrics::Reason::kSigninPrimaryAccount,
+ signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT, GURL::EmptyGURL(),
+ /*record_signin_started_metrics=*/false,
+ DiceTabHelper::EnableSyncCallback(),
+ DiceTabHelper::ShowSigninErrorCallback());
EXPECT_EQ(2, ua_tester.GetActionCount("Signin_Signin_FromSettings"));
EXPECT_EQ(2, ua_tester.GetActionCount("Signin_SigninPage_Loading"));
h_tester.ExpectUniqueSample(
@@ -216,7 +246,9 @@ TEST_F(DiceTabHelperTest, AddSecondaryAccountMetrics) {
signin_url_, signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS,
signin_metrics::Reason::kAddSecondaryAccount,
signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
- GURL::EmptyGURL());
+ GURL::EmptyGURL(), /*record_signin_started_metrics=*/true,
+ DiceTabHelper::EnableSyncCallback(),
+ DiceTabHelper::ShowSigninErrorCallback());
h_tester.ExpectUniqueSample(
"Signin.SignIn.Started",
signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS, 1);
diff --git a/chromium/chrome/browser/signin/dice_web_signin_interceptor.cc b/chromium/chrome/browser/signin/dice_web_signin_interceptor.cc
index 09bb1e79567..e6bc1c83561 100644
--- a/chromium/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chromium/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -42,11 +42,12 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/themes/autogenerated_theme_util.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/common/password_manager_ui.h"
+#include "components/policy/core/browser/signin/profile_separation_policies.h"
#include "components/policy/core/browser/signin/user_cloud_signin_restriction_policy_fetcher.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
@@ -56,6 +57,7 @@
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/signin/public/base/signin_metrics.h"
+#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/identity_manager/account_managed_status_finder.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
@@ -63,17 +65,14 @@
#include "google_apis/gaia/gaia_auth_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/mojom/themes.mojom.h"
+#include "ui/base/ui_base_features.h"
namespace {
constexpr char kProfileCreationInterceptionDeclinedPref[] =
"signin.ProfileCreationInterceptionDeclinedPref";
-void RecordSigninInterceptionHeuristicOutcome(
- SigninInterceptionHeuristicOutcome outcome) {
- base::UmaHistogramEnumeration("Signin.Intercept.HeuristicOutcome", outcome);
-}
-
// Helper function to return the primary account info. The returned info is
// empty if there is no primary account, and non-empty otherwise. Extended
// fields may be missing if they are not available.
@@ -119,8 +118,14 @@ void DiceWebSigninInterceptor::RegisterProfilePrefs(
registry->RegisterBooleanPref(prefs::kSigninInterceptionEnabled, true);
registry->RegisterStringPref(prefs::kManagedAccountsSigninRestriction,
std::string());
+ registry->RegisterStringPref(prefs::kSigninInterceptionIDPCookiesUrl,
+ std::string());
registry->RegisterBooleanPref(
prefs::kManagedAccountsSigninRestrictionScopeMachine, false);
+ registry->RegisterIntegerPref(prefs::kProfileSeparationSettings, 0);
+ registry->RegisterIntegerPref(prefs::kProfileSeparationDataMigrationSettings,
+ 1);
+ registry->RegisterListPref(prefs::kProfileSeparationDomainExceptionList);
}
absl::optional<SigninInterceptionHeuristicOutcome>
@@ -215,17 +220,22 @@ void DiceWebSigninInterceptor::MaybeInterceptWebSignin(
return;
}
+ DCHECK_EQ(interception_start_time_, base::TimeTicks());
+ interception_start_time_ = base::TimeTicks::Now();
+
if (!web_contents) {
// The tab has been closed (typically during the token exchange, which may
// take some time).
RecordSigninInterceptionHeuristicOutcome(
SigninInterceptionHeuristicOutcome::kAbortTabClosed);
+ Reset();
return;
}
if (!delegate_->IsSigninInterceptionSupported(*web_contents)) {
RecordSigninInterceptionHeuristicOutcome(
SigninInterceptionHeuristicOutcome::kAbortNoSupportedBrowser);
+ Reset();
return;
}
@@ -238,6 +248,7 @@ void DiceWebSigninInterceptor::MaybeInterceptWebSignin(
->IsFormManagerPendingPasswordUpdate()) {
RecordSigninInterceptionHeuristicOutcome(
SigninInterceptionHeuristicOutcome::kAbortPasswordUpdatePending);
+ Reset();
return;
}
@@ -248,6 +259,7 @@ void DiceWebSigninInterceptor::MaybeInterceptWebSignin(
password_manager::ui::State::PENDING_PASSWORD_UPDATE_STATE) {
RecordSigninInterceptionHeuristicOutcome(
SigninInterceptionHeuristicOutcome::kAbortPasswordUpdate);
+ Reset();
return;
}
@@ -320,11 +332,12 @@ void DiceWebSigninInterceptor::Reset() {
intercepted_account_management_accepted_ = false;
interception_type_ = absl::nullopt;
dice_signed_in_profile_creator_.reset();
+ interception_start_time_ = base::TimeTicks();
was_interception_ui_displayed_ = false;
interception_bubble_handle_.reset();
on_intercepted_account_level_policy_value_timeout_.Cancel();
account_level_signin_restriction_policy_fetcher_.reset();
- intercepted_account_level_policy_value_.reset();
+ intercepted_account_profile_separation_policies_.reset();
}
const ProfileAttributesEntry*
@@ -348,15 +361,6 @@ DiceWebSigninInterceptor::ShouldShowProfileSwitchBubble(
bool DiceWebSigninInterceptor::ShouldEnforceEnterpriseProfileSeparation(
const AccountInfo& intercepted_account_info) const {
DCHECK(intercepted_account_info.IsValid());
-
- if (!signin_util::ProfileSeparationEnforcedByPolicy(
- profile_,
- intercepted_account_level_policy_value_.value_or(std::string()))) {
- return false;
- }
- if (new_account_interception_)
- return intercepted_account_info.IsManaged();
-
CoreAccountInfo primary_core_account_info =
identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
// In case of re-auth, do not show the enterprise separation dialog if the
@@ -366,7 +370,14 @@ bool DiceWebSigninInterceptor::ShouldEnforceEnterpriseProfileSeparation(
return !chrome::enterprise_util::UserAcceptedAccountManagement(profile_);
}
- return false;
+ if (!signin_util::IsProfileSeparationEnforcedByProfile(profile_) &&
+ !signin_util::IsProfileSeparationEnforcedByPolicies(
+ intercepted_account_profile_separation_policies_.value_or(
+ policy::ProfileSeparationPolicies()))) {
+ return false;
+ }
+
+ return new_account_interception_ && intercepted_account_info.IsManaged();
}
bool DiceWebSigninInterceptor::ShouldShowEnterpriseDialog(
@@ -505,8 +516,9 @@ void DiceWebSigninInterceptor::OnInterceptionReadyToBeProcessed(
primary_account_id == info.account_id) &&
signin_util::
ProfileSeparationAllowsKeepingUnmanagedBrowsingDataInManagedProfile(
- profile_, intercepted_account_level_policy_value_.value_or(
- std::string()));
+ profile_,
+ intercepted_account_profile_separation_policies_.value_or(
+ policy::ProfileSeparationPolicies()));
RecordSigninInterceptionHeuristicOutcome(
SigninInterceptionHeuristicOutcome::kInterceptEnterpriseForced);
}
@@ -553,8 +565,6 @@ void DiceWebSigninInterceptor::OnInterceptionReadyToBeProcessed(
bool show_managed_disclaimer =
*interception_type !=
WebSigninInterceptor::SigninInterceptionType::kProfileSwitch &&
- (base::FeatureList::IsEnabled(kSigninInterceptBubbleV2) ||
- base::FeatureList::IsEnabled(kSyncPromoAfterSigninIntercept)) &&
(info.IsManaged() ||
policy::ManagementServiceFactory::GetForPlatform()->IsManaged());
@@ -603,7 +613,7 @@ void DiceWebSigninInterceptor::OnExtendedAccountInfoUpdated(
// account with a timeout.
if (!EnterpriseSeparationMaybeRequired(
info.email, new_account_interception_,
- intercepted_account_level_policy_value_)
+ intercepted_account_profile_separation_policies_)
.has_value()) {
FetchAccountLevelSigninRestrictionForInterceptedAccount(
info, base::BindOnce(
@@ -691,8 +701,14 @@ void DiceWebSigninInterceptor::OnNewSignedInProfileCreated(
// option is removed.
if (!new_profile->IsGuestSession()) {
// Apply the new color to the profile.
- ThemeServiceFactory::GetForProfile(new_profile)
- ->BuildAutogeneratedThemeFromColor(*profile_color);
+ if (features::IsChromeWebuiRefresh2023()) {
+ ThemeServiceFactory::GetForProfile(new_profile)
+ ->SetUserColorAndBrowserColorVariant(
+ *profile_color, ui::mojom::BrowserColorVariant::kTonalSpot);
+ } else {
+ ThemeServiceFactory::GetForProfile(new_profile)
+ ->BuildAutogeneratedThemeFromColor(*profile_color);
+ }
}
// TODO(crbug/1450011): Move this to DiceSignedInProfileCreator when
@@ -727,9 +743,11 @@ void DiceWebSigninInterceptor::OnEnterpriseProfileCreationResult(
SkColor profile_color,
SigninInterceptionResult create) {
signin_util::RecordEnterpriseProfileCreationUserChoice(
- /*enforced_by_policy=*/signin_util::ProfileSeparationEnforcedByPolicy(
- profile_,
- intercepted_account_level_policy_value_.value_or(std::string())),
+ /*enforced_by_policy=*/!signin_util::IsProfileSeparationEnforcedByProfile(
+ profile_) &&
+ !signin_util::IsProfileSeparationEnforcedByPolicies(
+ intercepted_account_profile_separation_policies_.value_or(
+ policy::ProfileSeparationPolicies())),
/*created=*/create == SigninInterceptionResult::kAccepted);
// Make sure existing account is a non-signed in profile.
@@ -816,12 +834,12 @@ bool DiceWebSigninInterceptor::HasUserDeclinedProfileCreation(
void DiceWebSigninInterceptor::
FetchAccountLevelSigninRestrictionForInterceptedAccount(
const AccountInfo& account_info,
- base::OnceCallback<void(const std::string&)> callback) {
- if (intercepted_account_level_policy_value_fetch_result_for_testing_
+ base::OnceCallback<void(const policy::ProfileSeparationPolicies&)>
+ callback) {
+ if (intercepted_account_profile_separation_policies_for_testing_
.has_value()) {
std::move(callback).Run(
- intercepted_account_level_policy_value_fetch_result_for_testing_
- .value());
+ intercepted_account_profile_separation_policies_for_testing_.value());
return;
}
@@ -834,10 +852,11 @@ void DiceWebSigninInterceptor::
->GetManagedAccountsSigninRestriction(
identity_manager_, account_info.account_id, std::move(callback));
- on_intercepted_account_level_policy_value_timeout_.Reset(base::BindOnce(
- &DiceWebSigninInterceptor::
- OnAccountLevelManagedAccountsSigninRestrictionReceived,
- base::Unretained(this), /*timed_out=*/true, account_info, std::string()));
+ on_intercepted_account_level_policy_value_timeout_.Reset(
+ base::BindOnce(&DiceWebSigninInterceptor::
+ OnAccountLevelManagedAccountsSigninRestrictionReceived,
+ base::Unretained(this), /*timed_out=*/true, account_info,
+ policy::ProfileSeparationPolicies()));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, on_intercepted_account_level_policy_value_timeout_.callback(),
base::Seconds(5));
@@ -847,15 +866,18 @@ void DiceWebSigninInterceptor::
OnAccountLevelManagedAccountsSigninRestrictionReceived(
bool timed_out,
const AccountInfo& account_info,
- const std::string& signin_restriction) {
+ const policy::ProfileSeparationPolicies& profile_separation_policies) {
if (timed_out) {
- DCHECK(signin_restriction.empty())
+ DCHECK(profile_separation_policies.Empty())
<< "There should be no signin restriction at the account level in case "
"of a timeout";
+ intercepted_account_profile_separation_policies_ =
+ policy::ProfileSeparationPolicies();
} else {
on_intercepted_account_level_policy_value_timeout_.Cancel();
+ intercepted_account_profile_separation_policies_ =
+ profile_separation_policies;
}
- intercepted_account_level_policy_value_ = signin_restriction;
OnInterceptionReadyToBeProcessed(account_info);
}
@@ -863,8 +885,8 @@ absl::optional<bool>
DiceWebSigninInterceptor::EnterpriseSeparationMaybeRequired(
const std::string& email,
bool is_new_account_interception,
- absl::optional<std::string>
- managed_account_profile_level_signin_restriction) const {
+ const absl::optional<policy::ProfileSeparationPolicies>&
+ intercepted_profile_separation_policies) const {
// No enterprise separation required for consumer accounts.
if (signin::AccountManagedStatusFinder::IsEnterpriseUserBasedOnEmail(email) ==
signin::AccountManagedStatusFinder::EmailEnterpriseStatus::
@@ -882,19 +904,46 @@ DiceWebSigninInterceptor::EnterpriseSeparationMaybeRequired(
if (!intercepted_account_info.IsManaged())
return false;
// If `profile` requires enterprise profile separation, return true.
- if (signin_util::ProfileSeparationEnforcedByPolicy(
- profile_, managed_account_profile_level_signin_restriction.value_or(
- std::string()))) {
+ if (signin_util::IsProfileSeparationEnforcedByProfile(profile_)) {
return true;
}
+
+ if (signin_util::IsProfileSeparationEnforcedByPolicies(
+ intercepted_profile_separation_policies.value_or(
+ policy::ProfileSeparationPolicies()))) {
+ return true;
+ }
+
// If we still do not know if profile separation is required, the account
// level policies for the intercepted account must be fetched if possible.
if (is_new_account_interception &&
- !managed_account_profile_level_signin_restriction.has_value() &&
+ !intercepted_profile_separation_policies.has_value() &&
(g_browser_process->system_network_context_manager() ||
- intercepted_account_level_policy_value_fetch_result_for_testing_)) {
+ intercepted_account_profile_separation_policies_for_testing_)) {
return absl::nullopt;
}
return false;
}
+
+void DiceWebSigninInterceptor::RecordSigninInterceptionHeuristicOutcome(
+ SigninInterceptionHeuristicOutcome outcome) const {
+ // Record the outcome.
+ base::UmaHistogramEnumeration("Signin.Intercept.HeuristicOutcome", outcome);
+
+ // Record the latency, except in the case where this is a duplicate request
+ // for the same interception.
+ DCHECK_NE(interception_start_time_, base::TimeTicks());
+ if (outcome ==
+ SigninInterceptionHeuristicOutcome::kAbortInterceptInProgress) {
+ // This is a special-case where we immediately abort the intercept request
+ // without first updating interception_start_time_ (because the previous
+ // request has not completed).
+ // Record the histogram for this request with zero duration.
+ base::UmaHistogramTimes("Signin.Intercept.HeuristicLatency",
+ base::Milliseconds(0));
+ } else {
+ base::UmaHistogramTimes("Signin.Intercept.HeuristicLatency",
+ base::TimeTicks::Now() - interception_start_time_);
+ }
+}
diff --git a/chromium/chrome/browser/signin/dice_web_signin_interceptor.h b/chromium/chrome/browser/signin/dice_web_signin_interceptor.h
index 1cf5ba5ca93..7fcb083a6f3 100644
--- a/chromium/chrome/browser/signin/dice_web_signin_interceptor.h
+++ b/chromium/chrome/browser/signin/dice_web_signin_interceptor.h
@@ -18,6 +18,7 @@
#include "chrome/browser/ui/webui/signin/enterprise_profile_welcome_ui.h"
#include "chrome/browser/ui/webui/signin/signin_utils.h"
#include "components/keyed_service/core/keyed_service.h"
+#include "components/policy/core/browser/signin/profile_separation_policies.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "google_apis/gaia/core_account_id.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -121,9 +122,9 @@ class DiceWebSigninInterceptor : public KeyedService,
return is_interception_in_progress_;
}
- void SetAccountLevelSigninRestrictionFetchResultForTesting(
- absl::optional<std::string> value) {
- intercepted_account_level_policy_value_fetch_result_for_testing_ =
+ void SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ absl::optional<policy::ProfileSeparationPolicies> value) {
+ intercepted_account_profile_separation_policies_for_testing_ =
std::move(value);
}
@@ -235,27 +236,30 @@ class DiceWebSigninInterceptor : public KeyedService,
// timeout.
void FetchAccountLevelSigninRestrictionForInterceptedAccount(
const AccountInfo& account_info,
- base::OnceCallback<void(const std::string&)> callback);
+ base::OnceCallback<void(const policy::ProfileSeparationPolicies&)>
+ callback);
// Called when the the value of the cloud user level value of the
// ManagedAccountsSigninRestriction is received.
void OnAccountLevelManagedAccountsSigninRestrictionReceived(
bool timed_out,
const AccountInfo& account_info,
- const std::string& signin_restriction);
+ const policy::ProfileSeparationPolicies& profile_separation_policies);
// Returns true if enterprise separation is required.
// Returns false is enterprise separation is not required.
// Returns no value if info is required to determine if enterprise separation
- // is required. If `managed_account_profile_level_signin_restriction` is
- // `absl::nullopt` then the user cloud policy value of
- // ManagedAccountsSigninRestriction has not yet been fetched. If it is an
- // empty string, then the value has been fetched but no policy was set.
+ // is required. If `profile_separation_policies` is `absl::nullopt` then the
+ // user cloud profile separation policies have not yet been fetched.
absl::optional<bool> EnterpriseSeparationMaybeRequired(
const std::string& email,
bool is_new_account_interception,
- absl::optional<std::string>
- managed_account_profile_level_signin_restriction) const;
+ const absl::optional<policy::ProfileSeparationPolicies>&
+ profile_separation_policies) const;
+
+ // Records the heuristic outcome and latency metrics.
+ void RecordSigninInterceptionHeuristicOutcome(
+ SigninInterceptionHeuristicOutcome outcome) const;
const raw_ptr<Profile, DanglingUntriaged> profile_;
const raw_ptr<signin::IdentityManager, DanglingUntriaged> identity_manager_;
@@ -282,7 +286,9 @@ class DiceWebSigninInterceptor : public KeyedService,
// Used to retain the interception UI bubble until profile creation completes.
std::unique_ptr<ScopedWebSigninInterceptionBubbleHandle>
interception_bubble_handle_;
- // Used for metrics:
+
+ // Used for metrics.
+ base::TimeTicks interception_start_time_;
bool was_interception_ui_displayed_ = false;
// Timeout for the fetch of cloud user level policy value of
@@ -298,9 +304,10 @@ class DiceWebSigninInterceptor : public KeyedService,
account_level_signin_restriction_policy_fetcher_;
// Value of the ManagedAccountsSigninRestriction for the intercepted account.
// If no value is set, then we have not yet received the policy value.
- absl::optional<std::string> intercepted_account_level_policy_value_;
- absl::optional<std::string>
- intercepted_account_level_policy_value_fetch_result_for_testing_;
+ absl::optional<policy::ProfileSeparationPolicies>
+ intercepted_account_profile_separation_policies_;
+ absl::optional<policy::ProfileSeparationPolicies>
+ intercepted_account_profile_separation_policies_for_testing_;
};
#endif // CHROME_BROWSER_SIGNIN_DICE_WEB_SIGNIN_INTERCEPTOR_H_
diff --git a/chromium/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc b/chromium/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
index 598c52d13d5..1309b2f2d4f 100644
--- a/chromium/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
+++ b/chromium/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
@@ -40,7 +40,6 @@
#include "chrome/test/base/ui_test_utils.h"
#include "components/account_id/account_id.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/policy/core/common/features.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
@@ -50,6 +49,7 @@
#include "google_apis/gaia/gaia_urls.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
#include "url/gurl.h"
namespace {
@@ -134,7 +134,7 @@ class FakeDiceWebSigninInterceptorDelegate
}
private:
- raw_ptr<Browser, DanglingUntriaged> fre_browser_ = nullptr;
+ raw_ptr<Browser, AcrossTasksDanglingUntriaged> fre_browser_ = nullptr;
CoreAccountId fre_account_id_;
WebSigninInterceptor::SigninInterceptionType expected_interception_type_ =
WebSigninInterceptor::SigninInterceptionType::kMultiUser;
@@ -238,7 +238,8 @@ class DiceWebSigninInterceptorBrowserTest : public SigninBrowserTestBase {
SigninBrowserTestBase::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
- ->SetAccountLevelSigninRestrictionFetchResultForTesting("");
+ ->SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ policy::ProfileSeparationPolicies(""));
}
void OnWillCreateBrowserContextServices(
@@ -313,8 +314,14 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest, InterceptionTest) {
ASSERT_TRUE(entry);
EXPECT_EQ("givenname", base::UTF16ToUTF8(entry->GetLocalProfileName()));
// Check the profile color.
- EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
- ->UsingAutogeneratedTheme());
+ if (features::IsChromeWebuiRefresh2023()) {
+ EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
+ ->GetUserColor()
+ .has_value());
+ } else {
+ EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
+ ->UsingAutogeneratedTheme());
+ }
// A browser has been created for the new profile and the tab was moved there.
Browser* added_browser = ui_test_utils::WaitForBrowserToOpen();
@@ -575,7 +582,7 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
// Install web app
Profile* profile = browser()->profile();
const GURL kWebAppURL("http://www.webapp.com");
- auto web_app_info = std::make_unique<WebAppInstallInfo>();
+ auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
web_app_info->start_url = kWebAppURL;
web_app_info->scope = kWebAppURL.GetWithoutFilename();
web_app_info->user_display_mode =
@@ -604,20 +611,8 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
SigninInterceptionHeuristicOutcome::kAbortNoSupportedBrowser, 1);
}
-class DiceWebSigninInterceptorEnterpriseBrowserTest
- : public DiceWebSigninInterceptorBrowserTest {
- public:
- DiceWebSigninInterceptorEnterpriseBrowserTest() {
- enterprise_feature_list_.InitAndEnableFeature(
- policy::features::kEnableUserCloudSigninRestrictionPolicyFetcher);
- }
-
- private:
- base::test::ScopedFeatureList enterprise_feature_list_;
-};
-
// Tests the complete interception flow including profile and browser creation.
-IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
+IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTestNoForcedInterception) {
base::HistogramTester histogram_tester;
@@ -651,7 +646,8 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"none");
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
- ->SetAccountLevelSigninRestrictionFetchResultForTesting("");
+ ->SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ policy::ProfileSeparationPolicies(""));
SetupGaiaResponses();
@@ -693,8 +689,14 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
ASSERT_TRUE(entry);
EXPECT_EQ("example.com", base::UTF16ToUTF8(entry->GetLocalProfileName()));
// Check the profile color.
- EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
- ->UsingAutogeneratedTheme());
+ if (features::IsChromeWebuiRefresh2023()) {
+ EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
+ ->GetUserColor()
+ .has_value());
+ } else {
+ EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
+ ->UsingAutogeneratedTheme());
+ }
// A browser has been created for the new profile and the tab was moved there.
Browser* added_browser = ui_test_utils::WaitForBrowserToOpen();
@@ -717,7 +719,7 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
}
// Tests the complete interception flow including profile and browser creation.
-IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
+IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
EnterpriseInterceptionDeclined) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
@@ -795,7 +797,7 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
}
// Tests the complete interception flow including profile and browser creation.
-IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
+IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTestAccountLevelPolicy) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
@@ -813,8 +815,8 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"none");
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
- ->SetAccountLevelSigninRestrictionFetchResultForTesting(
- "primary_account");
+ ->SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ policy::ProfileSeparationPolicies("primary_account"));
SetupGaiaResponses();
@@ -856,8 +858,14 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
ASSERT_TRUE(entry);
EXPECT_EQ("example.com", base::UTF16ToUTF8(entry->GetLocalProfileName()));
// Check the profile color.
- EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
- ->UsingAutogeneratedTheme());
+ if (features::IsChromeWebuiRefresh2023()) {
+ EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
+ ->GetUserColor()
+ .has_value());
+ } else {
+ EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
+ ->UsingAutogeneratedTheme());
+ }
// A browser has been created for the new profile and the tab was moved there.
Browser* added_browser = ui_test_utils::WaitForBrowserToOpen();
@@ -882,7 +890,7 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
// Tests the complete interception flow including profile and browser creation.
IN_PROC_BROWSER_TEST_F(
- DiceWebSigninInterceptorEnterpriseBrowserTest,
+ DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTestAccountLevelPolicyDeclined) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
@@ -900,8 +908,8 @@ IN_PROC_BROWSER_TEST_F(
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"none");
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
- ->SetAccountLevelSigninRestrictionFetchResultForTesting(
- "primary_account");
+ ->SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ policy::ProfileSeparationPolicies("primary_account"));
SetupGaiaResponses();
@@ -949,7 +957,7 @@ IN_PROC_BROWSER_TEST_F(
// Tests the complete interception flow including profile and browser creation.
IN_PROC_BROWSER_TEST_F(
- DiceWebSigninInterceptorEnterpriseBrowserTest,
+ DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTestAccountLevelPolicyStrictDeclined) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
@@ -967,8 +975,8 @@ IN_PROC_BROWSER_TEST_F(
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"none");
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
- ->SetAccountLevelSigninRestrictionFetchResultForTesting(
- "primary_account_strict");
+ ->SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ policy::ProfileSeparationPolicies("primary_account_strict"));
SetupGaiaResponses();
@@ -1015,7 +1023,7 @@ IN_PROC_BROWSER_TEST_F(
}
// Tests the complete interception flow including profile and browser creation.
-IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
+IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTest) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
@@ -1073,8 +1081,14 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
ASSERT_TRUE(entry);
EXPECT_EQ("example.com", base::UTF16ToUTF8(entry->GetLocalProfileName()));
// Check the profile color.
- EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
- ->UsingAutogeneratedTheme());
+ if (features::IsChromeWebuiRefresh2023()) {
+ EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
+ ->GetUserColor()
+ .has_value());
+ } else {
+ EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
+ ->UsingAutogeneratedTheme());
+ }
// A browser has been created for the new profile and the tab was moved there.
Browser* added_browser = ui_test_utils::WaitForBrowserToOpen();
@@ -1100,7 +1114,7 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
// Tests the complete interception flow for a reauth of the primary account of a
// non-syncing profile.
IN_PROC_BROWSER_TEST_F(
- DiceWebSigninInterceptorEnterpriseBrowserTest,
+ DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionPrimaryACcountReauthSyncDisabledTest) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
@@ -1167,7 +1181,7 @@ IN_PROC_BROWSER_TEST_F(
// Tests the complete interception flow for a reauth of the primary account of a
// syncing profile.
IN_PROC_BROWSER_TEST_F(
- DiceWebSigninInterceptorEnterpriseBrowserTest,
+ DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionPrimaryACcountReauthSyncEnabledTest) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
@@ -1231,7 +1245,7 @@ IN_PROC_BROWSER_TEST_F(
}
// Tests the complete profile switch flow when the profile is not loaded.
-IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
+IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
EnterpriseSwitchAndLoad) {
base::HistogramTester histogram_tester;
// Enforce enterprise profile separation.
@@ -1315,7 +1329,7 @@ IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
}
// Tests the complete profile switch flow when the profile is already loaded.
-IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
+IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
EnterpriseSwitchAlreadyOpen) {
base::HistogramTester histogram_tester;
// Enforce enterprise profile separation.
diff --git a/chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.cc b/chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.cc
index 7166d062cd1..c98ba224874 100644
--- a/chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.cc
+++ b/chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.cc
@@ -35,9 +35,10 @@ void DiceWebSigninInterceptorFactory::RegisterProfilePrefs(
DiceWebSigninInterceptor::RegisterProfilePrefs(registry);
}
-KeyedService* DiceWebSigninInterceptorFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+DiceWebSigninInterceptorFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
- return new DiceWebSigninInterceptor(
+ return std::make_unique<DiceWebSigninInterceptor>(
Profile::FromBrowserContext(context),
std::make_unique<DiceWebSigninInterceptorDelegate>());
}
diff --git a/chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.h b/chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.h
index 40920ddf759..4836686bfe3 100644
--- a/chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.h
+++ b/chromium/chrome/browser/signin/dice_web_signin_interceptor_factory.h
@@ -29,7 +29,7 @@ class DiceWebSigninInterceptorFactory : public ProfileKeyedServiceFactory {
// BrowserContextKeyedServiceFactory:
void RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) override;
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc b/chromium/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
index 57059f9846e..18751998933 100644
--- a/chromium/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
+++ b/chromium/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
@@ -29,6 +29,7 @@
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
+#include "components/policy/core/browser/signin/profile_separation_policies.h"
#include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/signin_pref_names.h"
@@ -113,25 +114,18 @@ void MakeValidAccountInfo(
}
std::string ParamToTestSuffixForInterceptionAndSyncPromo(
- const ::testing::TestParamInfo<std::tuple<bool, bool>> info) {
- bool interception_enabled = std::get<0>(info.param);
- bool sync_promo_enabled = std::get<1>(info.param);
- return base::StrCat(
- {interception_enabled ? "Intercept" : "NoIntercept",
- sync_promo_enabled ? "WithSyncPromo" : "WithoutSyncPromo"});
-}
-
-std::string ParamToTestSuffixForSyncPromo(
const ::testing::TestParamInfo<bool> info) {
- bool sync_promo_enabled = info.param;
- return sync_promo_enabled ? "WithSyncPromo" : "WithoutSyncPromo";
+ bool interception_enabled = info.param;
+ return interception_enabled ? "Intercept" : "NoIntercept";
}
} // namespace
class DiceWebSigninInterceptorTest : public BrowserWithTestWindowTest {
public:
- DiceWebSigninInterceptorTest() = default;
+ DiceWebSigninInterceptorTest()
+ : BrowserWithTestWindowTest(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
~DiceWebSigninInterceptorTest() override = default;
DiceWebSigninInterceptor* interceptor() {
@@ -186,6 +180,9 @@ class DiceWebSigninInterceptorTest : public BrowserWithTestWindowTest {
testing::Mock::VerifyAndClearExpectations(mock_delegate());
histogram_tester.ExpectUniqueSample("Signin.Intercept.HeuristicOutcome",
expected_outcome, 1);
+ histogram_tester.ExpectUniqueTimeSample("Signin.Intercept.HeuristicLatency",
+ base::Milliseconds(0), 1);
+
EXPECT_EQ(interceptor()->is_interception_in_progress(),
SigninInterceptionHeuristicOutcomeIsSuccess(expected_outcome));
}
@@ -210,6 +207,8 @@ class DiceWebSigninInterceptorTest : public BrowserWithTestWindowTest {
testing::Mock::VerifyAndClearExpectations(mock_delegate());
histogram_tester.ExpectUniqueSample("Signin.Intercept.HeuristicOutcome",
expected_outcome, 1);
+ histogram_tester.ExpectUniqueTimeSample("Signin.Intercept.HeuristicLatency",
+ base::Milliseconds(0), 1);
EXPECT_EQ(interceptor()->is_interception_in_progress(),
SigninInterceptionHeuristicOutcomeIsSuccess(expected_outcome));
}
@@ -445,19 +444,10 @@ TEST_F(DiceWebSigninInterceptorTest,
class DiceWebSigninInterceptorManagedAccountTest
: public DiceWebSigninInterceptorTest,
- public testing::WithParamInterface<std::tuple<bool, bool>> {
+ public testing::WithParamInterface<bool> {
public:
DiceWebSigninInterceptorManagedAccountTest()
- : signin_interception_enabled_(std::get<0>(GetParam())),
- sync_promo_enabled_(std::get<1>(GetParam())) {
- if (sync_promo_enabled_) {
- scoped_feature_list_.InitAndEnableFeature(kSyncPromoAfterSigninIntercept);
- } else {
- scoped_feature_list_.InitWithFeatures(
- /*enabled_features=*/{}, /*disabled_features=*/{
- kSigninInterceptBubbleV2, kSyncPromoAfterSigninIntercept});
- }
- }
+ : signin_interception_enabled_(GetParam()) {}
protected:
void SetUp() override {
@@ -467,10 +457,6 @@ class DiceWebSigninInterceptorManagedAccountTest
}
bool signin_interception_enabled_;
- bool sync_promo_enabled_;
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_P(DiceWebSigninInterceptorManagedAccountTest,
@@ -484,13 +470,14 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
"alice@example.com", signin::ConsentLevel::kSignin);
MakeValidAccountInfo(&account_info, "example.com");
identity_test_env()->UpdateAccountInfoForAccount(account_info);
- interceptor()->SetAccountLevelSigninRestrictionFetchResultForTesting("");
+ interceptor()->SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ policy::ProfileSeparationPolicies(""));
WebSigninInterceptor::Delegate::BubbleParameters expected_parameters(
WebSigninInterceptor::SigninInterceptionType::kEnterpriseAcceptManagement,
account_info, account_info, SkColor(), /*show_guest_option=*/false,
/*show_link_data_option=*/true,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
@@ -508,7 +495,8 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
"alice@example.com", signin::ConsentLevel::kSignin);
MakeValidAccountInfo(&account_info, "example.com");
identity_test_env()->UpdateAccountInfoForAccount(account_info);
- interceptor()->SetAccountLevelSigninRestrictionFetchResultForTesting("");
+ interceptor()->SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ policy::ProfileSeparationPolicies(""));
if (signin_interception_enabled_) {
TestAsynchronousInterception(
@@ -542,7 +530,7 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced,
account_info, account_info, SkColor(), /*show_guest_option=*/false,
/*show_link_data_option=*/false,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
@@ -568,7 +556,7 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced,
account_info, AccountInfo(), SkColor(), /*show_guest_option=*/false,
/*show_link_data_option=*/false,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
@@ -585,15 +573,15 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
MakeValidAccountInfo(&account_info, "example.com");
identity_test_env()->UpdateAccountInfoForAccount(account_info);
- interceptor()->SetAccountLevelSigninRestrictionFetchResultForTesting(
- "primary_account_keep_existing_data");
+ interceptor()->SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ policy::ProfileSeparationPolicies("primary_account_keep_existing_data"));
// Check that interception works otherwise, as a sanity check.
WebSigninInterceptor::Delegate::BubbleParameters expected_parameters(
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced,
account_info, AccountInfo(), SkColor(), /*show_guest_option=*/false,
/*show_link_data_option=*/true,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
@@ -626,7 +614,7 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced,
account_info, primary_account_info, SkColor(),
/*show_guest_option=*/false, /*show_link_data_option=*/false,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
@@ -651,7 +639,7 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced,
account_info, AccountInfo(), SkColor(), /*show_guest_option=*/false,
/*show_link_data_option=*/true,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
@@ -682,7 +670,7 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced,
account_info, primary_account_info, SkColor(),
/*show_guest_option=*/false, /*show_link_data_option=*/false,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
@@ -717,7 +705,7 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
WebSigninInterceptor::SigninInterceptionType::kProfileSwitchForced,
account_info, AccountInfo(), SkColor(), /*show_guest_option=*/false,
/*show_link_data_option=*/false,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
@@ -730,45 +718,9 @@ TEST_P(DiceWebSigninInterceptorManagedAccountTest,
INSTANTIATE_TEST_SUITE_P(All,
DiceWebSigninInterceptorManagedAccountTest,
- ::testing::Combine(::testing::Bool(),
- ::testing::Bool()),
+ ::testing::Bool(),
&ParamToTestSuffixForInterceptionAndSyncPromo);
-// This test suite rewrites and parameterize previous tests from
-// DiceWebSigninInterceptorTest which would show a managed disclaimer when the
-// kSyncPromoAfterSigninIntercept feature is enabled, but wouldn't show it in
-// the original tests with the feature disabled.
-// TODO(crbug.com/1282157): This test suite should be removed when
-// kSyncPromoAfterSigninIntercept is fully launched and these tests should move
-// back to the original suite.
-class DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest
- : public DiceWebSigninInterceptorTest,
- public testing::WithParamInterface<bool> {
- public:
- DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest()
- : sync_promo_enabled_(GetParam()) {
- if (sync_promo_enabled_) {
- scoped_feature_list_.InitAndEnableFeature(kSyncPromoAfterSigninIntercept);
- } else {
- scoped_feature_list_.InitWithFeatures(
- /*enabled_features=*/{}, /*disabled_features=*/{
- kSigninInterceptBubbleV2, kSyncPromoAfterSigninIntercept});
- }
- }
-
- protected:
- bool sync_promo_enabled_;
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-INSTANTIATE_TEST_SUITE_P(
- All,
- DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
- ::testing::Bool(),
- &ParamToTestSuffixForSyncPromo);
-
TEST_F(DiceWebSigninInterceptorTest, ShouldShowEnterpriseBubbleWithoutUPA) {
AccountInfo account_info_1 =
identity_test_env()->MakeAccountAvailable("bob@example.com");
@@ -992,8 +944,7 @@ TEST_F(DiceWebSigninInterceptorTest, InterceptionInProgress) {
MaybeIntercept(account_info.account_id);
}
-TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
- DeclineCreationRepeatedly) {
+TEST_F(DiceWebSigninInterceptorTest, DeclineCreationRepeatedly) {
base::HistogramTester histogram_tester;
AccountInfo primary_account_info =
identity_test_env()->MakePrimaryAccountAvailable(
@@ -1009,7 +960,7 @@ TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
WebSigninInterceptor::SigninInterceptionType::kEnterprise, account_info,
primary_account_info, SkColor(),
/*show_guest_option=*/false, /*show_link_data_option=*/false,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
for (int i = 0; i < kMaxProfileCreationDeclinedCount; ++i) {
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
@@ -1052,7 +1003,7 @@ TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
}
// Regression test for https://crbug.com/1309647
-TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
+TEST_F(DiceWebSigninInterceptorTest,
DeclineCreationRepeatedlyWithPolicyFetcher) {
base::HistogramTester histogram_tester;
AccountInfo primary_account_info =
@@ -1063,7 +1014,8 @@ TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
MakeValidAccountInfo(&account_info, "example.com");
identity_test_env()->UpdateAccountInfoForAccount(account_info);
- interceptor()->SetAccountLevelSigninRestrictionFetchResultForTesting("");
+ interceptor()->SetInterceptedAccountProfileSeparationPoliciesForTesting(
+ policy::ProfileSeparationPolicies(""));
const int kMaxProfileCreationDeclinedCount = 2;
// Decline the interception kMaxProfileCreationDeclinedCount times.
@@ -1071,7 +1023,7 @@ TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
WebSigninInterceptor::SigninInterceptionType::kEnterprise, account_info,
primary_account_info, SkColor(),
/*show_guest_option=*/false, /*show_link_data_option=*/false,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
for (int i = 0; i < kMaxProfileCreationDeclinedCount; ++i) {
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
@@ -1221,8 +1173,7 @@ TEST_F(DiceWebSigninInterceptorTest, ProfileCreationDisallowed) {
MaybeIntercept(account_info.account_id);
}
-TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
- WaitForAccountInfoAvailable) {
+TEST_F(DiceWebSigninInterceptorTest, WaitForAccountInfoAvailable) {
base::HistogramTester histogram_tester;
AccountInfo primary_account_info =
identity_test_env()->MakePrimaryAccountAvailable(
@@ -1244,7 +1195,7 @@ TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
WebSigninInterceptor::SigninInterceptionType::kEnterprise, account_info,
primary_account_info, SkColor(),
/*show_guest_option=*/false, /*show_link_data_option=*/false,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
@@ -1253,8 +1204,7 @@ TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
identity_test_env()->UpdateAccountInfoForAccount(account_info);
}
-TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
- AccountInfoAlreadyAvailable) {
+TEST_F(DiceWebSigninInterceptorTest, AccountInfoAlreadyAvailable) {
base::HistogramTester histogram_tester;
AccountInfo primary_account_info =
identity_test_env()->MakePrimaryAccountAvailable(
@@ -1269,7 +1219,7 @@ TEST_P(DiceWebSigninInterceptorWithManagedDisclaimerForSyncPromoTest,
WebSigninInterceptor::SigninInterceptionType::kEnterprise, account_info,
primary_account_info, SkColor(),
/*show_guest_option=*/false, /*show_link_data_option=*/false,
- /*show_managed_disclaimer=*/sync_promo_enabled_);
+ /*show_managed_disclaimer=*/true);
EXPECT_CALL(*mock_delegate(),
ShowSigninInterceptionBubble(
web_contents(), MatchBubbleParameters(expected_parameters),
diff --git a/chromium/chrome/browser/signin/e2e_tests/live_sign_in_test.cc b/chromium/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
index 0f9a061638b..6243d244c6a 100644
--- a/chromium/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
+++ b/chromium/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
@@ -17,8 +17,8 @@
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/profile_picker.h"
-#include "chrome/browser/ui/profile_ui_test_utils.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
+#include "chrome/browser/ui/profiles/profile_ui_test_utils.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
diff --git a/chromium/chrome/browser/signin/e2e_tests/live_test.cc b/chromium/chrome/browser/signin/e2e_tests/live_test.cc
index d3421a2eab7..6e341c05d14 100644
--- a/chromium/chrome/browser/signin/e2e_tests/live_test.cc
+++ b/chromium/chrome/browser/signin/e2e_tests/live_test.cc
@@ -40,6 +40,9 @@ void LiveTest::SetUp() {
base::PathService::Get(base::BasePathKey::DIR_SOURCE_ROOT, &root_path);
base::FilePath config_path =
base::MakeAbsoluteFilePath(root_path.Append(kTestAccountFilePath));
+ CHECK(!config_path.empty()) << kTestAccountFilePath
+ << " does not exist. This file is only available "
+ "in Google-internal checkouts.";
test_accounts_.Init(config_path);
InProcessBrowserTest::SetUp();
}
diff --git a/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.cc b/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.cc
index 11493a7864a..53fbfededeb 100644
--- a/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.cc
+++ b/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.cc
@@ -41,8 +41,8 @@ bool TestAccountsUtil::Init(const base::FilePath& config_path) {
JSONFileValueDeserializer deserializer(config_path);
std::unique_ptr<Value> content_json =
deserializer.Deserialize(&error_code, &error_str);
- CHECK(error_code == 0) << "Error reading json file. Error code: "
- << error_code << " " << error_str;
+ CHECK(error_code == 0) << "Error reading json file at " << config_path
+ << ". Error code: " << error_code << " " << error_str;
CHECK(content_json);
// Only store platform specific users. If an account does not have
diff --git a/chromium/chrome/browser/signin/force_signin_verifier.cc b/chromium/chrome/browser/signin/force_signin_verifier.cc
index e28e84d6260..23d4a05b1a4 100644
--- a/chromium/chrome/browser/signin/force_signin_verifier.cc
+++ b/chromium/chrome/browser/signin/force_signin_verifier.cc
@@ -16,7 +16,7 @@
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/browser/ui/ui_features.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/identity_manager/access_token_info.h"
@@ -52,6 +52,8 @@ ForceSigninVerifier::ForceSigninVerifier(
content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
// Most of time (~94%), sign-in token can be verified with server.
SendRequest();
+
+ identity_manager_observer.Observe(identity_manager);
}
ForceSigninVerifier::~ForceSigninVerifier() {
@@ -95,8 +97,9 @@ void ForceSigninVerifier::OnConnectionChanged(
// Try again immediately once the network is back and cancel any pending
// request.
backoff_entry_.Reset();
- if (backoff_request_timer_.IsRunning())
+ if (backoff_request_timer_.IsRunning()) {
backoff_request_timer_.Stop();
+ }
SendRequestIfNetworkAvailable(type);
}
@@ -124,7 +127,8 @@ void ForceSigninVerifier::SendRequest() {
void ForceSigninVerifier::SendRequestIfNetworkAvailable(
network::mojom::ConnectionType network_type) {
- if (network_type == network::mojom::ConnectionType::CONNECTION_NONE ||
+ if (!identity_manager_ ||
+ network_type == network::mojom::ConnectionType::CONNECTION_NONE ||
!ShouldSendRequest()) {
return;
}
@@ -136,11 +140,13 @@ void ForceSigninVerifier::SendRequestIfNetworkAvailable(
"force_signin_verifier", identity_manager_, oauth2_scopes,
base::BindOnce(&ForceSigninVerifier::OnAccessTokenFetchComplete,
weak_factory_.GetWeakPtr()),
- signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
+ signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate,
+ signin::ConsentLevel::kSync);
}
bool ForceSigninVerifier::ShouldSendRequest() {
return !has_token_verified_ && access_token_fetcher_.get() == nullptr &&
+ identity_manager_ &&
identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSync);
}
@@ -163,8 +169,9 @@ void ForceSigninVerifier::OnCloseBrowsersSuccess(
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(profile_path);
- if (!entry)
+ if (!entry) {
return;
+ }
entry->LockForceSigninProfile(true);
ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
ProfilePicker::EntryPoint::kProfileLocked));
@@ -182,3 +189,10 @@ net::BackoffEntry* ForceSigninVerifier::GetBackoffEntryForTesting() {
base::OneShotTimer* ForceSigninVerifier::GetOneShotTimerForTesting() {
return &backoff_request_timer_;
}
+
+void ForceSigninVerifier::OnIdentityManagerShutdown(
+ signin::IdentityManager* identity_manager) {
+ identity_manager_observer.Reset();
+
+ identity_manager_ = nullptr;
+}
diff --git a/chromium/chrome/browser/signin/force_signin_verifier.h b/chromium/chrome/browser/signin/force_signin_verifier.h
index 03adceb5a0f..37a9a48cebe 100644
--- a/chromium/chrome/browser/signin/force_signin_verifier.h
+++ b/chromium/chrome/browser/signin/force_signin_verifier.h
@@ -9,8 +9,10 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/base/backoff_entry.h"
#include "services/network/public/cpp/network_connection_tracker.h"
@@ -31,7 +33,8 @@ struct AccessTokenInfo;
// into memory by the first time via gaia server. It will retry on any transient
// error.
class ForceSigninVerifier
- : public network::NetworkConnectionTracker::NetworkConnectionObserver {
+ : public network::NetworkConnectionTracker::NetworkConnectionObserver,
+ public signin::IdentityManager::Observer {
public:
explicit ForceSigninVerifier(Profile* profile,
signin::IdentityManager* identity_manager);
@@ -53,6 +56,10 @@ class ForceSigninVerifier
// Return the value of |has_token_verified_|.
bool HasTokenBeenVerified();
+ // signin::IdentityManager::Observer:
+ void OnIdentityManagerShutdown(
+ signin::IdentityManager* identity_manager) override;
+
protected:
// Send the token verification request. The request will be sent only if
// - The token has never been verified before.
@@ -89,6 +96,15 @@ class ForceSigninVerifier
raw_ptr<Profile> profile_ = nullptr;
raw_ptr<signin::IdentityManager> identity_manager_ = nullptr;
+ // We need this observer in order to reset the value of the reference
+ // to the `identity_manager_`.
+ // `ForceSigninVerifier` instance lives in `ChromeSigninClient`, for which
+ // `IdentityManager` already has a dependency. Therefore we cannot add a
+ // regular KeyedService factory as it would create a circular dependency.
+ base::ScopedObservation<signin::IdentityManager,
+ signin::IdentityManager::Observer>
+ identity_manager_observer{this};
+
base::WeakPtrFactory<ForceSigninVerifier> weak_factory_{this};
};
diff --git a/chromium/chrome/browser/signin/header_modification_delegate_impl.cc b/chromium/chrome/browser/signin/header_modification_delegate_impl.cc
index 12b1dcc063e..8f7d8723dbc 100644
--- a/chromium/chrome/browser/signin/header_modification_delegate_impl.cc
+++ b/chromium/chrome/browser/signin/header_modification_delegate_impl.cc
@@ -7,7 +7,6 @@
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
-#include "chrome/browser/extensions/api/identity/web_auth_flow.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/chrome_signin_helper.h"
@@ -22,18 +21,24 @@
#include "components/signin/public/identity_manager/tribool.h"
#include "components/sync/base/pref_names.h"
#include "components/sync/service/sync_service.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/site_instance.h"
+#include "content/public/browser/storage_partition.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
#endif
-
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "components/account_manager_core/pref_names.h"
#endif
+#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
+#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher.h"
+#include "chrome/browser/signin/bound_session_credentials/bound_session_registration_fetcher_impl.h"
+#include "chrome/browser/signin/bound_session_credentials/unexportable_key_service_factory.h"
+#endif
+
namespace signin {
#if BUILDFLAG(IS_ANDROID)
@@ -64,8 +69,9 @@ bool HeaderModificationDelegateImpl::ShouldInterceptNavigation(
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
- if (ShouldIgnoreGuestWebViewRequest(contents))
+ if (ShouldIgnoreGuestWebViewRequest(contents)) {
return false;
+ }
#endif
return true;
@@ -141,6 +147,26 @@ void HeaderModificationDelegateImpl::ProcessResponse(
ResponseAdapter* response_adapter,
const GURL& redirect_url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
+ if (switches::IsBoundSessionCredentialsEnabled()) {
+ BoundSessionCookieRefreshService* bound_session_cookie_refresh_service =
+ BoundSessionCookieRefreshServiceFactory::GetForProfile(profile_);
+ if (bound_session_cookie_refresh_service) {
+ // Terminate the session if session termination header is set.
+ bound_session_cookie_refresh_service->MaybeTerminateSession(
+ response_adapter->GetHeaders());
+
+ auto params = BoundSessionRegistrationFetcherParam::MaybeCreateInstance(
+ response_adapter->GetUrl(), response_adapter->GetHeaders());
+ if (params.has_value()) {
+ bound_session_cookie_refresh_service->CreateRegistrationRequest(
+ std::move(params).value());
+ }
+ }
+ }
+#endif
+
if (profile_->IsOffTheRecord()) {
// We expect seeing traffic from OTR profiles only if the feature is
// enabled.
@@ -160,22 +186,14 @@ void HeaderModificationDelegateImpl::ProcessResponse(
// static
bool HeaderModificationDelegateImpl::ShouldIgnoreGuestWebViewRequest(
content::WebContents* contents) {
- if (!contents)
+ if (!contents) {
return true;
+ }
if (extensions::WebViewRendererState::GetInstance()->IsGuest(
contents->GetPrimaryMainFrame()->GetProcess()->GetID())) {
- auto identity_api_config =
- extensions::WebAuthFlow::GetWebViewPartitionConfig(
- extensions::WebAuthFlow::GET_AUTH_TOKEN,
- contents->GetBrowserContext());
- if (contents->GetSiteInstance()->GetStoragePartitionConfig() !=
- identity_api_config)
- return true;
-
- // If the StoragePartitionConfig matches, but |contents| is not using a
- // guest SiteInstance, then there is likely a serious bug.
CHECK(contents->GetSiteInstance()->IsGuest());
+ return true;
}
return false;
}
diff --git a/chromium/chrome/browser/signin/header_modification_delegate_impl.h b/chromium/chrome/browser/signin/header_modification_delegate_impl.h
index 209084a1985..ac6ee49cafb 100644
--- a/chromium/chrome/browser/signin/header_modification_delegate_impl.h
+++ b/chromium/chrome/browser/signin/header_modification_delegate_impl.h
@@ -10,7 +10,6 @@
#include "build/buildflag.h"
#include "chrome/browser/signin/header_modification_delegate.h"
#include "components/content_settings/core/browser/cookie_settings.h"
-#include "content/public/browser/browser_thread.h"
#include "extensions/buildflags/buildflags.h"
class Profile;
diff --git a/chromium/chrome/browser/signin/identity_manager_factory.cc b/chromium/chrome/browser/signin/identity_manager_factory.cc
index 9cf4c48627d..77522f65b7b 100644
--- a/chromium/chrome/browser/signin/identity_manager_factory.cc
+++ b/chromium/chrome/browser/signin/identity_manager_factory.cc
@@ -142,9 +142,12 @@ KeyedService* IdentityManagerFactory::BuildServiceInstanceFor(
params.signin_client = ChromeSigninClientFactory::GetForProfile(profile);
#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
- params.delete_signin_cookies_on_exit =
- signin::SettingsDeleteSigninCookiesOnExit(
- CookieSettingsFactory::GetForProfile(profile).get());
+ {
+ scoped_refptr<content_settings::CookieSettings> cookie_settings =
+ CookieSettingsFactory::GetForProfile(profile);
+ params.delete_signin_cookies_on_exit =
+ signin::SettingsDeleteSigninCookiesOnExit(cookie_settings.get());
+ }
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
diff --git a/chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.cc b/chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.cc
index 39b79bebd86..a15db58e9df 100644
--- a/chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.cc
+++ b/chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.cc
@@ -39,10 +39,8 @@ IdentityTestEnvironmentProfileAdaptor::CreateProfileForIdentityTestEnvironment(
// static
std::unique_ptr<TestingProfile>
IdentityTestEnvironmentProfileAdaptor::CreateProfileForIdentityTestEnvironment(
- TestingProfile::Builder& builder,
- signin::AccountConsistencyMethod account_consistency) {
- for (auto& identity_factory :
- GetIdentityTestEnvironmentFactories(account_consistency)) {
+ TestingProfile::Builder& builder) {
+ for (auto& identity_factory : GetIdentityTestEnvironmentFactories()) {
builder.AddTestingFactory(identity_factory.first, identity_factory.second);
}
@@ -52,10 +50,8 @@ IdentityTestEnvironmentProfileAdaptor::CreateProfileForIdentityTestEnvironment(
// static
void IdentityTestEnvironmentProfileAdaptor::
SetIdentityTestEnvironmentFactoriesOnBrowserContext(
- content::BrowserContext* context,
- signin::AccountConsistencyMethod account_consistency) {
- for (const auto& factory_pair :
- GetIdentityTestEnvironmentFactories(account_consistency)) {
+ content::BrowserContext* context) {
+ for (const auto& factory_pair : GetIdentityTestEnvironmentFactories()) {
factory_pair.first->SetTestingFactory(context, factory_pair.second);
}
}
@@ -73,17 +69,14 @@ void IdentityTestEnvironmentProfileAdaptor::
// static
TestingProfile::TestingFactories
-IdentityTestEnvironmentProfileAdaptor::GetIdentityTestEnvironmentFactories(
- signin::AccountConsistencyMethod account_consistency) {
+IdentityTestEnvironmentProfileAdaptor::GetIdentityTestEnvironmentFactories() {
return {{IdentityManagerFactory::GetInstance(),
- base::BindRepeating(&BuildIdentityManagerForTests,
- account_consistency)}};
+ base::BindRepeating(&BuildIdentityManagerForTests)}};
}
// static
std::unique_ptr<KeyedService>
IdentityTestEnvironmentProfileAdaptor::BuildIdentityManagerForTests(
- signin::AccountConsistencyMethod account_consistency,
content::BrowserContext* context) {
Profile* profile = Profile::FromBrowserContext(context);
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -95,7 +88,7 @@ IdentityTestEnvironmentProfileAdaptor::BuildIdentityManagerForTests(
#else
return signin::IdentityTestEnvironment::BuildIdentityManagerForTests(
ChromeSigninClientFactory::GetForProfile(profile), profile->GetPrefs(),
- profile->GetPath(), account_consistency);
+ profile->GetPath());
#endif
}
diff --git a/chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.h b/chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.h
index dfafb8b213d..671499fb3fa 100644
--- a/chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.h
+++ b/chromium/chrome/browser/signin/identity_test_environment_profile_adaptor.h
@@ -35,18 +35,13 @@ class IdentityTestEnvironmentProfileAdaptor {
// IdentityTestEnvironment requires.
// See the above variant for comments on common parameters.
static std::unique_ptr<TestingProfile>
- CreateProfileForIdentityTestEnvironment(
- TestingProfile::Builder& builder,
- signin::AccountConsistencyMethod account_consistency =
- signin::AccountConsistencyMethod::kDisabled);
+ CreateProfileForIdentityTestEnvironment(TestingProfile::Builder& builder);
// Sets the testing factories that signin::IdentityTestEnvironment
// requires explicitly on a Profile that is passed to it.
// See the above variant for comments on common parameters.
static void SetIdentityTestEnvironmentFactoriesOnBrowserContext(
- content::BrowserContext* browser_context,
- signin::AccountConsistencyMethod account_consistency =
- signin::AccountConsistencyMethod::kDisabled);
+ content::BrowserContext* browser_context);
// Appends the set of testing factories that signin::IdentityTestEnvironment
// requires to |factories_to_append_to|, which should be the set of testing
@@ -63,9 +58,7 @@ class IdentityTestEnvironmentProfileAdaptor {
// Returns the set of testing factories that signin::IdentityTestEnvironment
// requires, which can be useful to configure profiles for services that do
// not require any other testing factory than the ones specified in here.
- static TestingProfile::TestingFactories GetIdentityTestEnvironmentFactories(
- signin::AccountConsistencyMethod account_consistency =
- signin::AccountConsistencyMethod::kDisabled);
+ static TestingProfile::TestingFactories GetIdentityTestEnvironmentFactories();
// Constructs an adaptor that associates an IdentityTestEnvironment instance
// with |profile| via the relevant backing objects. Note that
@@ -94,7 +87,6 @@ class IdentityTestEnvironmentProfileAdaptor {
// Testing factory that creates an IdentityManager
// with a FakeProfileOAuth2TokenService.
static std::unique_ptr<KeyedService> BuildIdentityManagerForTests(
- signin::AccountConsistencyMethod account_consistency,
content::BrowserContext* context);
signin::IdentityTestEnvironment identity_test_env_;
diff --git a/chromium/chrome/browser/signin/mirror_browsertest.cc b/chromium/chrome/browser/signin/mirror_browsertest.cc
index cef7c54bf6c..7ad5e2f6640 100644
--- a/chromium/chrome/browser/signin/mirror_browsertest.cc
+++ b/chromium/chrome/browser/signin/mirror_browsertest.cc
@@ -99,35 +99,6 @@ class MirrorBrowserTest : public InProcessBrowserTest {
// disable this feature.
feature_list_.InitAndDisableFeature(features::kHttpsUpgrades);
}
- void RunExtensionConsentTest(extensions::WebAuthFlow::Partition partition,
- bool expects_header) {
- net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
- https_server.AddDefaultHandlers(GetChromeTestDataDir());
- const std::string kAuthPath = "/auth";
- net::test_server::HttpRequest::HeaderMap headers;
- base::RunLoop run_loop;
- https_server.RegisterRequestMonitor(base::BindLambdaForTesting(
- [&](const net::test_server::HttpRequest& request) {
- if (request.GetURL().path() != kAuthPath)
- return;
-
- headers = request.headers;
- run_loop.Quit();
- }));
- ASSERT_TRUE(https_server.Start());
-
- auto web_auth_flow = std::make_unique<extensions::WebAuthFlow>(
- nullptr, browser()->profile(),
- https_server.GetURL("google.com", kAuthPath),
- extensions::WebAuthFlow::INTERACTIVE, partition, /*user_gesture=*/true);
-
- web_auth_flow->Start();
- run_loop.Run();
- EXPECT_EQ(!!headers.count(signin::kChromeConnectedHeader), expects_header);
-
- web_auth_flow.release()->DetachDelegateAndDelete();
- base::RunLoop().RunUntilIdle();
- }
private:
void SetUpOnMainThread() override {
@@ -270,33 +241,4 @@ IN_PROC_BROWSER_TEST_F(MirrorBrowserTest, MirrorRequestHeader) {
}
}
-// These tests should be removed once `features::kWebAuthFlowInBrowserTab` is
-// launched.
-class MirrorBrowserTestWithWebAuthFlowInBrowserTabOff
- : public MirrorBrowserTest {
- public:
- MirrorBrowserTestWithWebAuthFlowInBrowserTabOff() {
- scoped_feature_list_.InitAndDisableFeature(
- features::kWebAuthFlowInBrowserTab);
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-// Verifies that requests originated from chrome.identity.launchWebAuthFlow()
-// API don't have Mirror headers attached.
-// This is a regression test for crbug.com/1077504.
-IN_PROC_BROWSER_TEST_F(MirrorBrowserTestWithWebAuthFlowInBrowserTabOff,
- NoMirrorExtensionConsent_LaunchWebAuthFlow) {
- RunExtensionConsentTest(extensions::WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, false);
-}
-
-// Verifies that requests originated from chrome.identity.getAuthToken()
-// API have Mirror headers attached.
-IN_PROC_BROWSER_TEST_F(MirrorBrowserTestWithWebAuthFlowInBrowserTabOff,
- MirrorExtensionConsent_GetAuthToken) {
- RunExtensionConsentTest(extensions::WebAuthFlow::GET_AUTH_TOKEN, true);
-}
-
} // namespace
diff --git a/chromium/chrome/browser/signin/mirror_interactive_uitest.cc b/chromium/chrome/browser/signin/mirror_interactive_uitest.cc
index f6d6e62054f..17710cc3a77 100644
--- a/chromium/chrome/browser/signin/mirror_interactive_uitest.cc
+++ b/chromium/chrome/browser/signin/mirror_interactive_uitest.cc
@@ -62,8 +62,7 @@ class MirrorResponseBrowserTest : public InProcessBrowserTest {
// "X-Chrome-Manage-Accounts" header.
void ReceiveManageAccountsHeader(
const base::flat_map<std::string, std::string>& header_params) {
- ASSERT_TRUE(ui_test_utils::NavigateToURL(
- browser(), GetUrlWithManageAccountsHeader(header_params)));
+ NavigateToURL(GetUrlWithManageAccountsHeader(header_params), absl::nullopt);
}
GURL GetUrlWithManageAccountsHeader(
@@ -79,6 +78,21 @@ class MirrorResponseBrowserTest : public InProcessBrowserTest {
return https_server_.GetURL(path);
}
+ // Helper method to navigate with an optional request initiator origin.
+ void NavigateToURL(const GURL& url,
+ absl::optional<url::Origin> initiator_origin) {
+ NavigateParams params(browser(), url, ui::PAGE_TRANSITION_TYPED);
+ params.disposition = WindowOpenDisposition::CURRENT_TAB;
+ if (initiator_origin) {
+ // `is_renderer_initiated` requires non-null `initiator_origin`.
+ params.is_renderer_initiated = true;
+ params.initiator_origin = initiator_origin;
+ }
+ Navigate(&params);
+ EXPECT_TRUE(
+ content::WaitForLoadStop(params.navigated_or_inserted_contents));
+ }
+
// InProcessBrowserTest:
void SetUp() override {
https_server_.AddDefaultHandlers(GetChromeTestDataDir());
@@ -139,17 +153,68 @@ IN_PROC_BROWSER_TEST_F(MirrorResponseBrowserTest, Reauth) {
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
-// Tests that incognito browser is opened when receiving "INCOGNITO" from Gaia.
+// When receiving "INCOGNITO" from Gaia and the request is initiated by a Google
+// domain - an incognito tab should be opened.
IN_PROC_BROWSER_TEST_F(MirrorResponseBrowserTest, Incognito) {
+ base::HistogramTester histogram_tester;
+ size_t browser_count = chrome::GetTotalBrowserCount();
ui_test_utils::BrowserChangeObserver browser_change_observer(
/*browser=*/nullptr,
ui_test_utils::BrowserChangeObserver::ChangeType::kAdded);
- ReceiveManageAccountsHeader({{"action", "INCOGNITO"}});
+
+ NavigateToURL(GetUrlWithManageAccountsHeader({{"action", "INCOGNITO"}}),
+ url::Origin::Create(GURL("https://google.com")));
+
+ // Incognito window should have been displayed, the browser count goes up.
+ EXPECT_GT(chrome::GetTotalBrowserCount(), browser_count);
+
+ // No waiting happens here - BrowserChangeObserver is used to obtain a pointer
+ // to the newly added browser.
Browser* incognito_browser = browser_change_observer.Wait();
EXPECT_TRUE(incognito_browser->profile()->IsIncognitoProfile());
+
+ histogram_tester.ExpectUniqueSample(
+ "Signin.ProcessMirrorHeaders.AllowedFromInitiator.GoIncognito", true, 1);
+}
+
+// When receiving "INCOGNITO" from Gaia and the request is initiator is unknown
+// - an incognito tab should not be opened.
+IN_PROC_BROWSER_TEST_F(MirrorResponseBrowserTest,
+ IncognitoFromEmptyInitiatorIgnored) {
+ base::HistogramTester histogram_tester;
+ size_t browser_count = chrome::GetTotalBrowserCount();
+
+ NavigateToURL(GetUrlWithManageAccountsHeader({{"action", "INCOGNITO"}}),
+ absl::nullopt);
+
+ // Incognito window should not have been displayed, the browser count
+ // stays the same.
+ EXPECT_EQ(chrome::GetTotalBrowserCount(), browser_count);
+
+ histogram_tester.ExpectUniqueSample(
+ "Signin.ProcessMirrorHeaders.AllowedFromInitiator.GoIncognito", false, 1);
+}
+
+// When receiving "INCOGNITO" from Gaia and the request initiator is not a
+// Google domain - an incognito tab should not be opened.
+IN_PROC_BROWSER_TEST_F(MirrorResponseBrowserTest,
+ IncognitoFromNonGoogleInitiatorIgnored) {
+ base::HistogramTester histogram_tester;
+ size_t browser_count = chrome::GetTotalBrowserCount();
+
+ NavigateToURL(GetUrlWithManageAccountsHeader({{"action", "INCOGNITO"}}),
+ url::Origin::Create(GURL("https://example.com")));
+
+ // Incognito window should not have been displayed, the browser count
+ // stays the same.
+ EXPECT_EQ(chrome::GetTotalBrowserCount(), browser_count);
+
+ histogram_tester.ExpectUniqueSample(
+ "Signin.ProcessMirrorHeaders.AllowedFromInitiator.GoIncognito", false, 1);
}
-// Tests that the response is coming from a background browser is ignored.
+// When receiving "INCOGNITO" from Gaia in a background browser - an incognito
+// tab should not be opened.
IN_PROC_BROWSER_TEST_F(MirrorResponseBrowserTest, BackgroundResponseIgnored) {
// Minimize the browser window to disactivate it.
browser()->window()->Minimize();
@@ -158,8 +223,10 @@ IN_PROC_BROWSER_TEST_F(MirrorResponseBrowserTest, BackgroundResponseIgnored) {
size_t browser_count = chrome::GetTotalBrowserCount();
GURL url = GetUrlWithManageAccountsHeader({{"action", "INCOGNITO"}});
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_FROM_API);
+ params.initiator_origin = url::Origin::Create(GURL("https://google.com"));
// Use `NEW_BACKGROUND_TAB` to avoid activating `browser()`.
params.disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
+ params.is_renderer_initiated = true;
Navigate(&params);
EXPECT_TRUE(content::WaitForLoadStop(params.navigated_or_inserted_contents));
diff --git a/chromium/chrome/browser/signin/primary_account_policy_manager.cc b/chromium/chrome/browser/signin/primary_account_policy_manager.cc
index e8d44221bf1..64370293de9 100644
--- a/chromium/chrome/browser/signin/primary_account_policy_manager.cc
+++ b/chromium/chrome/browser/signin/primary_account_policy_manager.cc
@@ -236,11 +236,11 @@ void PrimaryAccountPolicyManager::EnsurePrimaryAccountAllowedForProfile(
#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Disabling signin in chrome and 'RestrictSigninToPattern' policy
- // are not supported on Lacros. This code should be unreachable. The main
- // profile should never be deleted.
- DCHECK(false)
- << "Disabling signin in chrome and 'RestrictSigninToPattern' policy "
- "are not supported on Lacros.";
+ // are not supported on Lacros. This code should be unreachable, except in
+ // Guest sessions. The main profile should never be deleted.
+ DCHECK(!profile->GetPrefs()->GetBoolean(prefs::kSigninAllowed) &&
+ profile->IsGuestSession())
+ << "On Lacros, signin may only be disallowed in the guest session.";
#else
if (ChromeSigninClientFactory::GetForProfile(profile)
->IsClearPrimaryAccountAllowed(identity_manager->HasPrimaryAccount(
diff --git a/chromium/chrome/browser/signin/primary_account_policy_manager_factory.cc b/chromium/chrome/browser/signin/primary_account_policy_manager_factory.cc
index 9b734daef59..0194437fa1a 100644
--- a/chromium/chrome/browser/signin/primary_account_policy_manager_factory.cc
+++ b/chromium/chrome/browser/signin/primary_account_policy_manager_factory.cc
@@ -32,8 +32,9 @@ PrimaryAccountPolicyManagerFactory::PrimaryAccountPolicyManagerFactory()
PrimaryAccountPolicyManagerFactory::~PrimaryAccountPolicyManagerFactory() =
default;
-KeyedService* PrimaryAccountPolicyManagerFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+PrimaryAccountPolicyManagerFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
- return new PrimaryAccountPolicyManager(profile);
+ return std::make_unique<PrimaryAccountPolicyManager>(profile);
}
diff --git a/chromium/chrome/browser/signin/primary_account_policy_manager_factory.h b/chromium/chrome/browser/signin/primary_account_policy_manager_factory.h
index 308bf218297..8be60b8d4b3 100644
--- a/chromium/chrome/browser/signin/primary_account_policy_manager_factory.h
+++ b/chromium/chrome/browser/signin/primary_account_policy_manager_factory.h
@@ -24,7 +24,7 @@ class PrimaryAccountPolicyManagerFactory : public ProfileKeyedServiceFactory {
~PrimaryAccountPolicyManagerFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};
diff --git a/chromium/chrome/browser/signin/process_dice_header_delegate_impl.cc b/chromium/chrome/browser/signin/process_dice_header_delegate_impl.cc
index 66daac78841..c3fdaab4c8b 100644
--- a/chromium/chrome/browser/signin/process_dice_header_delegate_impl.cc
+++ b/chromium/chrome/browser/signin/process_dice_header_delegate_impl.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "chrome/browser/profiles/profile.h"
@@ -14,7 +15,6 @@
#include "chrome/browser/signin/dice_web_signin_interceptor_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/webui/signin/signin_ui_error.h"
-#include "chrome/common/url_constants.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents.h"
@@ -22,13 +22,6 @@
namespace {
-void RedirectToNtp(content::WebContents* contents) {
- VLOG(1) << "RedirectToNtp";
- contents->GetController().LoadURL(
- GURL(chrome::kChromeUINewTabURL), content::Referrer(),
- ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
-}
-
// Helper function similar to DiceTabHelper::FromWebContents(), but also handles
// the case where |contents| is nullptr.
DiceTabHelper* GetDiceTabHelperFromWebContents(content::WebContents* contents) {
@@ -42,10 +35,7 @@ DiceTabHelper* GetDiceTabHelperFromWebContents(content::WebContents* contents) {
// static
std::unique_ptr<ProcessDiceHeaderDelegateImpl>
-ProcessDiceHeaderDelegateImpl::Create(
- content::WebContents* web_contents,
- EnableSyncCallback enable_sync_callback,
- ShowSigninErrorCallback show_signin_error_callback) {
+ProcessDiceHeaderDelegateImpl::Create(content::WebContents* web_contents) {
bool is_sync_signin_tab = false;
signin_metrics::AccessPoint access_point =
signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN;
@@ -53,6 +43,8 @@ ProcessDiceHeaderDelegateImpl::Create(
signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO;
signin_metrics::Reason reason = signin_metrics::Reason::kUnknownReason;
GURL redirect_url;
+ EnableSyncCallback enable_sync_callback;
+ ShowSigninErrorCallback show_signin_error_callback;
DiceTabHelper* tab_helper = DiceTabHelper::FromWebContents(web_contents);
if (tab_helper) {
@@ -61,9 +53,24 @@ ProcessDiceHeaderDelegateImpl::Create(
access_point = tab_helper->signin_access_point();
promo_action = tab_helper->signin_promo_action();
reason = tab_helper->signin_reason();
+ // `show_signin_error_callback` may be null if the `DiceTabHelper` was reset
+ // after completion of a signin flow.
+ show_signin_error_callback =
+ std::move(tab_helper->GetShowSigninErrorCallback());
+ if (is_sync_signin_tab) {
+ enable_sync_callback = tab_helper->GetEnableSyncCallback();
+ }
} else {
access_point = signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN;
}
+
+ // If there is no active `DiceTabHelper`, default to the in-browser error
+ // callback. This callback does nothing if there is no browser open.
+ if (!show_signin_error_callback) {
+ show_signin_error_callback =
+ DiceTabHelper::GetShowSigninErrorCallbackForBrowser();
+ }
+
return std::make_unique<ProcessDiceHeaderDelegateImpl>(
web_contents, is_sync_signin_tab, access_point, promo_action, reason,
std::move(redirect_url), std::move(enable_sync_callback),
@@ -88,7 +95,10 @@ ProcessDiceHeaderDelegateImpl::ProcessDiceHeaderDelegateImpl(
reason_(reason),
redirect_url_(std::move(redirect_url)),
enable_sync_callback_(std::move(enable_sync_callback)),
- show_signin_error_callback_(std::move(show_signin_error_callback)) {}
+ show_signin_error_callback_(std::move(show_signin_error_callback)) {
+ DCHECK_EQ(!is_sync_signin_tab_, enable_sync_callback_.is_null());
+ DCHECK(show_signin_error_callback_);
+}
ProcessDiceHeaderDelegateImpl::~ProcessDiceHeaderDelegateImpl() = default;
@@ -105,6 +115,12 @@ bool ProcessDiceHeaderDelegateImpl::ShouldEnableSync() {
return false;
}
+ if (!enable_sync_callback_) {
+ VLOG(1)
+ << "Do not start sync after web sign-in [no sync flow in progress].";
+ return false;
+ }
+
return true;
}
@@ -122,8 +138,8 @@ void ProcessDiceHeaderDelegateImpl::HandleTokenExchangeSuccess(
void ProcessDiceHeaderDelegateImpl::EnableSync(
const CoreAccountId& account_id) {
- DiceTabHelper* tab_helper =
- GetDiceTabHelperFromWebContents(web_contents_.get());
+ content::WebContents* web_contents = web_contents_.get();
+ DiceTabHelper* tab_helper = GetDiceTabHelperFromWebContents(web_contents);
if (tab_helper) {
tab_helper->OnSyncSigninFlowComplete();
}
@@ -133,44 +149,26 @@ void ProcessDiceHeaderDelegateImpl::EnableSync(
return;
}
- content::WebContents* web_contents = web_contents_.get();
VLOG(1) << "Start sync after web sign-in.";
std::move(enable_sync_callback_)
.Run(&profile_.get(), access_point_, promo_action_, reason_, web_contents,
account_id);
- if (!web_contents) {
- return;
- }
-
- // After signing in to Chrome, the user should be redirected to the NTP,
- // unless specified otherwise.
- if (redirect_url_.is_empty()) {
- RedirectToNtp(web_contents);
- return;
- }
-
- DCHECK(redirect_url_.is_valid());
- web_contents->GetController().LoadURL(redirect_url_, content::Referrer(),
- ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
- std::string());
+ Redirect();
}
void ProcessDiceHeaderDelegateImpl::HandleTokenExchangeFailure(
const std::string& email,
const GoogleServiceAuthError& error) {
DCHECK_NE(GoogleServiceAuthError::NONE, error.state());
- DiceTabHelper* tab_helper =
- GetDiceTabHelperFromWebContents(web_contents_.get());
+ content::WebContents* web_contents = web_contents_.get();
+ DiceTabHelper* tab_helper = GetDiceTabHelperFromWebContents(web_contents);
if (tab_helper) {
tab_helper->OnSyncSigninFlowComplete();
}
- bool should_enable_sync = ShouldEnableSync();
-
- content::WebContents* web_contents = web_contents_.get();
- if (should_enable_sync && web_contents) {
- RedirectToNtp(web_contents);
+ if (ShouldEnableSync()) {
+ Redirect();
}
// Show the error even if the WebContents was closed, because the user may be
@@ -183,3 +181,15 @@ void ProcessDiceHeaderDelegateImpl::HandleTokenExchangeFailure(
signin_metrics::AccessPoint ProcessDiceHeaderDelegateImpl::GetAccessPoint() {
return access_point_;
}
+
+void ProcessDiceHeaderDelegateImpl::Redirect() {
+ content::WebContents* web_contents = web_contents_.get();
+ if (!web_contents || redirect_url_.is_empty()) {
+ return;
+ }
+
+ DCHECK(redirect_url_.is_valid()) << "Invalid redirect url: " << redirect_url_;
+ web_contents->GetController().LoadURL(redirect_url_, content::Referrer(),
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
+ std::string());
+}
diff --git a/chromium/chrome/browser/signin/process_dice_header_delegate_impl.h b/chromium/chrome/browser/signin/process_dice_header_delegate_impl.h
index e5bf924c0a8..5c70870c13f 100644
--- a/chromium/chrome/browser/signin/process_dice_header_delegate_impl.h
+++ b/chromium/chrome/browser/signin/process_dice_header_delegate_impl.h
@@ -9,7 +9,7 @@
#include <string>
#include "base/functional/callback_forward.h"
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/signin/dice_response_handler.h"
#include "components/signin/public/base/signin_metrics.h"
@@ -24,6 +24,8 @@ class SigninUIError;
class ProcessDiceHeaderDelegateImpl : public ProcessDiceHeaderDelegate {
public:
// Callback starting Sync.
+ // This is similar to `DiceTabHelper::EnableSyncCallback` but is a once
+ // callback (vs repeating).
using EnableSyncCallback =
base::OnceCallback<void(Profile*,
signin_metrics::AccessPoint,
@@ -33,15 +35,15 @@ class ProcessDiceHeaderDelegateImpl : public ProcessDiceHeaderDelegate {
const CoreAccountId&)>;
// Callback showing a signin error UI.
+ // This is similar to `DiceTabHelper::ShowSigninErrorCallback` but is a once
+ // callback (vs repeating).
using ShowSigninErrorCallback = base::OnceCallback<
void(Profile*, content::WebContents*, const SigninUIError&)>;
// Helper function for creating `ProcessDiceHeaderDelegateImpl` from a
// `content::WebContents`.
static std::unique_ptr<ProcessDiceHeaderDelegateImpl> Create(
- content::WebContents* web_contents,
- EnableSyncCallback enable_sync_callback,
- ShowSigninErrorCallback show_signin_error_callback);
+ content::WebContents* web_contents);
// |is_sync_signin_tab| is true if a sync signin flow has been started in that
// tab.
@@ -73,6 +75,9 @@ class ProcessDiceHeaderDelegateImpl : public ProcessDiceHeaderDelegate {
// Returns true if sync should be enabled after the user signs in.
bool ShouldEnableSync();
+ // Navigates to `redirect_url_`. Does nothing if the url is empty.
+ void Redirect();
+
const base::WeakPtr<content::WebContents> web_contents_;
const raw_ref<Profile> profile_;
const bool is_sync_signin_tab_;
diff --git a/chromium/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc b/chromium/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc
index 4ff2de34850..22443bddabf 100644
--- a/chromium/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc
+++ b/chromium/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc
@@ -124,10 +124,12 @@ class ProcessDiceHeaderDelegateImplTest
std::unique_ptr<ProcessDiceHeaderDelegateImpl>
CreateDelegateAndNavigateToSignin(
bool is_sync_signin_tab,
+ const GURL& redirect_url,
Reason reason = Reason::kSigninPrimaryAccount) {
signin_reason_ = reason;
- if (!identity_test_environment_profile_adaptor_)
+ if (!identity_test_environment_profile_adaptor_) {
InitializeIdentityTestEnvironment();
+ }
// Load the signin page.
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(signin_url_,
@@ -137,19 +139,34 @@ class ProcessDiceHeaderDelegateImplTest
DiceTabHelper::CreateForWebContents(web_contents());
DiceTabHelper* dice_tab_helper =
DiceTabHelper::FromWebContents(web_contents());
- dice_tab_helper->InitializeSigninFlow(signin_url_, kTestAccessPoint,
- signin_reason_, kTestPromoAction,
- GURL::EmptyGURL());
+ dice_tab_helper->InitializeSigninFlow(
+ signin_url_, kTestAccessPoint, signin_reason_, kTestPromoAction,
+ redirect_url,
+ /*record_signin_started_metrics=*/true,
+ base::BindRepeating(
+ &ProcessDiceHeaderDelegateImplTest::StartSyncCallback,
+ base::Unretained(this)),
+ base::BindRepeating(
+ &ProcessDiceHeaderDelegateImplTest::ShowSigninErrorCallback,
+ base::Unretained(this)));
}
simulator->Commit();
DCHECK_EQ(signin_url_, web_contents()->GetVisibleURL());
- return ProcessDiceHeaderDelegateImpl::Create(
- web_contents(),
- base::BindOnce(&ProcessDiceHeaderDelegateImplTest::StartSyncCallback,
- base::Unretained(this)),
- base::BindOnce(
- &ProcessDiceHeaderDelegateImplTest::ShowSigninErrorCallback,
- base::Unretained(this)));
+
+ if (is_sync_signin_tab) {
+ return ProcessDiceHeaderDelegateImpl::Create(web_contents());
+ } else {
+ // Use the constructor rather than the `Create()` method, to specify the
+ // error callback.
+ return std::make_unique<ProcessDiceHeaderDelegateImpl>(
+ web_contents(), /*is_sync_signin_tab=*/false,
+ signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
+ kTestPromoAction, signin_metrics::Reason::kUnknownReason, GURL(),
+ ProcessDiceHeaderDelegateImpl::EnableSyncCallback(),
+ base::BindOnce(
+ &ProcessDiceHeaderDelegateImplTest::ShowSigninErrorCallback,
+ base::Unretained(this)));
+ }
}
// ChromeRenderViewHostTestHarness:
@@ -214,7 +231,8 @@ class ProcessDiceHeaderDelegateImplTest
// Check that sync is enabled if the tab is closed during signin.
TEST_F(ProcessDiceHeaderDelegateImplTest, CloseTabWhileStartingSync) {
std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
- CreateDelegateAndNavigateToSignin(true);
+ CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/true,
+ /*redirect_url=*/GURL());
// Close the tab.
DeleteContents();
@@ -229,7 +247,8 @@ TEST_F(ProcessDiceHeaderDelegateImplTest, CloseTabWhileStartingSync) {
// received.
TEST_F(ProcessDiceHeaderDelegateImplTest, CloseTabWhileFailingSignin) {
std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
- CreateDelegateAndNavigateToSignin(true);
+ CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/true,
+ /*redirect_url=*/GURL());
// Close the tab.
DeleteContents();
@@ -240,6 +259,43 @@ TEST_F(ProcessDiceHeaderDelegateImplTest, CloseTabWhileFailingSignin) {
EXPECT_TRUE(show_error_called_);
}
+// Tests that there is no redirect when `redirect_url` is empty.
+TEST_F(ProcessDiceHeaderDelegateImplTest, NoRedirect) {
+ std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
+ CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/true,
+ /*redirect_url=*/GURL());
+ delegate->EnableSync(account_id_);
+ EXPECT_TRUE(enable_sync_called_);
+ // There was no redirect.
+ EXPECT_EQ(signin_url_, web_contents()->GetVisibleURL());
+ EXPECT_FALSE(show_error_called_);
+ // Check that the sync signin flow is complete.
+ DiceTabHelper* dice_tab_helper =
+ DiceTabHelper::FromWebContents(web_contents());
+ ASSERT_TRUE(dice_tab_helper);
+ EXPECT_FALSE(dice_tab_helper->IsSyncSigninInProgress());
+}
+
+// Check that a Dice header can still be processed in a reused tab.
+// Regression test for https://crbug.com/1471277
+TEST_F(ProcessDiceHeaderDelegateImplTest, TabReuse) {
+ // Complete a first signin flow.
+ std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
+ CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/true,
+ /*redirect_url=*/GURL());
+ delegate->EnableSync(account_id_);
+ EXPECT_TRUE(enable_sync_called_);
+ EXPECT_FALSE(show_error_called_);
+
+ // Receive another Dice header in the same tab.
+ enable_sync_called_ = false;
+ ProcessDiceHeaderDelegateImpl::Create(web_contents());
+ // Calling `EnableSync()` does nothing because the tab has already been used.
+ delegate->EnableSync(account_id_);
+ EXPECT_FALSE(enable_sync_called_);
+ EXPECT_FALSE(show_error_called_);
+}
+
struct TestConfiguration {
// Test setup.
bool signed_in; // User was already signed in at the start of the flow.
@@ -267,14 +323,16 @@ class ProcessDiceHeaderDelegateImplTestEnableSync
// Test the EnableSync() method in all configurations.
TEST_P(ProcessDiceHeaderDelegateImplTestEnableSync, EnableSync) {
- if (GetParam().signed_in)
+ if (GetParam().signed_in) {
AddAccount(/*is_primary=*/true);
+ }
+ const GURL kNtpUrl(chrome::kChromeUINewTabURL);
std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
- CreateDelegateAndNavigateToSignin(GetParam().signin_tab);
+ CreateDelegateAndNavigateToSignin(GetParam().signin_tab,
+ /*redirect_url=*/kNtpUrl);
delegate->EnableSync(account_id_);
EXPECT_EQ(GetParam().callback_called, enable_sync_called_);
- GURL expected_url =
- GetParam().show_ntp ? GURL(chrome::kChromeUINewTabURL) : signin_url_;
+ GURL expected_url = GetParam().show_ntp ? kNtpUrl : signin_url_;
EXPECT_EQ(expected_url, web_contents()->GetVisibleURL());
EXPECT_FALSE(show_error_called_);
// Check that the sync signin flow is complete.
@@ -308,15 +366,17 @@ class ProcessDiceHeaderDelegateImplTestHandleTokenExchangeFailure
// Test the HandleTokenExchangeFailure() method in all configurations.
TEST_P(ProcessDiceHeaderDelegateImplTestHandleTokenExchangeFailure,
HandleTokenExchangeFailure) {
- if (GetParam().signed_in)
+ if (GetParam().signed_in) {
AddAccount(/*is_primary=*/true);
+ }
+ const GURL kNtpUrl(chrome::kChromeUINewTabURL);
std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
- CreateDelegateAndNavigateToSignin(GetParam().signin_tab);
+ CreateDelegateAndNavigateToSignin(GetParam().signin_tab,
+ /*redirect_url=*/kNtpUrl);
delegate->HandleTokenExchangeFailure(email_, auth_error_);
EXPECT_FALSE(enable_sync_called_);
EXPECT_EQ(GetParam().callback_called, show_error_called_);
- GURL expected_url =
- GetParam().show_ntp ? GURL(chrome::kChromeUINewTabURL) : signin_url_;
+ GURL expected_url = GetParam().show_ntp ? kNtpUrl : signin_url_;
EXPECT_EQ(expected_url, web_contents()->GetVisibleURL());
// Check that the sync signin flow is complete.
if (GetParam().signin_tab) {
@@ -359,11 +419,13 @@ class ProcessDiceHeaderDelegateImplTestHandleTokenExchangeSuccess
// Test the HandleTokenExchangeSuccess() method in all configurations.
TEST_P(ProcessDiceHeaderDelegateImplTestHandleTokenExchangeSuccess,
HandleTokenExchangeSuccess) {
- if (GetParam().is_reauth)
+ if (GetParam().is_reauth) {
AddAccount(/*is_primary=*/false);
+ }
std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
- CreateDelegateAndNavigateToSignin(GetParam().signin_tab,
- GetParam().reason);
+ CreateDelegateAndNavigateToSignin(
+ GetParam().signin_tab,
+ /*redirect_url=*/GURL(chrome::kChromeUINewTabURL), GetParam().reason);
EXPECT_CALL(
*mock_interceptor(),
MaybeInterceptWebSignin(web_contents(), account_id_,
diff --git a/chromium/chrome/browser/signin/profile_token_web_signin_interceptor.cc b/chromium/chrome/browser/signin/profile_token_web_signin_interceptor.cc
index 0ee9f659e64..9dbd96618d0 100644
--- a/chromium/chrome/browser/signin/profile_token_web_signin_interceptor.cc
+++ b/chromium/chrome/browser/signin/profile_token_web_signin_interceptor.cc
@@ -23,11 +23,13 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "chrome/common/themes/autogenerated_theme_util.h"
#include "chrome/common/webui_url_constants.h"
#include "content/public/browser/web_contents.h"
#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/mojom/themes.mojom.h"
+#include "ui/base/ui_base_features.h"
ProfileTokenWebSigninInterceptor::ProfileTokenWebSigninInterceptor(
Profile* profile,
@@ -139,14 +141,14 @@ void ProfileTokenWebSigninInterceptor::OnProfileCreationChoice(
if (switch_to_entry_) {
// Unretained is fine because the profile creator is owned by this.
profile_creator_ = std::make_unique<TokenManagedProfileCreator>(
- switch_to_entry_->GetPath(),
+ profile_, switch_to_entry_->GetPath(),
base::BindOnce(
&ProfileTokenWebSigninInterceptor::OnNewSignedInProfileCreated,
base::Unretained(this)));
} else {
// Unretained is fine because the profile creator is owned by this.
profile_creator_ = std::make_unique<TokenManagedProfileCreator>(
- intercepted_id_, enrollment_token_,
+ profile_, intercepted_id_, enrollment_token_,
profiles::GetDefaultNameForNewEnterpriseProfile(),
base::BindOnce(
&ProfileTokenWebSigninInterceptor::OnNewSignedInProfileCreated,
@@ -155,7 +157,7 @@ void ProfileTokenWebSigninInterceptor::OnProfileCreationChoice(
}
void ProfileTokenWebSigninInterceptor::OnNewSignedInProfileCreated(
- Profile* new_profile) {
+ base::WeakPtr<Profile> new_profile) {
DCHECK(profile_creator_);
if (!new_profile) {
@@ -167,8 +169,14 @@ void ProfileTokenWebSigninInterceptor::OnNewSignedInProfileCreated(
// Generate a color theme for new profiles
if (!switch_to_entry_) {
DCHECK_NE(SkColor(), profile_color_);
- ThemeServiceFactory::GetForProfile(new_profile)
- ->BuildAutogeneratedThemeFromColor(profile_color_);
+ if (features::IsChromeWebuiRefresh2023()) {
+ ThemeServiceFactory::GetForProfile(new_profile.get())
+ ->SetUserColorAndBrowserColorVariant(
+ profile_color_, ui::mojom::BrowserColorVariant::kTonalSpot);
+ } else {
+ ThemeServiceFactory::GetForProfile(new_profile.get())
+ ->BuildAutogeneratedThemeFromColor(profile_color_);
+ }
} else {
DVLOG(1) << "Profile switched sucessfully";
}
@@ -177,7 +185,7 @@ void ProfileTokenWebSigninInterceptor::OnNewSignedInProfileCreated(
// Work is done in this profile, the flow continues in the
// ProfileTokenWebSigninInterceptor that is attached to the new profile.
// We pass relevant parameters from this instance to the new one.
- ProfileTokenWebSigninInterceptorFactory::GetForProfile(new_profile)
+ ProfileTokenWebSigninInterceptorFactory::GetForProfile(new_profile.get())
->CreateBrowserAfterSigninInterception(web_contents_.get());
}
diff --git a/chromium/chrome/browser/signin/profile_token_web_signin_interceptor.h b/chromium/chrome/browser/signin/profile_token_web_signin_interceptor.h
index 0d2a9d6edc0..f416576dfe1 100644
--- a/chromium/chrome/browser/signin/profile_token_web_signin_interceptor.h
+++ b/chromium/chrome/browser/signin/profile_token_web_signin_interceptor.h
@@ -78,7 +78,7 @@ class ProfileTokenWebSigninInterceptor : public WebSigninInterceptor,
// callback to `session_startup_helper_`.
void OnNewBrowserCreated(bool is_new_profile);
- void OnNewSignedInProfileCreated(Profile* new_profile);
+ void OnNewSignedInProfileCreated(base::WeakPtr<Profile> new_profile);
const raw_ptr<Profile, DanglingUntriaged> profile_;
std::unique_ptr<Delegate> delegate_;
diff --git a/chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.cc b/chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.cc
index d72d6595c74..2c3fafa3290 100644
--- a/chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.cc
+++ b/chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.cc
@@ -29,9 +29,10 @@ ProfileTokenWebSigninInterceptorFactory::
ProfileTokenWebSigninInterceptorFactory::
~ProfileTokenWebSigninInterceptorFactory() = default;
-KeyedService* ProfileTokenWebSigninInterceptorFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+ProfileTokenWebSigninInterceptorFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
- return new ProfileTokenWebSigninInterceptor(
+ return std::make_unique<ProfileTokenWebSigninInterceptor>(
Profile::FromBrowserContext(context),
std::make_unique<DiceWebSigninInterceptorDelegate>());
}
diff --git a/chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.h b/chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.h
index dd94034f87f..d2b338d93af 100644
--- a/chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.h
+++ b/chromium/chrome/browser/signin/profile_token_web_signin_interceptor_factory.h
@@ -28,7 +28,7 @@ class ProfileTokenWebSigninInterceptorFactory
~ProfileTokenWebSigninInterceptorFactory() override;
// ProfileKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/signin/services/android/BUILD.gn b/chromium/chrome/browser/signin/services/android/BUILD.gn
index 5602dfb27cd..ac8bb7072da 100644
--- a/chromium/chrome/browser/signin/services/android/BUILD.gn
+++ b/chromium/chrome/browser/signin/services/android/BUILD.gn
@@ -68,6 +68,7 @@ android_library("unit_device_javatests") {
deps = [
":java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_app_java_resources",
"//chrome/browser/flags:java",
"//chrome/browser/profiles/android:java",
@@ -95,6 +96,7 @@ robolectric_library("junit") {
":java_resources",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_app_java_resources",
"//chrome/browser/flags:java",
"//chrome/browser/profiles/android:java",
diff --git a/chromium/chrome/browser/signin/signin_browser_test_base.h b/chromium/chrome/browser/signin/signin_browser_test_base.h
index 9c05699c5d6..56a110c01cc 100644
--- a/chromium/chrome/browser/signin/signin_browser_test_base.h
+++ b/chromium/chrome/browser/signin/signin_browser_test_base.h
@@ -15,6 +15,7 @@
#include "chrome/test/base/in_process_browser_test.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "components/trusted_vault/trusted_vault_histograms.h"
#include "services/network/test/test_url_loader_factory.h"
// Template for adding account management utilities to any test fixture which is
@@ -43,7 +44,17 @@ class SigninBrowserTestBaseT : public T {
// Returns `AccountInfo`s for each added account, in the same order as
// `emails`.
std::vector<AccountInfo> SetAccounts(const std::vector<std::string>& emails) {
- return identity_test_env()->MakeAccountsAvailableWithCookies(emails);
+ auto account_availability_options =
+ identity_test_env()
+ ->CreateAccountAvailabilityOptionsBuilder()
+ .WithCookie();
+
+ std::vector<AccountInfo> accounts_info;
+ for (const auto& email : emails) {
+ accounts_info.push_back(identity_test_env()->MakeAccountAvailable(
+ account_availability_options.Build(email)));
+ }
+ return accounts_info;
}
// Returns the profile attached to the `signin::IdentityTestEnvironment`. This
@@ -79,6 +90,10 @@ class SigninBrowserTestBaseT : public T {
DCHECK_EQ(GetProfile()->IsMainProfile(), use_main_profile_);
#endif
+ if (GetProfile()->IsOffTheRecord()) {
+ return;
+ }
+
identity_test_env_profile_adaptor_ =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(GetProfile());
identity_test_env()->SetTestURLLoaderFactory(&test_url_loader_factory_);
@@ -103,18 +118,8 @@ class SigninBrowserTestBaseT : public T {
virtual void OnWillCreateBrowserContextServices(
content::BrowserContext* context) {
- signin::AccountConsistencyMethod account_consistency_method =
-#if BUILDFLAG(ENABLE_MIRROR)
- signin::AccountConsistencyMethod::kMirror;
-#elif BUILDFLAG(ENABLE_DICE_SUPPORT)
- signin::AccountConsistencyMethod::kDice;
-#else
- signin::AccountConsistencyMethod::kDisabled;
-#endif
-
IdentityTestEnvironmentProfileAdaptor::
- SetIdentityTestEnvironmentFactoriesOnBrowserContext(
- context, account_consistency_method);
+ SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
ChromeSigninClientFactory::GetInstance()->SetTestingFactory(
context, base::BindRepeating(&BuildChromeSigninClientWithURLLoader,
&test_url_loader_factory_));
diff --git a/chromium/chrome/browser/signin/signin_features.cc b/chromium/chrome/browser/signin/signin_features.cc
index ef7233b33fe..ed23c72ec79 100644
--- a/chromium/chrome/browser/signin/signin_features.cc
+++ b/chromium/chrome/browser/signin/signin_features.cc
@@ -38,6 +38,30 @@ const base::FeatureParam<SigninPromoVariant> kForYouFreSignInPromoVariant{
/*default_value=*/SigninPromoVariant::kSignIn,
/*options=*/&kSignInPromoVariantOptions};
+constexpr base::FeatureParam<WithDefaultBrowserStep>::Option
+ kWithDefaultBrowserStepOptions[] = {
+ {WithDefaultBrowserStep::kYes, "yes"},
+ {WithDefaultBrowserStep::kNo, "no"},
+ {WithDefaultBrowserStep::kForced, "forced"},
+};
+
+const base::FeatureParam<WithDefaultBrowserStep>
+ kForYouFreWithDefaultBrowserStep{
+ &kForYouFre, /*name=*/"with_default_browser_step",
+ /*default_value=*/WithDefaultBrowserStep::kNo,
+ /*options=*/&kWithDefaultBrowserStepOptions};
+
+constexpr base::FeatureParam<DefaultBrowserVariant>::Option
+ kDefaultBrowserVariantOptions[] = {
+ {DefaultBrowserVariant::kCurrent, "current"},
+ {DefaultBrowserVariant::kNew, "new"},
+};
+
+const base::FeatureParam<DefaultBrowserVariant> kForYouFreDefaultBrowserVariant{
+ &kForYouFre, /*name=*/"default_browser_variant",
+ /*default_value=*/DefaultBrowserVariant::kCurrent,
+ /*options=*/&kDefaultBrowserVariantOptions};
+
// Feature that indicates that we should put the client in a study group
// (provided through `kForYouFreStudyGroup`) to be able to look at metrics in
// the long term. Does not affect the client's behavior by itself, instead this
@@ -61,18 +85,6 @@ BASE_FEATURE(kProcessGaiaRemoveLocalAccountHeader,
"ProcessGaiaRemoveLocalAccountHeader",
base::FEATURE_ENABLED_BY_DEFAULT);
-// Enables the sync promo after the sign-in intercept. Fully rolled out on
-// Desktop.
-BASE_FEATURE(kSyncPromoAfterSigninIntercept,
- "SyncPromoAfterSigninIntercept",
- base::FEATURE_ENABLED_BY_DEFAULT);
-
-// Enables using new style (strings, illustration, and disclaimer if needed)
-// for the sign-in intercept bubble. Fully rolled out on Desktop.
-BASE_FEATURE(kSigninInterceptBubbleV2,
- "SigninInterceptBubbleV2",
- base::FEATURE_ENABLED_BY_DEFAULT);
-
// Enables showing the enterprise dialog after every signin into a managed
// account.
BASE_FEATURE(kShowEnterpriseDialogForAllManagedAccountsSignin,
@@ -82,8 +94,32 @@ BASE_FEATURE(kShowEnterpriseDialogForAllManagedAccountsSignin,
// Disables signout for enteprise managed profiles
BASE_FEATURE(kDisallowManagedProfileSignout,
"DisallowManagedProfileSignout",
+ base::FEATURE_ENABLED_BY_DEFAULT);
+
+#if BUILDFLAG(ENABLE_MIRROR)
+BASE_FEATURE(kVerifyRequestInitiatorForMirrorHeaders,
+ "VerifyRequestInitiatorForMirrorHeaders",
+ base::FEATURE_ENABLED_BY_DEFAULT);
+#endif // BUILDFLAG(ENABLE_MIRROR)
+
+BASE_FEATURE(kProfilesReordering,
+ "ProfilesReordering",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
+BASE_FEATURE(kForceSigninFlowInProfilePicker,
+ "ForceSigninFlowInProfilePicker",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
+BASE_FEATURE(kGaiaSigninUrlEmbedded,
+ "GaiaSigninUrlEmbedded",
base::FEATURE_DISABLED_BY_DEFAULT);
-#if BUILDFLAG(ENABLE_WAFFLE_DESKTOP)
-BASE_FEATURE(kWaffle, "Waffle", base::FEATURE_DISABLED_BY_DEFAULT);
-#endif // BUILDFLAG(ENABLE_WAFFLE_DESKTOP)
+// Complimentary switch to `switches::kEnableBoundSessionCredentials` to enable
+// the bound session credentials feature in DICE profiles. For this switch to
+// have an effect, `switches::kEnableBoundSessionCredentials` should also be
+// enabled.
+#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
+BASE_FEATURE(kEnableBoundSessionCredentialsOnDiceProfiles,
+ "EnableBoundSessionCredentialsOnDiceProfiles",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+#endif
diff --git a/chromium/chrome/browser/signin/signin_features.h b/chromium/chrome/browser/signin/signin_features.h
index 933ed24f901..d20788a7fbd 100644
--- a/chromium/chrome/browser/signin/signin_features.h
+++ b/chromium/chrome/browser/signin/signin_features.h
@@ -20,24 +20,53 @@ enum class SigninPromoVariant { kSignIn, kMakeYourOwn, kDoMore };
extern const base::FeatureParam<SigninPromoVariant>
kForYouFreSignInPromoVariant;
+enum class WithDefaultBrowserStep {
+ // The default browser step should be shown as appropriate.
+ kYes,
+ // The default browser step should be skipped.
+ kNo,
+ // The default browser step should be shown even if we normally should skip
+ // it, example because of policies or the current default state.
+ kForced,
+};
+extern const base::FeatureParam<WithDefaultBrowserStep>
+ kForYouFreWithDefaultBrowserStep;
+
+enum class DefaultBrowserVariant {
+ // Use the current strings for the default browser prompt.
+ kCurrent,
+ // Use the new strings for the default browser prompt.
+ kNew,
+};
+extern const base::FeatureParam<DefaultBrowserVariant>
+ kForYouFreDefaultBrowserVariant;
+
BASE_DECLARE_FEATURE(kForYouFreSyntheticTrialRegistration);
extern const base::FeatureParam<std::string> kForYouFreStudyGroup;
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
#endif // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID)
-#if BUILDFLAG(ENABLE_WAFFLE_DESKTOP)
-BASE_DECLARE_FEATURE(kWaffle);
-#endif // BUILDFLAG(ENABLE_WAFFLE_DESKTOP)
-
BASE_DECLARE_FEATURE(kProcessGaiaRemoveLocalAccountHeader);
-BASE_DECLARE_FEATURE(kSyncPromoAfterSigninIntercept);
-
-BASE_DECLARE_FEATURE(kSigninInterceptBubbleV2);
-
BASE_DECLARE_FEATURE(kShowEnterpriseDialogForAllManagedAccountsSignin);
BASE_DECLARE_FEATURE(kDisallowManagedProfileSignout);
+#if BUILDFLAG(ENABLE_MIRROR)
+BASE_DECLARE_FEATURE(kVerifyRequestInitiatorForMirrorHeaders);
+#endif // BUILDFLAG(ENABLE_MIRROR)
+
+BASE_DECLARE_FEATURE(kProfilesReordering);
+
+BASE_DECLARE_FEATURE(kForceSigninFlowInProfilePicker);
+
+// Used in Profile Picker signin in flow. Do not show outbound links that will
+// lead to opening a browser by displaying the embedded version of the page.
+BASE_DECLARE_FEATURE(kGaiaSigninUrlEmbedded);
+
+#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
+BASE_DECLARE_FEATURE(kEnableBoundSessionCredentialsOnDiceProfiles);
+#endif
+
#endif // CHROME_BROWSER_SIGNIN_SIGNIN_FEATURES_H_
diff --git a/chromium/chrome/browser/signin/signin_manager_android_factory.cc b/chromium/chrome/browser/signin/signin_manager_android_factory.cc
index 9a65a0e7c5f..b04f6c58e73 100644
--- a/chromium/chrome/browser/signin/signin_manager_android_factory.cc
+++ b/chromium/chrome/browser/signin/signin_manager_android_factory.cc
@@ -36,10 +36,11 @@ SigninManagerAndroidFactory* SigninManagerAndroidFactory::GetInstance() {
return instance.get();
}
-KeyedService* SigninManagerAndroidFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+SigninManagerAndroidFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
- return new SigninManagerAndroid(profile, identity_manager);
+ return std::make_unique<SigninManagerAndroid>(profile, identity_manager);
}
diff --git a/chromium/chrome/browser/signin/signin_manager_android_factory.h b/chromium/chrome/browser/signin/signin_manager_android_factory.h
index 8630ed94bc2..387f9f0f073 100644
--- a/chromium/chrome/browser/signin/signin_manager_android_factory.h
+++ b/chromium/chrome/browser/signin/signin_manager_android_factory.h
@@ -24,7 +24,7 @@ class SigninManagerAndroidFactory : public ProfileKeyedServiceFactory {
~SigninManagerAndroidFactory() override;
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};
diff --git a/chromium/chrome/browser/signin/signin_manager_unittest.cc b/chromium/chrome/browser/signin/signin_manager_unittest.cc
index 443f83c755a..ff7164e025c 100644
--- a/chromium/chrome/browser/signin/signin_manager_unittest.cc
+++ b/chromium/chrome/browser/signin/signin_manager_unittest.cc
@@ -73,7 +73,6 @@ class SigninManagerTest : public testing::Test,
: client_(&prefs_),
identity_test_env_(/*test_url_loader_factory=*/nullptr,
/*pref_service=*/&prefs_,
- signin::AccountConsistencyMethod::kDice,
&client_),
observer_(identity_test_env_.identity_manager()) {
RecreateSigninManager();
diff --git a/chromium/chrome/browser/signin/signin_profile_attributes_updater.h b/chromium/chrome/browser/signin/signin_profile_attributes_updater.h
index e8471a4689e..8586b9ccaa3 100644
--- a/chromium/chrome/browser/signin/signin_profile_attributes_updater.h
+++ b/chromium/chrome/browser/signin/signin_profile_attributes_updater.h
@@ -44,7 +44,10 @@ class SigninProfileAttributesUpdater
void OnPrimaryAccountChanged(
const signin::PrimaryAccountChangeEvent& event) override;
- raw_ptr<signin::IdentityManager> identity_manager_;
+ // This dangling raw_ptr occurred in:
+ // unit_tests: All/IsolatedWebAppReaderRegistryFactoryTest.GuardedBehindFeatureFlag/1
+ // https://ci.chromium.org/ui/p/chromium/builders/try/win-rel/237392/test-results?q=ExactID%3Aninja%3A%2F%2Fchrome%2Ftest%3Aunit_tests%2FIsolatedWebAppReaderRegistryFactoryTest.GuardedBehindFeatureFlag%2FAll.1+VHash%3Ad3d9eb66b4bd136c
+ raw_ptr<signin::IdentityManager, FlakyDanglingUntriaged> identity_manager_;
raw_ptr<ProfileAttributesStorage> profile_attributes_storage_;
const base::FilePath profile_path_;
raw_ptr<PrefService> prefs_;
diff --git a/chromium/chrome/browser/signin/signin_promo.cc b/chromium/chrome/browser/signin/signin_promo.cc
index dc9dceea885..cef3eb21b79 100644
--- a/chromium/chrome/browser/signin/signin_promo.cc
+++ b/chromium/chrome/browser/signin/signin_promo.cc
@@ -83,8 +83,16 @@ GURL GetChromeSyncURLForDice(ChromeSyncUrlArgs args) {
if (args.request_dark_scheme) {
url = net::AppendQueryParameter(url, "color_scheme", "dark");
}
- if (args.for_promo_flow) {
- url = net::AppendQueryParameter(url, "flow", "promo");
+ switch (args.flow) {
+ // Default behavior.
+ case Flow::NONE:
+ break;
+ case Flow::PROMO:
+ url = net::AppendQueryParameter(url, "flow", "promo");
+ break;
+ case Flow::EMBEDDED_PROMO:
+ url = net::AppendQueryParameter(url, "flow", "embedded_promo");
+ break;
}
return url;
}
diff --git a/chromium/chrome/browser/signin/signin_promo.h b/chromium/chrome/browser/signin/signin_promo.h
index 71d0e4e1f19..ad563a9c465 100644
--- a/chromium/chrome/browser/signin/signin_promo.h
+++ b/chromium/chrome/browser/signin/signin_promo.h
@@ -51,6 +51,21 @@ GURL GetEmbeddedReauthURLWithEmail(signin_metrics::AccessPoint access_point,
const std::string& email);
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
+// Controls the information displayed around the Gaia Sign In page via the
+// "flow" url parameter.
+enum class Flow {
+ // No value set for the "flow" parameter.
+ NONE,
+ // The "promo" flow indicates to the user that they are signing in to Chrome
+ // but unlike the default dice sign-in page, they don't mention sync
+ // benefits.
+ PROMO,
+ // The "embedded_promo" flow has the same effect as `PROMO` with the addition
+ // of providing a page with no outbound links, in order not to be able to open
+ // browser page during the signin flow.
+ EMBEDDED_PROMO
+};
+
// Wraps arguments for `GetChromeSyncURLForDice()`. They are all optional.
struct ChromeSyncUrlArgs {
// If not empty, will be passed as hint to the page so that it will be
@@ -60,10 +75,8 @@ struct ChromeSyncUrlArgs {
const std::string continue_url;
// If true, the dark mode version of the page will be requested.
bool request_dark_scheme = false;
- // The "promo" flow indicates to the user that they are signing in to Chrome
- // but unlike the default dice sign-in page, they don't mention sync
- // benefits.
- bool for_promo_flow = false;
+ // Sets the "flow" parameter in the gaia sign in url.
+ Flow flow = Flow::NONE;
};
// Returns the URL to be used to signin and turn on Sync when DICE is enabled.
diff --git a/chromium/chrome/browser/signin/signin_promo_unittest.cc b/chromium/chrome/browser/signin/signin_promo_unittest.cc
index 5441d881266..6df16def118 100644
--- a/chromium/chrome/browser/signin/signin_promo_unittest.cc
+++ b/chromium/chrome/browser/signin/signin_promo_unittest.cc
@@ -47,12 +47,16 @@ TEST(SigninPromoTest, SigninURLForDice) {
"https://accounts.google.com/signin/chrome/sync?ssp=1&"
"color_scheme=dark&flow=promo",
GetChromeSyncURLForDice(
- {.request_dark_scheme = true, .for_promo_flow = true}));
+ {.request_dark_scheme = true, .flow = signin::Flow::PROMO}));
EXPECT_EQ(
"https://accounts.google.com/signin/chrome/sync?ssp=1&"
"email_hint=email%40gmail.com&continue=https%3A%2F%2Fcontinue_url%2F",
GetChromeSyncURLForDice({"email@gmail.com", "https://continue_url/"}));
EXPECT_EQ(
+ "https://accounts.google.com/signin/chrome/"
+ "sync?ssp=1&flow=embedded_promo",
+ GetChromeSyncURLForDice({.flow = signin::Flow::EMBEDDED_PROMO}));
+ EXPECT_EQ(
"https://accounts.google.com/AddSession?"
"Email=email%40gmail.com&continue=https%3A%2F%2Fcontinue_url%2F",
GetAddAccountURLForDice("email@gmail.com", "https://continue_url/"));
diff --git a/chromium/chrome/browser/signin/signin_ui_delegate_impl_lacros.cc b/chromium/chrome/browser/signin/signin_ui_delegate_impl_lacros.cc
index 6e013b40459..4842e324444 100644
--- a/chromium/chrome/browser/signin/signin_ui_delegate_impl_lacros.cc
+++ b/chromium/chrome/browser/signin/signin_ui_delegate_impl_lacros.cc
@@ -46,6 +46,9 @@ GetAddAccountSourceFromAccessPoint(signin_metrics::AccessPoint access_point) {
case signin_metrics::AccessPoint::ACCESS_POINT_MENU:
return account_manager::AccountManagerFacade::AccountAdditionSource::
kChromeMenuTurnOnSync;
+ case signin_metrics::AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN:
+ return account_manager::AccountManagerFacade::AccountAdditionSource::
+ kChromeSigninPromoAddAccount;
default:
NOTREACHED() << "Add account is requested from an unknown access point "
<< static_cast<int>(access_point);
diff --git a/chromium/chrome/browser/signin/signin_ui_util.cc b/chromium/chrome/browser/signin/signin_ui_util.cc
index 62e6362f7e4..4aef5e1919f 100644
--- a/chromium/chrome/browser/signin/signin_ui_util.cc
+++ b/chromium/chrome/browser/signin/signin_ui_util.cc
@@ -269,6 +269,28 @@ void ShowExtensionSigninPrompt(Profile* profile,
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
+void ShowSigninPromptFromPromo(Profile* profile,
+ signin_metrics::AccessPoint access_point) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ NOTREACHED();
+#elif BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+ CHECK_NE(signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN, access_point);
+ CHECK(!profile->IsOffTheRecord());
+
+ signin::IdentityManager* identity_manager =
+ IdentityManagerFactory::GetForProfile(profile);
+ if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
+ DVLOG(1) << "The user is already signed in.";
+ return;
+ }
+
+ GetSigninUiDelegate()->ShowSigninUI(
+ profile, /*enable_sync=*/false, access_point,
+ signin_metrics::PromoAction::
+ PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+}
+
void EnableSyncFromSingleAccountPromo(
Profile* profile,
const CoreAccountInfo& account,
diff --git a/chromium/chrome/browser/signin/signin_ui_util.h b/chromium/chrome/browser/signin/signin_ui_util.h
index 7f5f42e328d..1e4f0202317 100644
--- a/chromium/chrome/browser/signin/signin_ui_util.h
+++ b/chromium/chrome/browser/signin/signin_ui_util.h
@@ -13,7 +13,7 @@
#include "build/buildflag.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/signin/reauth_result.h"
-#include "chrome/browser/ui/signin_reauth_view_controller.h"
+#include "chrome/browser/ui/signin/signin_reauth_view_controller.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/signin/public/base/signin_metrics.h"
@@ -52,6 +52,11 @@ void ShowExtensionSigninPrompt(Profile* profile,
bool enable_sync,
const std::string& email_hint);
+// This function is used to sign-in the user into Chrome without offering sync.
+// This function does nothing if the user is already signed in to Chrome.
+void ShowSigninPromptFromPromo(Profile* profile,
+ signin_metrics::AccessPoint access_point);
+
// This function is used to enable sync for a given account:
// * This function does nothing if the user is already signed in to Chrome.
// * If |account| is empty, then it presents the Chrome sign-in page.
diff --git a/chromium/chrome/browser/signin/signin_ui_util_unittest.cc b/chromium/chrome/browser/signin/signin_ui_util_unittest.cc
index d8d36d4170b..b0484c31e59 100644
--- a/chromium/chrome/browser/signin/signin_ui_util_unittest.cc
+++ b/chromium/chrome/browser/signin/signin_ui_util_unittest.cc
@@ -29,6 +29,7 @@
#include "components/google/core/common/google_util.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_buildflags.h"
+#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
@@ -573,6 +574,31 @@ TEST_F(SigninUiUtilTest, ShowExtensionSigninPrompt_AsLockedProfile) {
/*email_hint=*/std::string());
EXPECT_EQ(0, tab_strip->count());
}
+
+TEST_F(SigninUiUtilTest, ShowSigninPromptFromPromo) {
+ Profile* profile = browser()->profile();
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ ShowSigninPromptFromPromo(profile, access_point_);
+ EXPECT_EQ(1, tab_strip->count());
+
+ content::WebContents* tab = tab_strip->GetWebContentsAt(0);
+ ASSERT_TRUE(tab);
+ EXPECT_TRUE(
+ base::StartsWith(tab->GetVisibleURL().spec(),
+ GaiaUrls::GetInstance()->add_account_url().spec(),
+ base::CompareCase::INSENSITIVE_ASCII));
+}
+
+TEST_F(SigninUiUtilTest, ShowSigninPromptFromPromoWithExistingAccount) {
+ signin::MakePrimaryAccountAvailable(GetIdentityManager(), "foo@example.com",
+ signin::ConsentLevel::kSignin);
+
+ Profile* profile = browser()->profile();
+ TabStripModel* tab_strip = browser()->tab_strip_model();
+ EXPECT_EQ(0, tab_strip->count());
+ ShowSigninPromptFromPromo(profile, access_point_);
+ EXPECT_EQ(0, tab_strip->count());
+}
#endif // !BUILDFLAG(IS_CHROMEOS_LACROS)
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -748,6 +774,25 @@ TEST_F(MirrorSigninUiUtilTest,
ShowExtensionSigninPrompt(profile(), /*enable_sync=*/true, kMainEmail);
}
+TEST_F(MirrorSigninUiUtilTest, ShowSigninPromptFromPromo) {
+ signin_metrics::AccessPoint kAccessPoint =
+ signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN;
+ ExpectAddAccount(
+ /*enable_sync=*/false, kAccessPoint,
+ signin_metrics::PromoAction::
+ PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT);
+ ShowSigninPromptFromPromo(profile(), kAccessPoint);
+}
+
+TEST_F(MirrorSigninUiUtilTest, ShowSigninPromptFromPromoWithExistingAccount) {
+ signin::MakePrimaryAccountAvailable(
+ IdentityManagerFactory::GetForProfile(profile()), "foo@example.com",
+ signin::ConsentLevel::kSignin);
+ ShowSigninPromptFromPromo(
+ profile(),
+ signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
+}
+
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
// This test does not use the SigninUiUtilTest test fixture, because it
diff --git a/chromium/chrome/browser/signin/signin_util.cc b/chromium/chrome/browser/signin/signin_util.cc
index a647d4dfe74..4a88d9b7fb2 100644
--- a/chromium/chrome/browser/signin/signin_util.cc
+++ b/chromium/chrome/browser/signin/signin_util.cc
@@ -6,8 +6,11 @@
#include <memory>
+#include "base/barrier_closure.h"
+#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
@@ -15,6 +18,8 @@
#include "base/supports_user_data.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/enterprise/profile_management/profile_management_features.h"
+#include "chrome/browser/enterprise/util/managed_browser_utils.h"
#include "chrome/browser/policy/cloud/user_policy_signin_service_internal.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profiles_state.h"
@@ -22,8 +27,13 @@
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/google/core/common/google_util.h"
+#include "components/policy/core/browser/signin/profile_separation_policies.h"
#include "components/prefs/pref_service.h"
+#include "components/signin/public/base/signin_pref_names.h"
+#include "content/public/browser/storage_partition.h"
#include "google_apis/gaia/gaia_auth_util.h"
+#include "net/cookies/canonical_cookie.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "ui/base/l10n/l10n_util.h"
namespace signin_util {
@@ -49,6 +59,75 @@ ScopedForceSigninSetterForTesting::~ScopedForceSigninSetterForTesting() {
ResetForceSigninForTesting(); // IN-TEST
}
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+CookiesMover::CookiesMover(base::WeakPtr<Profile> source_profile,
+ base::WeakPtr<Profile> destination_profile,
+ base::OnceCallback<void()> callback)
+ : url_(source_profile->GetPrefs()->GetString(
+ prefs::kSigninInterceptionIDPCookiesUrl)),
+ source_profile_(std::move(source_profile)),
+ destination_profile_(std::move(destination_profile)),
+ callback_(std::move(callback)) {
+ CHECK(callback_);
+}
+
+CookiesMover::~CookiesMover() = default;
+
+void CookiesMover::StartMovingCookies() {
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
+ bool allow_cookies_to_be_moved = base::FeatureList::IsEnabled(
+ profile_management::features::kThirdPartyProfileManagement);
+#else
+ bool allow_cookies_to_be_moved = false;
+#endif
+ if (!allow_cookies_to_be_moved || url_.is_empty() || !url_.is_valid()) {
+ std::move(callback_).Run();
+ return;
+ }
+
+ source_profile_->GetPrefs()->ClearPref(
+ prefs::kSigninInterceptionIDPCookiesUrl);
+ source_profile_->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess()
+ ->GetCookieList(url_, net::CookieOptions::MakeAllInclusive(),
+ net::CookiePartitionKeyCollection::Todo(),
+ base::BindOnce(&CookiesMover::OnCookiesReceived,
+ weak_pointer_factory_.GetWeakPtr()));
+}
+
+void CookiesMover::OnCookiesReceived(
+ const std::vector<net::CookieWithAccessResult>& included,
+ const std::vector<net::CookieWithAccessResult>& excluded) {
+ // If either profile was destroyed, stop the operation.
+ if (source_profile_.WasInvalidated() ||
+ destination_profile_.WasInvalidated()) {
+ std::move(callback_).Run();
+ return;
+ }
+ // We expected 2 * `cookies.size()` actions since we have to set the cookie at
+ // destination and delete it from the source.
+ base::RepeatingClosure barrier = base::BarrierClosure(
+ included.size() * 2, base::BindOnce(&CookiesMover::OnCookiesMoved,
+ weak_pointer_factory_.GetWeakPtr()));
+ auto* source_cookie_manager = source_profile_->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess();
+ auto* destination_cookie_manager =
+ destination_profile_->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess();
+ for (const auto& [cookie, _] : included) {
+ destination_cookie_manager->SetCanonicalCookie(
+ cookie, url_, net::CookieOptions::MakeAllInclusive(),
+ base::IgnoreArgs<net::CookieAccessResult>(barrier));
+ source_cookie_manager->DeleteCanonicalCookie(
+ cookie, base::IgnoreArgs<bool>(barrier));
+ }
+}
+
+void CookiesMover::OnCookiesMoved() {
+ std::move(callback_).Run();
+}
+#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+
bool IsForceSigninEnabled() {
if (g_is_force_signin_enabled_cache == NOT_CACHED) {
PrefService* prefs = g_browser_process->local_state();
@@ -80,80 +159,58 @@ bool IsProfileDeletionAllowed(Profile* profile) {
#if !BUILDFLAG(IS_ANDROID)
#if !BUILDFLAG(IS_CHROMEOS)
-ProfileSeparationPolicyStateSet GetProfileSeparationPolicyState(
- Profile* profile,
- const absl::optional<std::string>& intercepted_account_level_policy_value) {
- ProfileSeparationPolicyStateSet result;
-
- std::string current_profile_account_restriction =
+// Returns true if managed accounts signin are required to create a new profile
+// by policies set in `profile`.
+bool IsProfileSeparationEnforcedByProfile(Profile* profile) {
+ std::string legacy_policy_for_current_profile =
profile->GetPrefs()->GetString(prefs::kManagedAccountsSigninRestriction);
-
- if (base::StartsWith(current_profile_account_restriction,
- "primary_account")) {
- result.Put(ProfileSeparationPolicyState::kEnforcedByExistingProfile);
-
- if (profile->GetPrefs()->GetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine)) {
- result.Put(ProfileSeparationPolicyState::kEnforcedOnMachineLevel);
- }
- }
- if (base::StartsWith(current_profile_account_restriction,
- "primary_account_strict")) {
- result.Put(ProfileSeparationPolicyState::kStrict);
- }
- if (base::StartsWith(
- intercepted_account_level_policy_value.value_or(std::string()),
- "primary_account")) {
- result.Put(ProfileSeparationPolicyState::kEnforcedByInterceptedAccount);
- }
-
- if (base::StartsWith(
- intercepted_account_level_policy_value.value_or(std::string()),
- "primary_account_strict")) {
- result.Put(ProfileSeparationPolicyState::kStrict);
- }
-
- if (result.Empty())
- return result;
-
- bool profile_allows_keeping_existing_browsing_data =
- !(result.Has(ProfileSeparationPolicyState::kEnforcedByExistingProfile)) ||
- base::EndsWith(current_profile_account_restriction, "keep_existing_data");
- bool account_allows_keeping_existing_browsing_data =
- !(result.Has(
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount)) ||
- base::EndsWith(intercepted_account_level_policy_value.value(),
- "keep_existing_data");
- // Keep Existing browsing data if both sources for the policy allow it.
- if (profile_allows_keeping_existing_browsing_data &&
- account_allows_keeping_existing_browsing_data) {
- result.Put(ProfileSeparationPolicyState::kKeepsBrowsingData);
- }
-
- return result;
+ bool enforced_by_existing_profile = base::StartsWith(
+ legacy_policy_for_current_profile, "primary_account_strict");
+ bool enforced_at_machine_level =
+ base::StartsWith(legacy_policy_for_current_profile, "primary_account") &&
+ profile->GetPrefs()->GetBoolean(
+ prefs::kManagedAccountsSigninRestrictionScopeMachine);
+ return enforced_by_existing_profile || enforced_at_machine_level;
}
-bool ProfileSeparationEnforcedByPolicy(
- Profile* profile,
- const absl::optional<std::string>& intercepted_account_level_policy_value) {
- auto separation_policy_state = GetProfileSeparationPolicyState(
- profile, intercepted_account_level_policy_value);
- return !base::Intersection(
- separation_policy_state,
- {ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel})
- .Empty();
+// Returns true if profile separation is enforced by
+// `intercepted_account_separation_policies`.
+bool IsProfileSeparationEnforcedByPolicies(
+ const policy::ProfileSeparationPolicies&
+ intercepted_account_separation_policies) {
+ std::string legacy_policy_for_intercepted_profile =
+ intercepted_account_separation_policies
+ .managed_accounts_signin_restrictions()
+ .value_or(std::string());
+ return base::StartsWith(legacy_policy_for_intercepted_profile,
+ "primary_account");
}
bool ProfileSeparationAllowsKeepingUnmanagedBrowsingDataInManagedProfile(
Profile* profile,
- const std::string& intercepted_account_level_policy_value) {
- auto profile_separation_state = GetProfileSeparationPolicyState(
- profile, intercepted_account_level_policy_value);
- return profile_separation_state.Empty() ||
- profile_separation_state.Has(
- ProfileSeparationPolicyState::kKeepsBrowsingData);
+ const policy::ProfileSeparationPolicies&
+ intercepted_account_separation_policies) {
+ // We should not move managed data.
+ if (chrome::enterprise_util::UserAcceptedAccountManagement(profile)) {
+ return false;
+ }
+
+ std::string legacy_policy_for_intercepted_profile =
+ intercepted_account_separation_policies
+ .managed_accounts_signin_restrictions()
+ .value_or(std::string());
+ std::string legacy_policy_for_current_profile =
+ profile->GetPrefs()->GetString(prefs::kManagedAccountsSigninRestriction);
+ bool allowed_by_existing_profile =
+ legacy_policy_for_current_profile.empty() ||
+ legacy_policy_for_current_profile == "none" ||
+ base::EndsWith(legacy_policy_for_current_profile, "keep_existing_data");
+ bool allowed_by_intercepted_account =
+ legacy_policy_for_intercepted_profile.empty() ||
+ legacy_policy_for_intercepted_profile == "none" ||
+ base::EndsWith(legacy_policy_for_intercepted_profile,
+ "keep_existing_data");
+ return allowed_by_existing_profile && allowed_by_intercepted_account;
}
#endif // !BUILDFLAG(IS_CHROMEOS)
diff --git a/chromium/chrome/browser/signin/signin_util.h b/chromium/chrome/browser/signin/signin_util.h
index bda932f7e2b..3e4c0d4d1a7 100644
--- a/chromium/chrome/browser/signin/signin_util.h
+++ b/chromium/chrome/browser/signin/signin_util.h
@@ -9,11 +9,14 @@
#include "base/containers/enum_set.h"
#include "base/files/file_path.h"
+#include "base/functional/callback.h"
#include "base/supports_user_data.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
+#include "components/policy/core/browser/signin/profile_separation_policies.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/identity_manager/tribool.h"
+#include "net/cookies/canonical_cookie.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
class Profile;
@@ -23,7 +26,6 @@ namespace signin_util {
enum class ProfileSeparationPolicyState {
kEnforcedByExistingProfile,
kEnforcedByInterceptedAccount,
- kStrict,
kEnforcedOnMachineLevel,
kKeepsBrowsingData,
kMaxValue = kKeepsBrowsingData
@@ -46,6 +48,40 @@ class ScopedForceSigninSetterForTesting {
const ScopedForceSigninSetterForTesting&) = delete;
};
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+// Utility class that moves cookies linked to a URL from one profile to the
+// other. This will be mostly used when a new profile is created after a
+// signin interception of an account linked a SAML signin.
+class CookiesMover {
+ public:
+ // Moves cookies related to `url` from `source_profile` to
+ // `destination_profile` and calls `callback` when it is done.
+ CookiesMover(base::WeakPtr<Profile> source_profile,
+ base::WeakPtr<Profile> destination_profile,
+ base::OnceCallback<void()> callback);
+
+ CookiesMover(const CookiesMover& copy) = delete;
+ CookiesMover& operator=(const CookiesMover&) = delete;
+ ~CookiesMover();
+
+ void StartMovingCookies();
+
+ private:
+ void OnCookiesReceived(
+ const std::vector<net::CookieWithAccessResult>& included,
+ const std::vector<net::CookieWithAccessResult>& excluded);
+
+ // Called when all the cookies have been moved.
+ void OnCookiesMoved();
+
+ GURL url_;
+ base::WeakPtr<Profile> source_profile_;
+ base::WeakPtr<Profile> destination_profile_;
+ base::OnceCallback<void()> callback_;
+ base::WeakPtrFactory<CookiesMover> weak_pointer_factory_{this};
+};
+#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+
// Return whether the force sign in policy is enabled or not.
// The state of this policy will not be changed without relaunch Chrome.
bool IsForceSigninEnabled();
@@ -63,29 +99,21 @@ bool IsProfileDeletionAllowed(Profile* profile);
#if !BUILDFLAG(IS_ANDROID)
#if !BUILDFLAG(IS_CHROMEOS)
-// Returns the state of profile separation on any account that would signin
-// inside `profile`. Returns an empty set if profile separation is not enforced
-// on accounts that will sign in the content area of `profile`.
-ProfileSeparationPolicyStateSet GetProfileSeparationPolicyState(
- Profile* profile,
- const absl::optional<std::string>& intercepted_account_level_policy_value =
- absl::nullopt);
-
-// Returns true if profile separation must be enforced on an account signing in
-// the content area of `profile` by the ManagedAccountsSigninRestriction policy
-// for `profile` or if the value of 'intercepted_account_level_policy_value'
-// enforces profile separation for an intercepted account.
-// `intercepted_account_level_policy_value` has a value only in the case of an
-// account interception. This is used mainly in DiceWebSigninInterceptor to
-// determine if an intercepted account requires a new profile.
-bool ProfileSeparationEnforcedByPolicy(
- Profile* profile,
- const absl::optional<std::string>& intercepted_account_level_policy_value =
- absl::nullopt);
+
+// Returns true if managed accounts signin are required to create a new profile
+// by policies set in `profile`.
+bool IsProfileSeparationEnforcedByProfile(Profile* profile);
+
+// Returns true if profile separation is enforced by
+// `intercepted_account_separation_policies`.
+bool IsProfileSeparationEnforcedByPolicies(
+ const policy::ProfileSeparationPolicies&
+ intercepted_account_separation_policies);
bool ProfileSeparationAllowsKeepingUnmanagedBrowsingDataInManagedProfile(
Profile* profile,
- const std::string& intercepted_account_level_policy_value);
+ const policy::ProfileSeparationPolicies&
+ intercepted_account_separation_policies);
#endif // !BUILDFLAG(IS_CHROMEOS)
// Records a UMA metric if the user accepts or not to create an enterprise
// profile.
diff --git a/chromium/chrome/browser/signin/signin_util_unittest.cc b/chromium/chrome/browser/signin/signin_util_unittest.cc
index e3c3c5ae8db..fc105f9330c 100644
--- a/chromium/chrome/browser/signin/signin_util_unittest.cc
+++ b/chromium/chrome/browser/signin/signin_util_unittest.cc
@@ -14,6 +14,7 @@
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
+#include "components/policy/core/browser/signin/profile_separation_policies.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/browser_task_environment.h"
@@ -21,6 +22,25 @@
using signin_util::ProfileSeparationPolicyState;
using signin_util::ProfileSeparationPolicyStateSet;
+namespace {
+
+const char kLegacyPolicyEmpty[] = "";
+const char kLegacyPolicyNone[] = "none";
+const char kLegacyPolicyPrimaryAccount[] = "primary_account";
+const char kLegacyPolicyPrimaryAccountStrict[] = "primary_account_strict";
+const char kLegacyPolicyPrimaryAccountStrictKeepExistingData[] =
+ "primary_account_strict_keep_existing_data";
+const char kLegacyPolicyPrimaryAccountKeepExistingData[] =
+ "primary_account_keep_existing_data";
+
+std::string FormatForLogging(const std::string& local_policy,
+ const std::string& intercepted_policy) {
+ return "Local policy : " + local_policy +
+ ", Intercepted policy : " + intercepted_policy;
+}
+
+} // namespace
+
class SigninUtilTest : public BrowserWithTestWindowTest {
public:
void SetUp() override {
@@ -32,6 +52,64 @@ class SigninUtilTest : public BrowserWithTestWindowTest {
signin_util::ResetForceSigninForTesting();
BrowserWithTestWindowTest::TearDown();
}
+
+ bool SeparationEnforcedByExistingProfileExpected(
+ const std::string& local_policy) {
+ return enforced_by_existing_profile.find(local_policy) !=
+ enforced_by_existing_profile.end();
+ }
+ bool SeparationEnforcedByInterceptedAccountExpected(
+ const std::string& intercepted_policy) {
+ return enforced_by_intercepted_account.find(intercepted_policy) !=
+ enforced_by_intercepted_account.end();
+ }
+ bool KeepBrowsingDataExpected(const std::string& local_policy,
+ const std::string& intercepted_policy) {
+ return keeps_browsing_data.find(local_policy) !=
+ keeps_browsing_data.end() &&
+ keeps_browsing_data.find(intercepted_policy) !=
+ keeps_browsing_data.end();
+ }
+ bool SeparationEnforcedOnMachineLevelExpected(
+ const std::string& local_policy) {
+ return enforced_on_machine_level.find(local_policy) !=
+ enforced_on_machine_level.end();
+ }
+
+ protected:
+ std::array<std::string, 6> all_policies{
+ kLegacyPolicyEmpty,
+ kLegacyPolicyNone,
+ kLegacyPolicyPrimaryAccount,
+ kLegacyPolicyPrimaryAccountStrict,
+ kLegacyPolicyPrimaryAccountStrictKeepExistingData,
+ kLegacyPolicyPrimaryAccountKeepExistingData,
+ };
+
+ base::flat_set<std::string> enforced_by_existing_profile{
+ kLegacyPolicyPrimaryAccountStrict,
+ kLegacyPolicyPrimaryAccountStrictKeepExistingData};
+
+ base::flat_set<std::string> enforced_by_intercepted_account{
+ kLegacyPolicyPrimaryAccount,
+ kLegacyPolicyPrimaryAccountStrict,
+ kLegacyPolicyPrimaryAccountStrictKeepExistingData,
+ kLegacyPolicyPrimaryAccountKeepExistingData,
+ };
+
+ base::flat_set<std::string> keeps_browsing_data{
+ kLegacyPolicyEmpty,
+ kLegacyPolicyNone,
+ kLegacyPolicyPrimaryAccountKeepExistingData,
+ kLegacyPolicyPrimaryAccountStrictKeepExistingData,
+ };
+
+ base::flat_set<std::string> enforced_on_machine_level{
+ kLegacyPolicyPrimaryAccount,
+ kLegacyPolicyPrimaryAccountStrict,
+ kLegacyPolicyPrimaryAccountStrictKeepExistingData,
+ kLegacyPolicyPrimaryAccountKeepExistingData,
+ };
};
TEST_F(SigninUtilTest, GetForceSigninPolicy) {
@@ -48,441 +126,67 @@ TEST_F(SigninUtilTest, GetForceSigninPolicy) {
}
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
-TEST_F(SigninUtilTest, GetProfileSeparationPolicyState) {
+TEST_F(SigninUtilTest, IsProfileSeparationEnforcedByProfile) {
std::unique_ptr<TestingProfile> profile = TestingProfile::Builder().Build();
+ for (const auto& local_policy : all_policies) {
+ if (local_policy.empty()) {
+ profile.get()->GetPrefs()->ClearPref(
+ prefs::kManagedAccountsSigninRestriction);
+ } else {
+ profile.get()->GetPrefs()->SetString(
+ prefs::kManagedAccountsSigninRestriction, local_policy);
+ }
+ EXPECT_EQ(signin_util::IsProfileSeparationEnforcedByProfile(profile.get()),
+ SeparationEnforcedByExistingProfileExpected(local_policy));
+ }
- // No policy set on the active profile.
- EXPECT_TRUE(
- signin_util::GetProfileSeparationPolicyState(profile.get()).Empty());
- EXPECT_TRUE(
- signin_util::GetProfileSeparationPolicyState(profile.get(), "none")
- .Empty());
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(),
- "primary_account"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
-
- // Active profile has "none" as a user level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "none");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, false);
- EXPECT_TRUE(
- signin_util::GetProfileSeparationPolicyState(profile.get()).Empty());
- EXPECT_TRUE(
- signin_util::GetProfileSeparationPolicyState(profile.get(), "none")
- .Empty());
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(),
- "primary_account"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kKeepsBrowsingData,
- ProfileSeparationPolicyState::kStrict}));
-
- // Active profile has "none" as a machine level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "none");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, true);
- EXPECT_TRUE(
- signin_util::GetProfileSeparationPolicyState(profile.get()).Empty());
- EXPECT_TRUE(
- signin_util::GetProfileSeparationPolicyState(profile.get(), "none")
- .Empty());
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(),
- "primary_account"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kKeepsBrowsingData,
- ProfileSeparationPolicyState::kStrict}));
-
- // Active profile has "primary_account" as a user level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, false);
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get()),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(), "none"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(),
- "primary_account"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict}));
-
- // Active profile has "primary_account_strict" as a user level
- // policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account_strict");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, false);
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get()),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kStrict}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(), "none"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kStrict}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(),
- "primary_account"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict}));
-
- // Active profile has "primary_account" as a machine level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, true);
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get()),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(), "none"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(),
- "primary_account"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict}));
-
- // Active profile has "primary_account_keep_existing_data" as a
- // machine level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account_keep_existing_data");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, true);
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get()),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(), "none"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(),
- "primary_account"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
-
- // Active profile has "primary_account_strict" as a machine level
- // policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account_strict");
- profile->GetPrefs()->SetBoolean(
+ // Test profile set a machine level.
+ profile.get()->GetPrefs()->SetBoolean(
prefs::kManagedAccountsSigninRestrictionScopeMachine, true);
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get()),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(), "none"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(),
- "primary_account"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict}));
- // Active profile has "primary_account_strict_keep_existing_data"
- // as a machine level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account_strict_keep_existing_data");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, true);
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get()),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(), "none"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(profile.get(),
- "primary_account"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
- EXPECT_EQ(signin_util::GetProfileSeparationPolicyState(
- profile.get(), "primary_account_strict_keep_existing_data"),
- ProfileSeparationPolicyStateSet(
- {ProfileSeparationPolicyState::kEnforcedByExistingProfile,
- ProfileSeparationPolicyState::kEnforcedOnMachineLevel,
- ProfileSeparationPolicyState::kEnforcedByInterceptedAccount,
- ProfileSeparationPolicyState::kStrict,
- ProfileSeparationPolicyState::kKeepsBrowsingData}));
+ for (const auto& local_policy : all_policies) {
+ if (local_policy.empty()) {
+ profile.get()->GetPrefs()->ClearPref(
+ prefs::kManagedAccountsSigninRestriction);
+ } else {
+ profile.get()->GetPrefs()->SetString(
+ prefs::kManagedAccountsSigninRestriction, local_policy);
+ }
+ EXPECT_EQ(signin_util::IsProfileSeparationEnforcedByProfile(profile.get()),
+ SeparationEnforcedOnMachineLevelExpected(local_policy));
+ }
}
-TEST_F(SigninUtilTest, ProfileSeparationEnforcedByPolicy) {
+TEST_F(SigninUtilTest, IsProfileSeparationEnforcedByPolicies) {
std::unique_ptr<TestingProfile> profile = TestingProfile::Builder().Build();
+ for (const auto& intercepted_policy : all_policies) {
+ EXPECT_EQ(
+ signin_util::IsProfileSeparationEnforcedByPolicies(
+ policy::ProfileSeparationPolicies(intercepted_policy)),
+ SeparationEnforcedByInterceptedAccountExpected(intercepted_policy));
+ }
+}
- // No policy set on the active profile.
- EXPECT_FALSE(signin_util::ProfileSeparationEnforcedByPolicy(profile.get(),
- std::string()));
- EXPECT_FALSE(
- signin_util::ProfileSeparationEnforcedByPolicy(profile.get(), "none"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account_strict"));
-
- // Active profile has "none" as a user level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "none");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, false);
- EXPECT_FALSE(signin_util::ProfileSeparationEnforcedByPolicy(profile.get(),
- std::string()));
- EXPECT_FALSE(
- signin_util::ProfileSeparationEnforcedByPolicy(profile.get(), "none"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account_strict"));
-
- // Active profile has "none" as a machine level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "none");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, true);
- EXPECT_FALSE(signin_util::ProfileSeparationEnforcedByPolicy(profile.get(),
- std::string()));
- EXPECT_FALSE(
- signin_util::ProfileSeparationEnforcedByPolicy(profile.get(), "none"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account_strict"));
-
- // Active profile has "primary_account" as a user level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, false);
- EXPECT_FALSE(signin_util::ProfileSeparationEnforcedByPolicy(profile.get(),
- std::string()));
- EXPECT_FALSE(
- signin_util::ProfileSeparationEnforcedByPolicy(profile.get(), "none"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account_strict"));
-
- // Active profile has "primary_account_strict" as a user level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account_strict");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, false);
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(profile.get(),
- std::string()));
- EXPECT_TRUE(
- signin_util::ProfileSeparationEnforcedByPolicy(profile.get(), "none"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account_strict"));
-
- // Active profile has "primary_account" as a machine level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, true);
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(profile.get(),
- std::string()));
- EXPECT_TRUE(
- signin_util::ProfileSeparationEnforcedByPolicy(profile.get(), "none"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account_strict"));
-
- // Active profile has "primary_account_strict" as a machine level policy.
- profile->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
- "primary_account_strict");
- profile->GetPrefs()->SetBoolean(
- prefs::kManagedAccountsSigninRestrictionScopeMachine, true);
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(profile.get(),
- std::string()));
- EXPECT_TRUE(
- signin_util::ProfileSeparationEnforcedByPolicy(profile.get(), "none"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account"));
- EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
- profile.get(), "primary_account_strict"));
+TEST_F(SigninUtilTest,
+ ProfileSeparationAllowsKeepingUnmanagedBrowsingDataInManagedProfile) {
+ std::unique_ptr<TestingProfile> profile = TestingProfile::Builder().Build();
+ for (const auto& local_policy : all_policies) {
+ if (local_policy.empty()) {
+ profile.get()->GetPrefs()->ClearPref(
+ prefs::kManagedAccountsSigninRestriction);
+ } else {
+ profile.get()->GetPrefs()->SetString(
+ prefs::kManagedAccountsSigninRestriction, local_policy);
+ }
+
+ for (const auto& intercepted_policy : all_policies) {
+ EXPECT_EQ(
+ signin_util::
+ ProfileSeparationAllowsKeepingUnmanagedBrowsingDataInManagedProfile(
+ profile.get(),
+ policy::ProfileSeparationPolicies(intercepted_policy)),
+ KeepBrowsingDataExpected(local_policy, intercepted_policy));
+ }
+ }
}
#endif
diff --git a/chromium/chrome/browser/signin/token_managed_profile_creator.cc b/chromium/chrome/browser/signin/token_managed_profile_creator.cc
index f054e9e8192..7665e7d90f7 100644
--- a/chromium/chrome/browser/signin/token_managed_profile_creator.cc
+++ b/chromium/chrome/browser/signin/token_managed_profile_creator.cc
@@ -17,17 +17,20 @@
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/account_consistency_mode_manager_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/signin_util.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
TokenManagedProfileCreator::TokenManagedProfileCreator(
+ Profile* source_profile,
const std::string& id,
const std::string& enrollment_token,
const std::u16string& local_profile_name,
- base::OnceCallback<void(Profile*)> callback)
- : id_(id),
+ base::OnceCallback<void(base::WeakPtr<Profile>)> callback)
+ : source_profile_(source_profile),
+ id_(id),
enrollment_token_(enrollment_token),
expected_profile_path_(g_browser_process->profile_manager()
->GetNextExpectedProfileDirectoryPath()),
@@ -48,9 +51,10 @@ TokenManagedProfileCreator::TokenManagedProfileCreator(
}
TokenManagedProfileCreator::TokenManagedProfileCreator(
+ Profile* source_profile,
const base::FilePath& target_profile_path,
- base::OnceCallback<void(Profile*)> callback)
- : callback_(std::move(callback)) {
+ base::OnceCallback<void(base::WeakPtr<Profile>)> callback)
+ : source_profile_(source_profile), callback_(std::move(callback)) {
// Make sure the callback is not called synchronously.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
@@ -90,7 +94,9 @@ void TokenManagedProfileCreator::OnNewProfileCreated(Profile* new_profile) {
}
void TokenManagedProfileCreator::OnNewProfileInitialized(Profile* new_profile) {
- if (callback_) {
- std::move(callback_).Run(new_profile);
- }
+ // base::Unretained is fine because `cookies_mover_` is owned by this.
+ cookies_mover_ = std::make_unique<signin_util::CookiesMover>(
+ source_profile_->GetWeakPtr(), new_profile->GetWeakPtr(),
+ base::BindOnce(std::move(callback_), new_profile->GetWeakPtr()));
+ cookies_mover_->StartMovingCookies();
}
diff --git a/chromium/chrome/browser/signin/token_managed_profile_creator.h b/chromium/chrome/browser/signin/token_managed_profile_creator.h
index 422e6cccd25..4d978126ff8 100644
--- a/chromium/chrome/browser/signin/token_managed_profile_creator.h
+++ b/chromium/chrome/browser/signin/token_managed_profile_creator.h
@@ -16,6 +16,10 @@
#include "google_apis/gaia/core_account_id.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+namespace signin_util {
+class CookiesMover;
+}
+
// Extracts an account from an existing profile and moves it to a new profile.
class TokenManagedProfileCreator : public ProfileAttributesStorageObserver {
public:
@@ -26,16 +30,20 @@ class TokenManagedProfileCreator : public ProfileAttributesStorageObserver {
// If |local_profile_name| is not empty, it will be set as local name for the
// new profile.
// If |icon_index| is nullopt, a random icon will be selected.
- TokenManagedProfileCreator(const std::string& id,
- const std::string& enrollment_token,
- const std::u16string& local_profile_name,
- base::OnceCallback<void(Profile*)> callback);
+ TokenManagedProfileCreator(
+ Profile* source_profile,
+ const std::string& id,
+ const std::string& enrollment_token,
+ const std::u16string& local_profile_name,
+ base::OnceCallback<void(base::WeakPtr<Profile>)> callback);
// Uses this version when the profile already exists at `target_profile_path`
// but may not be loaded in memory. The profile is loaded if necessary, and
// the account is moved.
- TokenManagedProfileCreator(const base::FilePath& target_profile_path,
- base::OnceCallback<void(Profile*)> callback);
+ TokenManagedProfileCreator(
+ Profile* source_profile,
+ const base::FilePath& target_profile_path,
+ base::OnceCallback<void(base::WeakPtr<Profile>)> callback);
~TokenManagedProfileCreator() override;
@@ -56,10 +64,12 @@ class TokenManagedProfileCreator : public ProfileAttributesStorageObserver {
// Callback invoked once the token service is ready for the new profile.
void OnNewProfileTokensLoaded(Profile* new_profile);
+ raw_ptr<Profile> source_profile_;
const std::string id_;
const std::string enrollment_token_;
base::FilePath expected_profile_path_;
- base::OnceCallback<void(Profile*)> callback_;
+ base::OnceCallback<void(base::WeakPtr<Profile>)> callback_;
+ std::unique_ptr<signin_util::CookiesMover> cookies_mover_;
base::ScopedObservation<ProfileAttributesStorage,
ProfileAttributesStorage::Observer>
profile_observation_{this};
diff --git a/chromium/chrome/browser/signin/token_managed_profile_creator_unittest.cc b/chromium/chrome/browser/signin/token_managed_profile_creator_unittest.cc
index 8fc72a31e3d..ef308ba1884 100644
--- a/chromium/chrome/browser/signin/token_managed_profile_creator_unittest.cc
+++ b/chromium/chrome/browser/signin/token_managed_profile_creator_unittest.cc
@@ -4,12 +4,16 @@
#include "chrome/browser/signin/token_managed_profile_creator.h"
+#include "base/barrier_closure.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
+#include "chrome/browser/enterprise/profile_management/profile_management_features.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
@@ -18,40 +22,160 @@
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/signin/public/base/signin_pref_names.h"
+#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_task_environment.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
-class TokenManagedProfileCreatorTest : public testing::Test {
+namespace {
+
+void CreateCookies(
+ Profile* profile,
+ const std::map<std::string, std::string> cookie_url_and_name) {
+ network::mojom::CookieManager* cookie_manager =
+ profile->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess();
+
+ base::RunLoop run_loop;
+ base::RepeatingClosure barrier = base::BarrierClosure(
+ cookie_url_and_name.size(),
+ base::BindLambdaForTesting([&]() { run_loop.Quit(); }));
+
+ for (const auto& [url_string, name] : cookie_url_and_name) {
+ GURL url(url_string);
+ std::unique_ptr<net::CanonicalCookie> cookie =
+ net::CanonicalCookie::CreateSanitizedCookie(
+ url, name, "A=" + name, url.host(), url.path(), base::Time::Now(),
+ base::Time::Max(), base::Time::Now(), url.SchemeIsCryptographic(),
+ false, net::CookieSameSite::NO_RESTRICTION,
+ net::COOKIE_PRIORITY_DEFAULT, false, absl::nullopt);
+ cookie_manager->SetCanonicalCookie(
+ *cookie, url, net::CookieOptions::MakeAllInclusive(),
+ base::BindLambdaForTesting(
+ [&](net::CookieAccessResult access_result) { barrier.Run(); }));
+ }
+
+ run_loop.Run();
+}
+
+} // namespace
+
+class TokenManagedProfileCreatorTest
+ : public testing::Test,
+ public testing::WithParamInterface<std::tuple<bool, bool>> {
public:
TokenManagedProfileCreatorTest()
: profile_manager_(std::make_unique<TestingProfileManager>(
- TestingBrowserProcess::GetGlobal())) {}
+ TestingBrowserProcess::GetGlobal())) {
+ feature_list_.InitWithFeatureState(
+ profile_management::features::kThirdPartyProfileManagement,
+ enable_third_party_management_feature());
+ }
~TokenManagedProfileCreatorTest() override = default;
- void SetUp() override { ASSERT_TRUE(profile_manager_->SetUp()); }
+ void SetUp() override {
+ ASSERT_TRUE(profile_manager_->SetUp());
+ profile_ = profile_manager_->CreateTestingProfile("profile");
+ }
// Callback for the TokenManagedProfileCreator.
- void OnProfileCreated(base::OnceClosure quit_closure, Profile* profile) {
+ void OnProfileCreated(base::OnceClosure quit_closure,
+ base::WeakPtr<Profile> profile) {
creator_callback_called_ = true;
- created_profile_ = profile;
+ created_profile_ = profile.get();
if (quit_closure) {
std::move(quit_closure).Run();
}
}
+ bool enable_third_party_management_feature() {
+ return std::get<0>(GetParam());
+ }
+
+ bool setup_cookies_to_move() { return std::get<1>(GetParam()); }
+
+ void SetupCookiesToMove() {
+ if (!setup_cookies_to_move()) {
+ return;
+ }
+ // Add some cookies
+ CreateCookies(profile_.get(), {{"https://google.com", "oldgoogle0"},
+ {"https://example.com", "oldexample0"}});
+ CreateCookies(profile_.get(), {{"https://google.com", "validgoogle1"},
+ {"https://example.com", "validexample1"}});
+ CreateCookies(profile_.get(), {{"https://google.com", "newgoogle2"},
+ {"https://example.com", "newexample2"}});
+
+ profile_->GetPrefs()->SetString(prefs::kSigninInterceptionIDPCookiesUrl,
+ "https://www.google.com/");
+ }
+
+ void VerifyCookiesMoved() {
+ if (!setup_cookies_to_move()) {
+ return;
+ }
+ GURL url("https://www.google.com/");
+ net::CookieList cookies_source_profile;
+ net::CookieList cookies_new_profile;
+ {
+ network::mojom::CookieManager* cookie_manager =
+ profile_->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess();
+ base::RunLoop loop;
+ cookie_manager->GetAllCookies(
+ base::BindLambdaForTesting([&](const net::CookieList& cookies) {
+ cookies_source_profile = cookies;
+ loop.Quit();
+ }));
+ loop.Run();
+ }
+ {
+ network::mojom::CookieManager* cookie_manager =
+ created_profile_->GetDefaultStoragePartition()
+ ->GetCookieManagerForBrowserProcess();
+ base::RunLoop loop;
+ cookie_manager->GetAllCookies(
+ base::BindLambdaForTesting([&](const net::CookieList& cookies) {
+ cookies_new_profile = cookies;
+ loop.Quit();
+ }));
+ loop.Run();
+ }
+
+ if (!base::FeatureList::IsEnabled(
+ profile_management::features::kThirdPartyProfileManagement)) {
+ EXPECT_EQ(6u, cookies_source_profile.size());
+ EXPECT_TRUE(cookies_new_profile.empty());
+ return;
+ }
+
+ EXPECT_EQ(3u, cookies_source_profile.size());
+ EXPECT_EQ(3u, cookies_new_profile.size());
+
+ for (const auto& cookie : cookies_new_profile) {
+ EXPECT_TRUE(cookie.IsDomainMatch(url.host()));
+ EXPECT_TRUE(cookie.Name() == "oldgoogle0" ||
+ cookie.Name() == "validgoogle1" ||
+ cookie.Name() == "newgoogle2");
+ }
+ }
+
protected:
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::UI};
+ base::test::ScopedFeatureList feature_list_;
std::unique_ptr<TestingProfileManager> profile_manager_;
+ raw_ptr<Profile> profile_;
raw_ptr<Profile> created_profile_;
bool creator_callback_called_ = false;
};
-TEST_F(TokenManagedProfileCreatorTest, CreatesProfileWithManagementInfo) {
+TEST_P(TokenManagedProfileCreatorTest, CreatesProfileWithManagementInfo) {
+ SetupCookiesToMove();
base::RunLoop loop;
TokenManagedProfileCreator creator(
- "id", "enrollment_token", u"local_profile_name",
+ profile_, "id", "enrollment_token", u"local_profile_name",
base::BindOnce(&TokenManagedProfileCreatorTest::OnProfileCreated,
base::Unretained(this), loop.QuitClosure()));
loop.Run();
@@ -66,9 +190,10 @@ TEST_F(TokenManagedProfileCreatorTest, CreatesProfileWithManagementInfo) {
EXPECT_EQ("id", entry->GetProfileManagementId());
EXPECT_EQ("enrollment_token", entry->GetProfileManagementEnrollmentToken());
EXPECT_EQ(u"local_profile_name", entry->GetName());
+ VerifyCookiesMoved();
}
-TEST_F(TokenManagedProfileCreatorTest, LoadsExistingProfile) {
+TEST_P(TokenManagedProfileCreatorTest, LoadsExistingProfile) {
auto* profile_manager = g_browser_process->profile_manager();
Profile& new_profile = profiles::testing::CreateProfileSync(
profile_manager, profile_manager->GenerateNextProfileDirectoryPath());
@@ -79,11 +204,12 @@ TEST_F(TokenManagedProfileCreatorTest, LoadsExistingProfile) {
entry->SetProfileManagementId("id");
entry->SetProfileManagementEnrollmentToken("enrollment_token");
}
-
+ SetupCookiesToMove();
base::RunLoop loop;
TokenManagedProfileCreator creator(
- path, base::BindOnce(&TokenManagedProfileCreatorTest::OnProfileCreated,
- base::Unretained(this), loop.QuitClosure()));
+ profile_, path,
+ base::BindOnce(&TokenManagedProfileCreatorTest::OnProfileCreated,
+ base::Unretained(this), loop.QuitClosure()));
loop.Run();
EXPECT_TRUE(creator_callback_called_);
ASSERT_TRUE(created_profile_);
@@ -94,4 +220,10 @@ TEST_F(TokenManagedProfileCreatorTest, LoadsExistingProfile) {
ASSERT_TRUE(entry);
EXPECT_EQ("id", entry->GetProfileManagementId());
EXPECT_EQ("enrollment_token", entry->GetProfileManagementEnrollmentToken());
+ VerifyCookiesMoved();
}
+
+INSTANTIATE_TEST_SUITE_P(All,
+ TokenManagedProfileCreatorTest,
+ ::testing::Combine(::testing::Bool(),
+ ::testing::Bool()));
diff --git a/chromium/chrome/browser/signin/wait_for_network_callback_helper_ash.cc b/chromium/chrome/browser/signin/wait_for_network_callback_helper_ash.cc
new file mode 100644
index 00000000000..4fc760a7c79
--- /dev/null
+++ b/chromium/chrome/browser/signin/wait_for_network_callback_helper_ash.cc
@@ -0,0 +1,36 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/wait_for_network_callback_helper_ash.h"
+
+#include "base/functional/callback.h"
+#include "chrome/browser/ash/net/delay_network_call.h"
+#include "chromeos/ash/components/network/network_handler.h"
+
+bool WaitForNetworkCallbackHelperAsh::AreNetworkCallsDelayed() {
+ if (delaying_network_calls_disabled_for_testing_) {
+ return false;
+ }
+ return ash::AreNetworkCallsDelayed();
+}
+
+void WaitForNetworkCallbackHelperAsh::DelayNetworkCall(
+ base::OnceClosure callback) {
+ if (!AreNetworkCallsDelayed()) {
+ std::move(callback).Run();
+ return;
+ }
+ ash::DelayNetworkCall(std::move(callback));
+}
+
+void WaitForNetworkCallbackHelperAsh::DisableNetworkCallsDelayedForTesting(
+ bool disable) {
+ if (!disable) {
+ // Delay network calls if offline is enabled.
+ // `NetworkHandler` must be initialized.
+ CHECK(ash::NetworkHandler::IsInitialized())
+ << "NetworkHandler must be Initialized";
+ }
+ delaying_network_calls_disabled_for_testing_ = disable;
+}
diff --git a/chromium/chrome/browser/signin/wait_for_network_callback_helper_ash.h b/chromium/chrome/browser/signin/wait_for_network_callback_helper_ash.h
new file mode 100644
index 00000000000..5bee84ee5e7
--- /dev/null
+++ b/chromium/chrome/browser/signin/wait_for_network_callback_helper_ash.h
@@ -0,0 +1,22 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_WAIT_FOR_NETWORK_CALLBACK_HELPER_ASH_H_
+#define CHROME_BROWSER_SIGNIN_WAIT_FOR_NETWORK_CALLBACK_HELPER_ASH_H_
+
+#include "base/functional/callback_forward.h"
+#include "components/signin/public/base/wait_for_network_callback_helper.h"
+
+class WaitForNetworkCallbackHelperAsh : public WaitForNetworkCallbackHelper {
+ public:
+ // WaitForNetworkCallbackHelper:
+ bool AreNetworkCallsDelayed() override;
+ void DelayNetworkCall(base::OnceClosure callback) override;
+ void DisableNetworkCallsDelayedForTesting(bool disable) override;
+
+ private:
+ bool delaying_network_calls_disabled_for_testing_ = false;
+};
+
+#endif // CHROME_BROWSER_SIGNIN_WAIT_FOR_NETWORK_CALLBACK_HELPER_ASH_H_
diff --git a/chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome.cc b/chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome.cc
new file mode 100644
index 00000000000..621ec30e18a
--- /dev/null
+++ b/chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome.cc
@@ -0,0 +1,53 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/wait_for_network_callback_helper_chrome.h"
+
+#include "content/public/browser/network_service_instance.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
+
+WaitForNetworkCallbackHelperChrome::WaitForNetworkCallbackHelperChrome() {
+ network_connection_observer_.Observe(content::GetNetworkConnectionTracker());
+}
+
+WaitForNetworkCallbackHelperChrome::~WaitForNetworkCallbackHelperChrome() =
+ default;
+
+void WaitForNetworkCallbackHelperChrome::OnConnectionChanged(
+ network::mojom::ConnectionType type) {
+ if (type == network::mojom::ConnectionType::CONNECTION_NONE) {
+ return;
+ }
+
+ std::vector<base::OnceClosure> callbacks;
+ delayed_callbacks_.swap(callbacks);
+ for (base::OnceClosure& callback : callbacks) {
+ std::move(callback).Run();
+ }
+}
+
+bool WaitForNetworkCallbackHelperChrome::AreNetworkCallsDelayed() {
+ // Don't bother if we don't have any kind of network connection.
+ network::mojom::ConnectionType type;
+ bool sync = content::GetNetworkConnectionTracker()->GetConnectionType(
+ &type,
+ base::BindOnce(&WaitForNetworkCallbackHelperChrome::OnConnectionChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+ if (!sync || type == network::mojom::ConnectionType::CONNECTION_NONE) {
+ return true;
+ }
+
+ return false;
+}
+
+void WaitForNetworkCallbackHelperChrome::DelayNetworkCall(
+ base::OnceClosure callback) {
+ if (!AreNetworkCallsDelayed()) {
+ std::move(callback).Run();
+ return;
+ }
+
+ // This queue will be processed in `OnConnectionChanged()`.
+ delayed_callbacks_.push_back(std::move(callback));
+}
diff --git a/chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome.h b/chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome.h
new file mode 100644
index 00000000000..261a02354ae
--- /dev/null
+++ b/chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome.h
@@ -0,0 +1,47 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_WAIT_FOR_NETWORK_CALLBACK_HELPER_CHROME_H_
+#define CHROME_BROWSER_SIGNIN_WAIT_FOR_NETWORK_CALLBACK_HELPER_CHROME_H_
+
+#include <vector>
+
+#include "base/functional/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "components/signin/public/base/wait_for_network_callback_helper.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
+#include "services/network/public/mojom/network_change_manager.mojom-forward.h"
+
+class WaitForNetworkCallbackHelperChrome
+ : public network::NetworkConnectionTracker::NetworkConnectionObserver,
+ public WaitForNetworkCallbackHelper {
+ public:
+ WaitForNetworkCallbackHelperChrome();
+
+ WaitForNetworkCallbackHelperChrome(
+ const WaitForNetworkCallbackHelperChrome&) = delete;
+ WaitForNetworkCallbackHelperChrome& operator=(
+ const WaitForNetworkCallbackHelperChrome&) = delete;
+
+ ~WaitForNetworkCallbackHelperChrome() override;
+
+ // network::NetworkConnectionTracker::NetworkConnectionObserver:
+ void OnConnectionChanged(network::mojom::ConnectionType type) override;
+
+ // WaitForNetworkCallbackHelper:
+ bool AreNetworkCallsDelayed() override;
+ void DelayNetworkCall(base::OnceClosure callback) override;
+
+ private:
+ std::vector<base::OnceClosure> delayed_callbacks_;
+ base::ScopedObservation<
+ network::NetworkConnectionTracker,
+ network::NetworkConnectionTracker::NetworkConnectionObserver>
+ network_connection_observer_{this};
+ base::WeakPtrFactory<WaitForNetworkCallbackHelperChrome> weak_ptr_factory_{
+ this};
+};
+
+#endif // CHROME_BROWSER_SIGNIN_WAIT_FOR_NETWORK_CALLBACK_HELPER_CHROME_H_
diff --git a/chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome_unittest.cc b/chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome_unittest.cc
new file mode 100644
index 00000000000..10bbe4572e9
--- /dev/null
+++ b/chromium/chrome/browser/signin/wait_for_network_callback_helper_chrome_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/wait_for_network_callback_helper_chrome.h"
+
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "services/network/test/test_network_connection_tracker.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class WaitForNetworkCallbackHelperChromeTest : public testing::Test {
+ public:
+ protected:
+ void SetUpNetworkConnection(bool respond_synchronously,
+ network::mojom::ConnectionType connection_type) {
+ auto* tracker = network::TestNetworkConnectionTracker::GetInstance();
+ tracker->SetRespondSynchronously(respond_synchronously);
+ tracker->SetConnectionType(connection_type);
+ }
+
+ void SetConnectionType(network::mojom::ConnectionType connection_type) {
+ network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
+ connection_type);
+ }
+
+ base::test::TaskEnvironment task_environment_;
+ WaitForNetworkCallbackHelperChrome helper_;
+};
+
+TEST_F(WaitForNetworkCallbackHelperChromeTest,
+ DelayNetworkCallRunsImmediatelyWithNetwork) {
+ SetUpNetworkConnection(true, network::mojom::ConnectionType::CONNECTION_3G);
+ base::test::TestFuture<void> future;
+ helper_.DelayNetworkCall(future.GetCallback());
+ EXPECT_TRUE(future.IsReady());
+}
+
+TEST_F(WaitForNetworkCallbackHelperChromeTest,
+ GetConnectionTypeRespondsAsynchronously) {
+ SetUpNetworkConnection(false, network::mojom::ConnectionType::CONNECTION_3G);
+
+ base::test::TestFuture<void> future;
+ helper_.DelayNetworkCall(future.GetCallback());
+ EXPECT_FALSE(future.IsReady());
+ EXPECT_TRUE(future.Wait());
+}
+
+TEST_F(WaitForNetworkCallbackHelperChromeTest,
+ DelayNetworkCallRunsAfterNetworkChange) {
+ SetUpNetworkConnection(true, network::mojom::ConnectionType::CONNECTION_NONE);
+
+ base::test::TestFuture<void> future;
+ helper_.DelayNetworkCall(future.GetCallback());
+ EXPECT_FALSE(future.IsReady());
+ SetConnectionType(network::mojom::ConnectionType::CONNECTION_3G);
+ EXPECT_TRUE(future.Wait());
+}
+
+TEST_F(WaitForNetworkCallbackHelperChromeTest,
+ MutlipleDelayNetworkCallsRunsAfterNetworkChange) {
+ SetUpNetworkConnection(true, network::mojom::ConnectionType::CONNECTION_NONE);
+
+ std::array<base::test::TestFuture<void>, 5> futures;
+ for (auto& future : futures) {
+ helper_.DelayNetworkCall(future.GetCallback());
+ EXPECT_FALSE(future.IsReady());
+ }
+ SetConnectionType(network::mojom::ConnectionType::CONNECTION_3G);
+ EXPECT_TRUE(futures[0].Wait());
+ for (auto& future : futures) {
+ EXPECT_TRUE(future.IsReady());
+ }
+}
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_factory.cc b/chromium/chrome/browser/spellchecker/spellcheck_factory.cc
index a011bb9509d..7c5113231ac 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_factory.cc
+++ b/chromium/chrome/browser/spellchecker/spellcheck_factory.cc
@@ -43,12 +43,11 @@ SpellcheckServiceFactory::SpellcheckServiceFactory()
SpellcheckServiceFactory::~SpellcheckServiceFactory() = default;
-KeyedService* SpellcheckServiceFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+SpellcheckServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
// Many variables are initialized from the |context| in the SpellcheckService.
- SpellcheckService* spellcheck = new SpellcheckService(context);
-
- return spellcheck;
+ return std::make_unique<SpellcheckService>(context);
}
void SpellcheckServiceFactory::RegisterProfilePrefs(
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_factory.h b/chromium/chrome/browser/spellchecker/spellcheck_factory.h
index 0c238d4cab8..0406ddddb96 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_factory.h
+++ b/chromium/chrome/browser/spellchecker/spellcheck_factory.h
@@ -33,7 +33,7 @@ class SpellcheckServiceFactory : public ProfileKeyedServiceFactory {
~SpellcheckServiceFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
void RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) override;
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_mac_view_interactive_uitest.mm b/chromium/chrome/browser/spellchecker/spellcheck_mac_view_interactive_uitest.mm
index b392a27b6f0..bf7a80abb03 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_mac_view_interactive_uitest.mm
+++ b/chromium/chrome/browser/spellchecker/spellcheck_mac_view_interactive_uitest.mm
@@ -17,10 +17,6 @@
#include "chrome/browser/spellchecker/test/spellcheck_panel_browsertest_helper.h"
#endif
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
namespace {
class SpellCheckMacViewInteractiveUiTest : public InProcessBrowserTest {
diff --git a/chromium/chrome/browser/supervised_user/BUILD.gn b/chromium/chrome/browser/supervised_user/BUILD.gn
index 088bccbbb73..3054b5da497 100644
--- a/chromium/chrome/browser/supervised_user/BUILD.gn
+++ b/chromium/chrome/browser/supervised_user/BUILD.gn
@@ -149,6 +149,7 @@ if (is_android) {
":test_support_java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
"//chrome/browser/flags:java",
"//chrome/browser/profiles/android:java",
@@ -159,13 +160,16 @@ if (is_android) {
"//chrome/test/android:chrome_java_integration_test_support",
"//chrome/test/android:pagecontroller_utils_java",
"//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/browser_ui/bottomsheet/android/test:java",
"//components/signin/public/android:java",
"//components/signin/public/android:signin_java_test_support",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//net/android:net_java_test_support",
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_java_test_support",
diff --git a/chromium/chrome/browser/sync/test/integration/sync_integration_tests_sources.gni b/chromium/chrome/browser/sync/test/integration/sync_integration_tests_sources.gni
index cb1d9613dfe..c590842c7f0 100644
--- a/chromium/chrome/browser/sync/test/integration/sync_integration_tests_sources.gni
+++ b/chromium/chrome/browser/sync/test/integration/sync_integration_tests_sources.gni
@@ -12,7 +12,10 @@ sync_integration_tests_sources = [
"../browser/sync/test/integration/single_client_device_info_sync_test.cc",
"../browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc",
"../browser/sync/test/integration/single_client_history_sync_test.cc",
+ "../browser/sync/test/integration/single_client_incoming_password_sharing_invitation_test.cc",
"../browser/sync/test/integration/single_client_offer_sync_test.cc",
+ "../browser/sync/test/integration/single_client_outgoing_password_sharing_invitation_test.cc",
+ "../browser/sync/test/integration/single_client_reading_list_sync_test.cc",
"../browser/sync/test/integration/single_client_sync_invalidations_test.cc",
"../browser/sync/test/integration/single_client_typed_urls_sync_test.cc",
"../browser/sync/test/integration/sync_exponential_backoff_test.cc",
@@ -102,6 +105,6 @@ if (is_win) {
# https://crbug.com/1252812 The intent picker (launch icon) actions are not
# working on Lacros.
-if (is_chromeos && !is_chromeos_lacros) {
+if (is_chromeos_ash) {
sync_integration_tests_sources += [ "../browser/sync/test/integration/two_client_web_apps_integration_test_cros.cc" ]
}
diff --git a/chromium/chrome/browser/tab/BUILD.gn b/chromium/chrome/browser/tab/BUILD.gn
index 79f095f9e45..50c9fa93589 100644
--- a/chromium/chrome/browser/tab/BUILD.gn
+++ b/chromium/chrome/browser/tab/BUILD.gn
@@ -134,6 +134,7 @@ generate_jni("jni_headers") {
"java/src/org/chromium/chrome/browser/tab/WebContentsStateBridge.java",
"java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java",
"java/src/org/chromium/chrome/browser/tab/state/LevelDBPersistedDataStorage.java",
+ "java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java",
]
}
diff --git a/chromium/chrome/browser/tab_contents/navigation_metrics_recorder.cc b/chromium/chrome/browser/tab_contents/navigation_metrics_recorder.cc
index 00f130db379..4b0e6b0e993 100644
--- a/chromium/chrome/browser/tab_contents/navigation_metrics_recorder.cc
+++ b/chromium/chrome/browser/tab_contents/navigation_metrics_recorder.cc
@@ -55,8 +55,7 @@ NavigationMetricsRecorder::GetThirdPartyCookieBlockState(const GURL& url) {
if (!cookie_settings_->ShouldBlockThirdPartyCookies())
return ThirdPartyCookieBlockState::kCookiesAllowed;
bool blocking_enabled_for_site =
- !cookie_settings_->IsThirdPartyAccessAllowed(url,
- /*source=*/nullptr);
+ !cookie_settings_->IsThirdPartyAccessAllowed(url);
return blocking_enabled_for_site
? ThirdPartyCookieBlockState::kThirdPartyCookiesBlocked
: ThirdPartyCookieBlockState::
diff --git a/chromium/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc b/chromium/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc
index 5c3e027b611..4ce12a7d902 100644
--- a/chromium/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc
+++ b/chromium/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc
@@ -122,7 +122,7 @@ class NavigationMetricsRecorderPrerenderBrowserTest
const NavigationMetricsRecorderPrerenderBrowserTest&) = delete;
void SetUp() override {
- prerender_helper_.SetUp(embedded_test_server());
+ prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
NavigationMetricsRecorderBrowserTest::SetUp();
}
diff --git a/chromium/chrome/browser/tab_contents/view_source_browsertest.cc b/chromium/chrome/browser/tab_contents/view_source_browsertest.cc
index 3fca0cb9009..2d8edddbefc 100644
--- a/chromium/chrome/browser/tab_contents/view_source_browsertest.cc
+++ b/chromium/chrome/browser/tab_contents/view_source_browsertest.cc
@@ -21,6 +21,7 @@
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
@@ -780,7 +781,8 @@ class ViewSourcePrerenderTest : public ViewSourceTest {
void set_target(content::WebContents* target) { target_ = target; }
void SetUp() override {
- prerender_test_helper().SetUp(embedded_test_server());
+ prerender_test_helper().RegisterServerRequestMonitor(
+ embedded_test_server());
ViewSourceTest::SetUp();
}
@@ -790,7 +792,7 @@ class ViewSourcePrerenderTest : public ViewSourceTest {
base::Unretained(this))};
// The WebContents which is expected to request prerendering.
- raw_ptr<content::WebContents, DanglingUntriaged> target_ = nullptr;
+ raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> target_ = nullptr;
};
// A frame in a prerendered page should be able to have its source viewed, like
diff --git a/chromium/chrome/browser/tab_contents/web_contents_collection.cc b/chromium/chrome/browser/tab_contents/web_contents_collection.cc
index c9755ebfba6..37084b50efc 100644
--- a/chromium/chrome/browser/tab_contents/web_contents_collection.cc
+++ b/chromium/chrome/browser/tab_contents/web_contents_collection.cc
@@ -35,6 +35,10 @@ class WebContentsCollection::ForwardingWebContentsObserver
observer_->DidFinishNavigation(web_contents(), navigation_handle);
}
+ void DidStartLoading() override {
+ observer_->DidStartLoading(web_contents());
+ }
+
// The observer that callbacks should forward to, annotating the
// web contents they were fired in.
raw_ptr<WebContentsCollection::Observer> observer_;
diff --git a/chromium/chrome/browser/tab_contents/web_contents_collection.h b/chromium/chrome/browser/tab_contents/web_contents_collection.h
index eb4208fc736..fddd87ddeea 100644
--- a/chromium/chrome/browser/tab_contents/web_contents_collection.h
+++ b/chromium/chrome/browser/tab_contents/web_contents_collection.h
@@ -31,6 +31,7 @@ class WebContentsCollection {
virtual void DidFinishNavigation(
content::WebContents* web_contents,
content::NavigationHandle* navigation_handle) {}
+ virtual void DidStartLoading(content::WebContents* web_contents) {}
protected:
virtual ~Observer() = default;
diff --git a/chromium/chrome/browser/tabmodel/BUILD.gn b/chromium/chrome/browser/tabmodel/BUILD.gn
index 1686c267df6..95a2eee4497 100644
--- a/chromium/chrome/browser/tabmodel/BUILD.gn
+++ b/chromium/chrome/browser/tabmodel/BUILD.gn
@@ -12,7 +12,6 @@ android_library("java") {
"android/java/src/org/chromium/chrome/browser/tabmodel/AsyncTabParams.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/AsyncTabParamsManager.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModel.java",
- "android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModelFilter.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoStateProvider.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabHost.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabHostRegistry.java",
@@ -40,6 +39,7 @@ android_library("java") {
"android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabModelObserver.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserver.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabRegistrationObserver.java",
+ "android/java/src/org/chromium/chrome/browser/tabmodel/TabModelTabObserver.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/TabModelUtils.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/TabReparentingParams.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java",
@@ -70,6 +70,7 @@ robolectric_library("junit") {
sources = [
"android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabModelObserverUnitTest.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabRegistrationObserverUnitTest.java",
+ "android/java/src/org/chromium/chrome/browser/tabmodel/TabModelTabObserverUnitTest.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java",
]
@@ -79,10 +80,12 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
"//chrome/browser/flags:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/tab:java",
+ "//chrome/browser/tab_group:java",
"//chrome/browser/tabmodel:java",
"//chrome/test/android:chrome_java_unit_test_support",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/chrome/browser/test_dummy/internal/BUILD.gn b/chromium/chrome/browser/test_dummy/internal/BUILD.gn
index a7607628a90..f19d727b3a8 100644
--- a/chromium/chrome/browser/test_dummy/internal/BUILD.gn
+++ b/chromium/chrome/browser/test_dummy/internal/BUILD.gn
@@ -50,6 +50,7 @@ android_library("base_module_java") {
"//chrome/android/modules/test_dummy/provider:java",
"//chrome/android/modules/test_dummy/public:java",
"//chrome/browser/test_dummy:java",
+ "//components/module_installer/android:module_installer_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
]
sources = [ "android/java/src/org/chromium/chrome/browser/test_dummy/TestDummyActivity.java" ]
diff --git a/chromium/chrome/browser/touch_to_fill/android/BUILD.gn b/chromium/chrome/browser/touch_to_fill/android/BUILD.gn
index bfee5460d74..c13df8a6bfc 100644
--- a/chromium/chrome/browser/touch_to_fill/android/BUILD.gn
+++ b/chromium/chrome/browser/touch_to_fill/android/BUILD.gn
@@ -57,6 +57,7 @@ android_library("public_java") {
robolectric_binary("touch_to_fill_junit_tests") {
testonly = true
resources_package = "org.chromium.chrome.browser.touch_to_fill"
+ shared_libraries = [ "//url:libgurl_robolectric($robolectric_toolchain)" ]
sources = [ "junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java" ]
@@ -64,12 +65,13 @@ robolectric_binary("touch_to_fill_junit_tests") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
"//chrome/android:chrome_test_util_java",
"//chrome/browser/flags:java",
+ "//chrome/browser/password_manager/android:public_impl_java",
"//chrome/browser/touch_to_fill/android:public_java",
"//chrome/browser/touch_to_fill/android/internal:java",
- "//chrome/browser/touch_to_fill/android/internal:resource_provider_public_impl_java",
"//chrome/browser/touch_to_fill/common/android:java",
"//chrome/browser/ui/android/favicon:java",
"//chrome/test/android:chrome_java_unit_test_support",
@@ -107,6 +109,8 @@ android_library("test_java") {
"//chrome/android:chrome_test_java",
"//chrome/android:chrome_test_util_java",
"//chrome/browser/flags:java",
+ "//chrome/browser/password_manager/android:password_manager_resource_provider_java",
+ "//chrome/browser/password_manager/android:public_impl_java",
"//chrome/browser/touch_to_fill/android/internal:java",
"//chrome/browser/touch_to_fill/android/internal:java_resources",
"//chrome/browser/touch_to_fill/android/internal:resource_provider_public_impl_java",
@@ -117,6 +121,7 @@ android_library("test_java") {
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/bottomsheet/android:java_resources",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/browser_ui/bottomsheet/android/test:java",
"//components/password_manager/core/browser:password_manager_java_enums",
"//components/url_formatter/android:url_formatter_java",
@@ -127,6 +132,7 @@ android_library("test_java") {
"//third_party/hamcrest:hamcrest_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
+ "//ui/accessibility:ax_base_java",
"//ui/android:ui_full_java",
"//ui/android:ui_java_test_support",
"//ui/android:ui_utils_java",
diff --git a/chromium/chrome/browser/touch_to_fill/android/internal/BUILD.gn b/chromium/chrome/browser/touch_to_fill/android/internal/BUILD.gn
index 19b627068ee..12afd9126ea 100644
--- a/chromium/chrome/browser/touch_to_fill/android/internal/BUILD.gn
+++ b/chromium/chrome/browser/touch_to_fill/android/internal/BUILD.gn
@@ -29,6 +29,7 @@ android_library("java") {
"//chrome/browser/ui/android/favicon:java",
"//chrome/browser/util:java",
"//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/widget/android:java",
"//components/embedder_support/android:util_java",
"//components/favicon/android:java",
"//components/password_manager/core/browser:password_manager_java_enums",
@@ -72,13 +73,9 @@ android_resources("java_resources") {
"java/res/drawable-night/touch_to_fill_header_image.xml",
"java/res/drawable/touch_to_fill_header_image.xml",
"java/res/layout/touch_to_fill_credential_item.xml",
- "java/res/layout/touch_to_fill_credential_item_modern.xml",
- "java/res/layout/touch_to_fill_fill_button.xml",
"java/res/layout/touch_to_fill_footer_item.xml",
"java/res/layout/touch_to_fill_header_item.xml",
- "java/res/layout/touch_to_fill_header_item_modern.xml",
"java/res/layout/touch_to_fill_webauthn_credential_item.xml",
- "java/res/layout/touch_to_fill_webauthn_credential_item_modern.xml",
"java/res/values/dimens.xml",
]
}
diff --git a/chromium/chrome/browser/touch_to_fill/common/android/BUILD.gn b/chromium/chrome/browser/touch_to_fill/common/android/BUILD.gn
index 3b9307e057e..6289706d313 100644
--- a/chromium/chrome/browser/touch_to_fill/common/android/BUILD.gn
+++ b/chromium/chrome/browser/touch_to_fill/common/android/BUILD.gn
@@ -12,7 +12,7 @@ android_resources("java_resources") {
"java/res/drawable/touch_to_fill_credential_background_modern_rounded_all.xml",
"java/res/drawable/touch_to_fill_credential_background_modern_rounded_down.xml",
"java/res/drawable/touch_to_fill_credential_background_modern_rounded_up.xml",
- "java/res/layout/touch_to_fill_fill_button_modern.xml",
+ "java/res/layout/touch_to_fill_fill_button.xml",
"java/res/layout/touch_to_fill_sheet.xml",
"java/res/values/dimens.xml",
]
@@ -31,6 +31,7 @@ android_library("java") {
"//third_party/androidx:androidx_annotation_annotation_jvm_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
+ "//ui/accessibility:ax_base_java",
"//ui/android:ui_java",
]
@@ -49,6 +50,7 @@ robolectric_library("junit") {
deps = [
":java",
+ "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//chrome/browser/tab:java",
diff --git a/chromium/chrome/browser/touch_to_fill/password_generation/android/BUILD.gn b/chromium/chrome/browser/touch_to_fill/password_generation/android/BUILD.gn
index 01c23b0f6f6..328c63ab124 100644
--- a/chromium/chrome/browser/touch_to_fill/password_generation/android/BUILD.gn
+++ b/chromium/chrome/browser/touch_to_fill/password_generation/android/BUILD.gn
@@ -8,7 +8,10 @@ source_set("public") {
deps = [
":android",
"//base",
+ "//chrome/browser/autofill:autofill",
+ "//chrome/browser/password_manager/android:password_generation_utils",
"//components/password_manager/content/browser:browser",
+ "//content/public/browser",
]
sources = [
@@ -28,7 +31,7 @@ source_set("android") {
deps = [
"//chrome/browser/touch_to_fill/password_generation/android/internal:jni",
"//components/password_manager/core/common:features",
- "//content/public/browser:browser",
+ "//content/public/browser",
"//ui/android:android",
"//ui/gfx:native_widget_types",
]
@@ -60,6 +63,7 @@ source_set("unit_tests") {
":public",
":test_support",
"//base",
+ "//chrome/browser/password_manager/android:password_generation_utils",
"//chrome/test:test_support",
"//components/password_manager/content/browser:browser",
"//content/test:test_support",
diff --git a/chromium/chrome/browser/touch_to_fill/password_generation/android/internal/BUILD.gn b/chromium/chrome/browser/touch_to_fill/password_generation/android/internal/BUILD.gn
index 0421b90aefa..2e909463dd1 100644
--- a/chromium/chrome/browser/touch_to_fill/password_generation/android/internal/BUILD.gn
+++ b/chromium/chrome/browser/touch_to_fill/password_generation/android/internal/BUILD.gn
@@ -21,6 +21,7 @@ android_library("java") {
"//chrome/browser/ui/android/strings:ui_strings_grd",
"//components/browser_ui/bottomsheet/android:java",
"//components/signin/public/android:java",
+ "//content/public/android:content_full_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
@@ -50,19 +51,31 @@ robolectric_library("junit") {
resources_package =
"org.chromium.chrome.browser.touch_to_fill.password_generation"
- sources = [ "java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java" ]
+ sources = [
+ "java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridgeTest.java",
+ "java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java",
+ ]
deps = [
":java",
+ ":java_resources",
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
+ "//chrome/browser/flags:java",
"//chrome/browser/sync/android:java",
"//chrome/test/android:chrome_java_unit_test_support",
"//components/browser_ui/bottomsheet/android:java",
- "//components/signin/public/android:java",
+ "//content/public/android:content_full_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
+ "//third_party/androidx:androidx_test_core_java",
+ "//third_party/androidx:androidx_test_ext_junit_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
+ "//ui/android:ui_java_test_support",
+ "//ui/android:ui_no_recycler_view_java",
+ "//ui/android:ui_utils_java",
]
}
@@ -85,13 +98,16 @@ android_library("javatests") {
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/bottomsheet/android:java_resources",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/browser_ui/bottomsheet/android/test:java",
"//components/signin/public/android:java",
+ "//content/public/android:content_full_java",
"//content/public/test/android:content_java_test_support",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_java_test_support",
"//ui/android:ui_no_recycler_view_java",
+ "//ui/android:ui_utils_java",
]
}
diff --git a/chromium/chrome/browser/touch_to_fill/payments/android/internal/BUILD.gn b/chromium/chrome/browser/touch_to_fill/payments/android/internal/BUILD.gn
index b402c8fb73b..6629c40d57f 100644
--- a/chromium/chrome/browser/touch_to_fill/payments/android/internal/BUILD.gn
+++ b/chromium/chrome/browser/touch_to_fill/payments/android/internal/BUILD.gn
@@ -81,6 +81,7 @@ robolectric_library("junit") {
"//components/autofill/android:autofill_features_java",
"//components/browser_ui/bottomsheet/android:java",
"//third_party/android_deps:espresso_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
@@ -119,15 +120,18 @@ android_library("javatests") {
"//components/autofill/android:main_autofill_java",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/bottomsheet/android:java_resources",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/browser_ui/bottomsheet/android/test:java",
"//components/strings:components_strings_grd",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
+ "//ui/accessibility:ax_base_java",
"//ui/android:ui_java_test_support",
"//ui/android:ui_no_recycler_view_java",
]
diff --git a/chromium/chrome/browser/ui/BUILD.gn b/chromium/chrome/browser/ui/BUILD.gn
index d7bb16c2883..aaaab0d2783 100644
--- a/chromium/chrome/browser/ui/BUILD.gn
+++ b/chromium/chrome/browser/ui/BUILD.gn
@@ -30,110 +30,6 @@ import("//third_party/protobuf/proto_library.gni")
import("//ui/base/ui_features.gni")
import("//ui/views/features.gni")
-if (is_mac) {
- # TODO(https://crbug.com/1280317): Merge back into the `ui` target once all
- # .mm files are ARCed.
- source_set("ui_arc") {
- sources = [
- "autofill/autofill_popup_controller_impl_mac.h",
- "autofill/autofill_popup_controller_impl_mac.mm",
- "browser_commands_mac.h",
- "browser_commands_mac.mm",
- "cocoa/applescript/apple_event_util.h",
- "cocoa/applescript/apple_event_util.mm",
- "cocoa/applescript/bookmark_folder_applescript.h",
- "cocoa/applescript/bookmark_folder_applescript.mm",
- "cocoa/applescript/bookmark_item_applescript.h",
- "cocoa/applescript/bookmark_item_applescript.mm",
- "cocoa/applescript/bookmark_node_applescript.h",
- "cocoa/applescript/bookmark_node_applescript.mm",
- "cocoa/applescript/browsercrapplication+applescript.h",
- "cocoa/applescript/browsercrapplication+applescript.mm",
- "cocoa/applescript/constants_applescript.h",
- "cocoa/applescript/constants_applescript.mm",
- "cocoa/applescript/element_applescript.h",
- "cocoa/applescript/element_applescript.mm",
- "cocoa/applescript/error_applescript.h",
- "cocoa/applescript/error_applescript.mm",
- "cocoa/applescript/tab_applescript.h",
- "cocoa/applescript/tab_applescript.mm",
- "cocoa/applescript/window_applescript.h",
- "cocoa/applescript/window_applescript.mm",
- "cocoa/confirm_quit_panel_controller.h",
- "cocoa/confirm_quit_panel_controller.mm",
- "cocoa/first_run_dialog_cocoa.h",
- "cocoa/first_run_dialog_cocoa.mm",
- "cocoa/first_run_dialog_controller.h",
- "cocoa/first_run_dialog_controller.mm",
- "cocoa/main_menu_builder.h",
- "cocoa/main_menu_builder.mm",
- "cocoa/scoped_menu_bar_lock.h",
- "cocoa/scoped_menu_bar_lock.mm",
- "cocoa/screentime/fake_webpage_controller.h",
- "cocoa/screentime/fake_webpage_controller.mm",
- "cocoa/screentime/history_bridge.h",
- "cocoa/screentime/history_bridge.mm",
- "cocoa/screentime/history_bridge_factory.h",
- "cocoa/screentime/history_bridge_factory.mm",
- "cocoa/screentime/history_deleter.h",
- "cocoa/screentime/history_deleter_impl.h",
- "cocoa/screentime/history_deleter_impl.mm",
- "cocoa/screentime/tab_helper.h",
- "cocoa/screentime/tab_helper.mm",
- "cocoa/screentime/webpage_controller.h",
- "cocoa/screentime/webpage_controller_impl.h",
- "cocoa/screentime/webpage_controller_impl.mm",
- "cocoa/share_menu_controller.h",
- "cocoa/share_menu_controller.mm",
- "cocoa/simple_message_box_cocoa.h",
- "cocoa/simple_message_box_cocoa.mm",
- "cocoa/status_icons/status_icon_mac.h",
- "cocoa/status_icons/status_icon_mac.mm",
- "cocoa/status_icons/status_tray_mac.h",
- "cocoa/status_icons/status_tray_mac.mm",
- "cocoa/touchbar/browser_window_default_touch_bar.h",
- "cocoa/touchbar/browser_window_default_touch_bar.mm",
- "cocoa/touchbar/browser_window_touch_bar_controller.h",
- "cocoa/touchbar/browser_window_touch_bar_controller.mm",
- "cocoa/touchbar/credit_card_autofill_touch_bar_controller.h",
- "cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm",
- "cocoa/touchbar/web_textfield_touch_bar_controller.h",
- "cocoa/touchbar/web_textfield_touch_bar_controller.mm",
- "webui/settings/settings_utils_mac.mm",
- ]
-
- configs += [ "//build/config/compiler:enable_arc" ]
- deps = [
- "//base",
- "//chrome/app:command_ids",
- "//chrome/app:generated_resources",
- "//chrome/app/vector_icons",
- "//chrome/browser:browser_process",
- "//chrome/browser/autofill",
- "//chrome/browser/profiles:profile",
- "//chrome/browser/ui/tabs:tab_enums",
- "//chrome/common",
- "//chrome/common:constants",
- "//components/bookmarks/browser",
- "//components/bookmarks/common",
- "//components/omnibox/browser:location_bar",
- "//components/omnibox/browser:vector_icons",
- "//components/prefs",
- "//components/search_engines",
- "//components/sessions:session_id",
- "//components/sessions:sessions",
- "//components/startup_metric_utils/browser",
- "//components/vector_icons",
- "//content/public/browser",
- "//skia",
- "//ui/base:base",
- "//ui/snapshot",
- "//ui/views",
- "//url",
- ]
- }
-}
-
# Use a static library here because many test binaries depend on this but don't
# require many files from it. This makes linking more efficient.
static_library("ui") {
@@ -219,6 +115,8 @@ static_library("ui") {
"find_bar/find_bar_state.h",
"find_bar/find_bar_state_factory.cc",
"find_bar/find_bar_state_factory.h",
+ "hats/survey_config.cc",
+ "hats/survey_config.h",
"idle_bubble.h",
"interventions/intervention_delegate.h",
"interventions/intervention_infobar_delegate.cc",
@@ -261,13 +159,15 @@ static_library("ui") {
"passwords/ui_utils.h",
"passwords/well_known_change_password_navigation_throttle.cc",
"passwords/well_known_change_password_navigation_throttle.h",
+ "plus_addresses/plus_address_creation_controller.h",
+ "plus_addresses/plus_address_creation_controller_impl.cc",
+ "plus_addresses/plus_address_creation_controller_impl.h",
"prefs/pref_watcher.cc",
"prefs/pref_watcher.h",
"prefs/prefs_tab_helper.cc",
"prefs/prefs_tab_helper.h",
- "profile_chooser_constants.h",
- "profile_error_dialog.cc",
- "profile_error_dialog.h",
+ "profiles/profile_error_dialog.cc",
+ "profiles/profile_error_dialog.h",
"recently_audible_helper.cc",
"recently_audible_helper.h",
"screen_capture_notification_ui.h",
@@ -285,6 +185,8 @@ static_library("ui") {
"simple_message_box_internal.h",
"startup/bad_flags_prompt.cc",
"startup/bad_flags_prompt.h",
+ "startup/bidding_and_auction_consented_debugging_infobar_delegate.cc",
+ "startup/bidding_and_auction_consented_debugging_infobar_delegate.h",
"status_bubble.h",
"storage_pressure_bubble.h",
"sync/tab_contents_synced_tab_delegate.cc",
@@ -379,6 +281,8 @@ static_library("ui") {
"webui/invalidations/invalidations_ui.h",
"webui/local_state/local_state_ui.cc",
"webui/local_state/local_state_ui.h",
+ "webui/location_internals/location_internals_handler.cc",
+ "webui/location_internals/location_internals_handler.h",
"webui/location_internals/location_internals_ui.cc",
"webui/location_internals/location_internals_ui.h",
"webui/log_web_ui_url.cc",
@@ -485,6 +389,7 @@ static_library("ui") {
# browser, then we can clean up these dependencies.
public_deps = [
"//chrome/browser/headless",
+ "//components/cross_device/logging",
"//components/dom_distiller/core",
"//components/paint_preview/buildflags",
"//components/safe_browsing:buildflags",
@@ -517,7 +422,6 @@ static_library("ui") {
"//chrome/browser/autofill",
"//chrome/browser/bitmap_fetcher",
"//chrome/browser/breadcrumbs",
- "//chrome/browser/chrome_for_testing:buildflags",
"//chrome/browser/companion/core",
"//chrome/browser/devtools",
"//chrome/browser/favicon",
@@ -537,6 +441,7 @@ static_library("ui") {
"//chrome/browser/storage_access_api",
"//chrome/browser/ui/side_panel:side_panel_enums",
"//chrome/browser/ui/webui:configs",
+ "//chrome/browser/ui/webui/location_internals:mojo_bindings",
"//chrome/browser/ui/webui/omnibox:mojo_bindings",
"//chrome/browser/ui/webui/segmentation_internals:mojo_bindings",
"//chrome/browser/ui/webui/suggest_internals:mojo_bindings",
@@ -552,6 +457,7 @@ static_library("ui") {
"//components/about_ui",
"//components/access_code_cast/common:metrics",
"//components/account_id",
+ "//components/autofill/content/browser",
"//components/autofill/content/browser:risk_proto",
"//components/autofill/core/browser",
"//components/blocked_content",
@@ -608,6 +514,7 @@ static_library("ui") {
"//components/history/core/common",
"//components/history_clusters/core",
"//components/history_clusters/history_clusters_internals/webui",
+ "//components/history_clusters/history_clusters_internals/webui:constants",
"//components/history_clusters/history_clusters_internals/webui:mojo_bindings",
"//components/history_clusters/ui",
"//components/image_fetcher/core",
@@ -645,6 +552,7 @@ static_library("ui") {
"//components/password_manager/core/browser",
"//components/password_manager/core/browser:affiliation",
"//components/password_manager/core/browser:import_results",
+ "//components/password_manager/core/browser/features:password_features",
"//components/password_manager/core/browser/import:csv",
"//components/password_manager/core/browser/import:importer",
"//components/password_manager/core/common",
@@ -653,6 +561,8 @@ static_library("ui") {
"//components/payments/core:error_strings",
"//components/performance_manager",
"//components/permissions",
+ "//components/plus_addresses",
+ "//components/policy/content",
"//components/policy/core/browser",
"//components/pref_registry",
"//components/privacy_sandbox",
@@ -670,6 +580,7 @@ static_library("ui") {
"//components/safe_browsing/content/browser/web_ui",
"//components/safe_browsing/core/browser/db:database_manager",
"//components/safe_browsing/core/browser/db:util",
+ "//components/safe_browsing/core/browser/hashprefix_realtime:hash_realtime_utils",
"//components/safe_browsing/core/browser/password_protection:password_protection_metrics_util",
"//components/safe_browsing/core/browser/tailored_security_service",
"//components/safe_browsing/core/common",
@@ -693,7 +604,7 @@ static_library("ui") {
"//components/site_engagement/core/mojom:mojo_bindings",
"//components/spellcheck/browser",
"//components/ssl_errors",
- "//components/startup_metric_utils/browser",
+ "//components/startup_metric_utils",
"//components/strings",
"//components/subresource_filter/content/browser",
"//components/subresource_filter/core/browser",
@@ -818,21 +729,25 @@ static_library("ui") {
]
}
- if (enable_waffle_desktop) {
- deps += [ "//chrome/browser/ui/webui/waffle:mojo_bindings" ]
+ if (enable_search_engine_choice) {
+ deps += [ "//chrome/browser/ui/webui/search_engine_choice:mojo_bindings" ]
sources += [
- "views/waffle/waffle_dialog_view.cc",
- "views/waffle/waffle_dialog_view.h",
- "waffle/waffle_tab_helper.cc",
- "waffle/waffle_tab_helper.h",
- "webui/waffle/waffle_handler.cc",
- "webui/waffle/waffle_handler.h",
- "webui/waffle/waffle_ui.cc",
- "webui/waffle/waffle_ui.h",
+ "search_engine_choice/search_engine_choice_tab_helper.cc",
+ "search_engine_choice/search_engine_choice_tab_helper.h",
+ "views/search_engine_choice/search_engine_choice_dialog_view.cc",
+ "views/search_engine_choice/search_engine_choice_dialog_view.h",
+ "webui/search_engine_choice/search_engine_choice_handler.cc",
+ "webui/search_engine_choice/search_engine_choice_handler.h",
+ "webui/search_engine_choice/search_engine_choice_ui.cc",
+ "webui/search_engine_choice/search_engine_choice_ui.h",
]
}
if (is_chromeos) {
+ sources += [
+ "webui/trusted_vault/trusted_vault_dialog_delegate.cc",
+ "webui/trusted_vault/trusted_vault_dialog_delegate.h",
+ ]
deps += [
"//chromeos/components/kiosk",
"//chromeos/constants",
@@ -863,6 +778,10 @@ static_library("ui") {
"android/autofill/autofill_popup_view_android.h",
"android/autofill/autofill_progress_dialog_view_android.cc",
"android/autofill/autofill_progress_dialog_view_android.h",
+ "android/autofill/autofill_save_card_bottom_sheet_bridge.cc",
+ "android/autofill/autofill_save_card_bottom_sheet_bridge.h",
+ "android/autofill/autofill_vcn_enroll_bottom_sheet_bridge.cc",
+ "android/autofill/autofill_vcn_enroll_bottom_sheet_bridge.h",
"android/autofill/card_expiration_date_fix_flow_view_android.cc",
"android/autofill/card_expiration_date_fix_flow_view_android.h",
"android/autofill/card_name_fix_flow_view_android.cc",
@@ -896,6 +815,8 @@ static_library("ui") {
"android/fast_checkout/fast_checkout_view_impl.h",
"android/fast_checkout/ui_view_android_utils.cc",
"android/fast_checkout/ui_view_android_utils.h",
+ "android/hats/survey_config_android.cc",
+ "android/hats/survey_config_android.h",
"android/infobars/autofill_credit_card_filling_infobar.cc",
"android/infobars/autofill_credit_card_filling_infobar.h",
"android/infobars/autofill_offer_notification_infobar.cc",
@@ -922,6 +843,7 @@ static_library("ui") {
"android/omnibox/omnibox_view_util.h",
"android/overlay/overlay_window_android.cc",
"android/overlay/overlay_window_android.h",
+ "android/page_insights/page_insights_swaa_checker.cc",
"android/passwords/all_passwords_bottom_sheet_view.h",
"android/passwords/all_passwords_bottom_sheet_view_impl.cc",
"android/passwords/all_passwords_bottom_sheet_view_impl.h",
@@ -1024,8 +946,11 @@ static_library("ui") {
"//chrome/browser/notifications/scheduler/public",
"//chrome/browser/touch_to_fill/payments/android",
"//chrome/browser/ui/android/autofill/internal:jni_headers",
+ "//chrome/browser/ui/android/page_insights:jni_headers",
"//chrome/browser/ui/android/toolbar:jni_headers",
"//chrome/browser/ui/webui/feed_internals:mojo_bindings",
+ "//components/autofill/android:payments_autofill_cc",
+ "//components/autofill/android:payments_jni_headers",
"//components/browser_ui/accessibility/android",
"//components/browser_ui/client_certificate/android",
"//components/browser_ui/share/android",
@@ -1329,8 +1254,8 @@ static_library("ui") {
"passwords/bubble_controllers/auto_sign_in_bubble_controller.h",
"passwords/bubble_controllers/generation_confirmation_bubble_controller.cc",
"passwords/bubble_controllers/generation_confirmation_bubble_controller.h",
- "passwords/bubble_controllers/items_bubble_controller.cc",
- "passwords/bubble_controllers/items_bubble_controller.h",
+ "passwords/bubble_controllers/manage_passwords_bubble_controller.cc",
+ "passwords/bubble_controllers/manage_passwords_bubble_controller.h",
"passwords/bubble_controllers/move_to_account_store_bubble_controller.cc",
"passwords/bubble_controllers/move_to_account_store_bubble_controller.h",
"passwords/bubble_controllers/password_bubble_controller_base.cc",
@@ -1341,6 +1266,8 @@ static_library("ui") {
"passwords/bubble_controllers/save_unsynced_credentials_locally_bubble_controller.h",
"passwords/bubble_controllers/save_update_bubble_controller.cc",
"passwords/bubble_controllers/save_update_bubble_controller.h",
+ "passwords/bubble_controllers/shared_passwords_notifications_bubble_controller.cc",
+ "passwords/bubble_controllers/shared_passwords_notifications_bubble_controller.h",
"passwords/credential_leak_dialog_controller.h",
"passwords/credential_leak_dialog_controller_impl.cc",
"passwords/credential_leak_dialog_controller_impl.h",
@@ -1383,13 +1310,31 @@ static_library("ui") {
"privacy_sandbox/privacy_sandbox_prompt.h",
"privacy_sandbox/privacy_sandbox_prompt_helper.cc",
"privacy_sandbox/privacy_sandbox_prompt_helper.h",
- "profile_view_utils.cc",
- "profile_view_utils.h",
+ "profiles/profile_colors_util.cc",
+ "profiles/profile_colors_util.h",
+ "profiles/profile_view_utils.cc",
+ "profiles/profile_view_utils.h",
"sad_tab.cc",
"sad_tab.h",
"sad_tab_helper.cc",
"sad_tab_helper.h",
"sad_tab_types.h",
+ "safety_hub/notification_permission_review_service.cc",
+ "safety_hub/notification_permission_review_service.h",
+ "safety_hub/notification_permission_review_service_factory.cc",
+ "safety_hub/notification_permission_review_service_factory.h",
+ "safety_hub/password_status_check_service.cc",
+ "safety_hub/password_status_check_service.h",
+ "safety_hub/password_status_check_service_factory.cc",
+ "safety_hub/password_status_check_service_factory.h",
+ "safety_hub/safety_hub_prefs.cc",
+ "safety_hub/safety_hub_prefs.h",
+ "safety_hub/safety_hub_service.cc",
+ "safety_hub/safety_hub_service.h",
+ "safety_hub/unused_site_permissions_service.cc",
+ "safety_hub/unused_site_permissions_service.h",
+ "safety_hub/unused_site_permissions_service_factory.cc",
+ "safety_hub/unused_site_permissions_service_factory.h",
"scoped_tabbed_browser_displayer.cc",
"scoped_tabbed_browser_displayer.h",
"search/instant_controller.cc",
@@ -1413,8 +1358,6 @@ static_library("ui") {
"serial/serial_chooser_controller.h",
"shared_highlighting/shared_highlighting_promo.cc",
"shared_highlighting/shared_highlighting_promo.h",
- "signin/profile_colors_util.cc",
- "signin/profile_colors_util.h",
"singleton_tabs.cc",
"singleton_tabs.h",
"startup/automation_infobar_delegate.cc",
@@ -1462,6 +1405,8 @@ static_library("ui") {
"tabs/existing_window_sub_menu_model.h",
"tabs/hover_tab_selector.cc",
"tabs/hover_tab_selector.h",
+ "tabs/organization/metrics.cc",
+ "tabs/organization/metrics.h",
"tabs/pinned_tab_codec.cc",
"tabs/pinned_tab_codec.h",
"tabs/pinned_tab_service.cc",
@@ -1565,24 +1510,16 @@ static_library("ui") {
"unload_controller.h",
"user_education/browser_feature_promo_snooze_service.cc",
"user_education/browser_feature_promo_snooze_service.h",
- "user_education/browser_tutorial_service.cc",
- "user_education/browser_tutorial_service.h",
- "user_education/open_page_and_show_help_bubble.cc",
- "user_education/open_page_and_show_help_bubble.h",
"user_education/scoped_new_badge_tracker.cc",
"user_education/scoped_new_badge_tracker.h",
- "user_education/user_education_service.cc",
- "user_education/user_education_service.h",
- "user_education/user_education_service_factory.cc",
- "user_education/user_education_service_factory.h",
+ "user_education/show_promo_in_page.cc",
+ "user_education/show_promo_in_page.h",
"user_notes/user_notes_controller.cc",
"user_notes/user_notes_controller.h",
"views/eye_dropper/eye_dropper.cc",
"views/eye_dropper/eye_dropper.h",
"views/eye_dropper/eye_dropper_view.cc",
"views/eye_dropper/eye_dropper_view.h",
- "views/eye_dropper/eye_dropper_view_mac.h",
- "views/eye_dropper/eye_dropper_view_mac.mm",
"webui/access_code_cast/access_code_cast_dialog.cc",
"webui/access_code_cast/access_code_cast_dialog.h",
"webui/access_code_cast/access_code_cast_handler.cc",
@@ -1623,6 +1560,10 @@ static_library("ui") {
"webui/cr_components/history_clusters/history_clusters_util.h",
"webui/cr_components/most_visited/most_visited_handler.cc",
"webui/cr_components/most_visited/most_visited_handler.h",
+ "webui/cr_components/theme_color_picker/customize_chrome_colors.cc",
+ "webui/cr_components/theme_color_picker/customize_chrome_colors.h",
+ "webui/cr_components/theme_color_picker/theme_color_picker_handler.cc",
+ "webui/cr_components/theme_color_picker/theme_color_picker_handler.h",
"webui/customize_themes/chrome_customize_themes_handler.cc",
"webui/customize_themes/chrome_customize_themes_handler.h",
"webui/devtools_ui.cc",
@@ -1657,6 +1598,8 @@ static_library("ui") {
"webui/feedback/feedback_handler.h",
"webui/feedback/feedback_ui.cc",
"webui/feedback/feedback_ui.h",
+ "webui/hats/hats_ui.cc",
+ "webui/hats/hats_ui.h",
"webui/help/version_updater.h",
"webui/history/browsing_history_handler.cc",
"webui/history/browsing_history_handler.h",
@@ -1825,20 +1768,18 @@ static_library("ui") {
"webui/side_panel/companion/companion_side_panel_untrusted_ui.h",
"webui/side_panel/companion/signin_delegate_impl.cc",
"webui/side_panel/companion/signin_delegate_impl.h",
- "webui/side_panel/customize_chrome/customize_chrome_colors.cc",
- "webui/side_panel/customize_chrome/customize_chrome_colors.h",
"webui/side_panel/customize_chrome/customize_chrome_page_handler.cc",
"webui/side_panel/customize_chrome/customize_chrome_page_handler.h",
"webui/side_panel/customize_chrome/customize_chrome_ui.cc",
"webui/side_panel/customize_chrome/customize_chrome_ui.h",
"webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc",
"webui/side_panel/history_clusters/history_clusters_side_panel_ui.h",
- "webui/side_panel/read_anything/read_anything_page_handler.cc",
- "webui/side_panel/read_anything/read_anything_page_handler.h",
"webui/side_panel/read_anything/read_anything_prefs.cc",
"webui/side_panel/read_anything/read_anything_prefs.h",
- "webui/side_panel/read_anything/read_anything_ui.cc",
- "webui/side_panel/read_anything/read_anything_ui.h",
+ "webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc",
+ "webui/side_panel/read_anything/read_anything_untrusted_page_handler.h",
+ "webui/side_panel/read_anything/read_anything_untrusted_ui.cc",
+ "webui/side_panel/read_anything/read_anything_untrusted_ui.h",
"webui/side_panel/reading_list/reading_list_page_handler.cc",
"webui/side_panel/reading_list/reading_list_page_handler.h",
"webui/side_panel/reading_list/reading_list_ui.cc",
@@ -1943,8 +1884,10 @@ static_library("ui") {
"//chrome/browser/new_tab_page/modules/feed:mojo_bindings",
"//chrome/browser/new_tab_page/modules/history_clusters:mojo_bindings",
"//chrome/browser/new_tab_page/modules/history_clusters/cart:mojo_bindings",
+ "//chrome/browser/new_tab_page/modules/history_clusters/discount:mojo_bindings",
"//chrome/browser/new_tab_page/modules/photos:mojo_bindings",
"//chrome/browser/new_tab_page/modules/recipes:mojo_bindings",
+ "//chrome/browser/new_tab_page/modules/v2/history_clusters:mojo_bindings",
"//chrome/browser/profile_resetter:profile_reset_report_proto",
"//chrome/browser/safe_browsing",
"//chrome/browser/safe_browsing:advanced_protection",
@@ -1979,6 +1922,7 @@ static_library("ui") {
"//components/commerce/core:shopping_service",
"//components/commerce/core/mojom:mojo_bindings",
"//components/commerce/core/webui",
+ "//components/endpoint_fetcher:endpoint_fetcher",
"//components/enterprise/common:files_scan_data",
"//components/feedback/proto",
"//components/headless/policy",
@@ -2020,6 +1964,7 @@ static_library("ui") {
"//third_party/libaddressinput:strings",
"//ui/base/dragdrop:types",
"//ui/base/dragdrop/mojom",
+ "//ui/color:color_provider_key",
"//ui/color/dynamic_color",
"//ui/events",
"//ui/webui/resources/cr_components/app_management:mojo_bindings",
@@ -2130,10 +2075,20 @@ static_library("ui") {
"../ash/app_list/app_service/app_service_app_model_builder.h",
"../ash/app_list/app_service/app_service_context_menu.cc",
"../ash/app_list/app_service/app_service_context_menu.h",
+ "../ash/app_list/app_service/app_service_promise_app_context_menu.cc",
+ "../ash/app_list/app_service/app_service_promise_app_context_menu.h",
+ "../ash/app_list/app_service/app_service_promise_app_icon_loader.cc",
+ "../ash/app_list/app_service/app_service_promise_app_icon_loader.h",
"../ash/app_list/app_service/app_service_promise_app_item.cc",
"../ash/app_list/app_service/app_service_promise_app_item.h",
"../ash/app_list/app_service/app_service_promise_app_model_builder.cc",
"../ash/app_list/app_service/app_service_promise_app_model_builder.h",
+ "../ash/app_list/app_service/app_service_shortcut_context_menu.cc",
+ "../ash/app_list/app_service/app_service_shortcut_context_menu.h",
+ "../ash/app_list/app_service/app_service_shortcut_item.cc",
+ "../ash/app_list/app_service/app_service_shortcut_item.h",
+ "../ash/app_list/app_service/app_service_shortcut_model_builder.cc",
+ "../ash/app_list/app_service/app_service_shortcut_model_builder.h",
"../ash/app_list/app_sync_ui_state.cc",
"../ash/app_list/app_sync_ui_state.h",
"../ash/app_list/app_sync_ui_state_factory.cc",
@@ -2280,16 +2235,28 @@ static_library("ui") {
"../ash/app_list/search/keyboard_shortcut_provider.h",
"../ash/app_list/search/keyboard_shortcut_result.cc",
"../ash/app_list/search/keyboard_shortcut_result.h",
- "../ash/app_list/search/local_images/annotation_storage.cc",
- "../ash/app_list/search/local_images/annotation_storage.h",
- "../ash/app_list/search/local_images/image_annotation_worker.cc",
- "../ash/app_list/search/local_images/image_annotation_worker.h",
- "../ash/app_list/search/local_images/local_image_search_provider.cc",
- "../ash/app_list/search/local_images/local_image_search_provider.h",
- "../ash/app_list/search/local_images/search_utils.cc",
- "../ash/app_list/search/local_images/search_utils.h",
- "../ash/app_list/search/local_images/sql_database.cc",
- "../ash/app_list/search/local_images/sql_database.h",
+ "../ash/app_list/search/local_image_search/annotation_storage.cc",
+ "../ash/app_list/search/local_image_search/annotation_storage.h",
+ "../ash/app_list/search/local_image_search/annotations_table.cc",
+ "../ash/app_list/search/local_image_search/annotations_table.h",
+ "../ash/app_list/search/local_image_search/documents_table.cc",
+ "../ash/app_list/search/local_image_search/documents_table.h",
+ "../ash/app_list/search/local_image_search/file_search_result.cc",
+ "../ash/app_list/search/local_image_search/file_search_result.h",
+ "../ash/app_list/search/local_image_search/image_annotation_worker.cc",
+ "../ash/app_list/search/local_image_search/image_annotation_worker.h",
+ "../ash/app_list/search/local_image_search/inverted_index_table.cc",
+ "../ash/app_list/search/local_image_search/inverted_index_table.h",
+ "../ash/app_list/search/local_image_search/local_image_search_provider.cc",
+ "../ash/app_list/search/local_image_search/local_image_search_provider.h",
+ "../ash/app_list/search/local_image_search/local_image_search_service.cc",
+ "../ash/app_list/search/local_image_search/local_image_search_service.h",
+ "../ash/app_list/search/local_image_search/local_image_search_service_factory.cc",
+ "../ash/app_list/search/local_image_search/local_image_search_service_factory.h",
+ "../ash/app_list/search/local_image_search/search_utils.cc",
+ "../ash/app_list/search/local_image_search/search_utils.h",
+ "../ash/app_list/search/local_image_search/sql_database.cc",
+ "../ash/app_list/search/local_image_search/sql_database.h",
"../ash/app_list/search/omnibox/omnibox_answer_result.cc",
"../ash/app_list/search/omnibox/omnibox_answer_result.h",
"../ash/app_list/search/omnibox/omnibox_lacros_provider.cc",
@@ -2452,6 +2419,8 @@ static_library("ui") {
"ash/capture_mode/recording_overlay_view_impl.h",
"ash/cast_config_controller_media_router.cc",
"ash/cast_config_controller_media_router.h",
+ "ash/chrome_accelerator_prefs_delegate.cc",
+ "ash/chrome_accelerator_prefs_delegate.h",
"ash/chrome_accessibility_delegate.cc",
"ash/chrome_accessibility_delegate.h",
"ash/chrome_browser_main_extra_parts_ash.cc",
@@ -2462,6 +2431,8 @@ static_library("ui") {
"ash/chrome_new_window_delegate_provider.h",
"ash/chrome_shell_delegate.cc",
"ash/chrome_shell_delegate.h",
+ "ash/clipboard_history_url_title_fetcher_impl.cc",
+ "ash/clipboard_history_url_title_fetcher_impl.h",
"ash/clipboard_image_model_factory_impl.cc",
"ash/clipboard_image_model_factory_impl.h",
"ash/clipboard_image_model_request.cc",
@@ -2494,6 +2465,8 @@ static_library("ui") {
"ash/glanceables/chrome_glanceables_delegate.h",
"ash/glanceables/glanceables_classroom_client_impl.cc",
"ash/glanceables/glanceables_classroom_client_impl.h",
+ "ash/glanceables/glanceables_classroom_course_work_item.cc",
+ "ash/glanceables/glanceables_classroom_course_work_item.h",
"ash/glanceables/glanceables_keyed_service.cc",
"ash/glanceables/glanceables_keyed_service.h",
"ash/glanceables/glanceables_keyed_service_factory.cc",
@@ -2625,12 +2598,22 @@ static_library("ui") {
"ash/shelf/app_service/app_service_app_window_shelf_item_controller.h",
"ash/shelf/app_service/app_service_instance_registry_helper.cc",
"ash/shelf/app_service/app_service_instance_registry_helper.h",
+ "ash/shelf/app_service/app_service_promise_app_shelf_context_menu.cc",
+ "ash/shelf/app_service/app_service_promise_app_shelf_context_menu.h",
"ash/shelf/app_service/app_service_shelf_context_menu.cc",
"ash/shelf/app_service/app_service_shelf_context_menu.h",
+ "ash/shelf/app_service/app_service_shortcut_shelf_context_menu.cc",
+ "ash/shelf/app_service/app_service_shortcut_shelf_context_menu.h",
+ "ash/shelf/app_service/app_service_shortcut_shelf_item_controller.cc",
+ "ash/shelf/app_service/app_service_shortcut_shelf_item_controller.h",
"ash/shelf/app_service/exo_app_type_resolver.cc",
"ash/shelf/app_service/exo_app_type_resolver.h",
"ash/shelf/app_service/shelf_app_service_app_updater.cc",
"ash/shelf/app_service/shelf_app_service_app_updater.h",
+ "ash/shelf/app_service/shelf_app_service_promise_app_updater.cc",
+ "ash/shelf/app_service/shelf_app_service_promise_app_updater.h",
+ "ash/shelf/app_service/shelf_app_service_shortcut_updater.cc",
+ "ash/shelf/app_service/shelf_app_service_shortcut_updater.h",
"ash/shelf/app_shortcut_shelf_item_controller.cc",
"ash/shelf/app_shortcut_shelf_item_controller.h",
"ash/shelf/app_window_base.cc",
@@ -2736,12 +2719,14 @@ static_library("ui") {
"views/arc_data_removal_dialog_view.cc",
"views/borealis/borealis_beta_badge.cc",
"views/borealis/borealis_beta_badge.h",
- "views/borealis/borealis_installer_disallowed_dialog.cc",
- "views/borealis/borealis_installer_disallowed_dialog.h",
+ "views/borealis/borealis_disallowed_dialog.cc",
+ "views/borealis/borealis_disallowed_dialog.h",
"views/borealis/borealis_installer_error_dialog.cc",
"views/borealis/borealis_installer_error_dialog.h",
"views/borealis/borealis_installer_view.cc",
"views/borealis/borealis_installer_view.h",
+ "views/borealis/borealis_launch_error_dialog.cc",
+ "views/borealis/borealis_launch_error_dialog.h",
"views/borealis/borealis_splash_screen_view.cc",
"views/borealis/borealis_splash_screen_view.h",
"views/bruschetta/bruschetta_installer_view.cc",
@@ -2765,6 +2750,8 @@ static_library("ui") {
"views/crostini/crostini_uninstaller_view.h",
"views/crostini/crostini_update_filesystem_view.cc",
"views/crostini/crostini_update_filesystem_view.h",
+ "views/extensions/web_file_handlers/web_file_handlers_file_launch_dialog.cc",
+ "views/extensions/web_file_handlers/web_file_handlers_file_launch_dialog.h",
"views/frame/browser_frame_ash.cc",
"views/frame/browser_frame_ash.h",
"views/frame/custom_tab_browser_frame.cc",
@@ -2818,6 +2805,10 @@ static_library("ui") {
"webui/ash/bluetooth_pairing_dialog.h",
"webui/ash/bluetooth_shared_load_time_data_provider.cc",
"webui/ash/bluetooth_shared_load_time_data_provider.h",
+ "webui/ash/borealis_installer/borealis_installer_page_handler.cc",
+ "webui/ash/borealis_installer/borealis_installer_page_handler.h",
+ "webui/ash/borealis_installer/borealis_installer_ui.cc",
+ "webui/ash/borealis_installer/borealis_installer_ui.h",
"webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc",
"webui/ash/cellular_setup/cellular_setup_localized_strings_provider.h",
"webui/ash/cellular_setup/mobile_setup_dialog.cc",
@@ -2874,6 +2865,8 @@ static_library("ui") {
"webui/ash/emoji/emoji_ui.h",
"webui/ash/emoji/gif_tenor_api_fetcher.cc",
"webui/ash/emoji/gif_tenor_api_fetcher.h",
+ "webui/ash/emoji/new_window_proxy.cc",
+ "webui/ash/emoji/new_window_proxy.h",
"webui/ash/enterprise_reporting/enterprise_reporting_ui.cc",
"webui/ash/enterprise_reporting/enterprise_reporting_ui.h",
"webui/ash/guest_os_installer/guest_os_installer_dialog.cc",
@@ -2922,6 +2915,8 @@ static_library("ui") {
"webui/ash/lock_screen_reauth/lock_screen_reauth_handler.h",
"webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc",
"webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.h",
+ "webui/ash/login/add_child_screen_handler.cc",
+ "webui/ash/login/add_child_screen_handler.h",
"webui/ash/login/app_downloading_screen_handler.cc",
"webui/ash/login/app_downloading_screen_handler.h",
"webui/ash/login/app_launch_splash_screen_handler.cc",
@@ -2942,6 +2937,8 @@ static_library("ui") {
"webui/ash/login/choobe_screen_handler.h",
"webui/ash/login/consolidated_consent_screen_handler.cc",
"webui/ash/login/consolidated_consent_screen_handler.h",
+ "webui/ash/login/consumer_update_screen_handler.cc",
+ "webui/ash/login/consumer_update_screen_handler.h",
"webui/ash/login/cookie_waiter.cc",
"webui/ash/login/cookie_waiter.h",
"webui/ash/login/core_oobe_handler.cc",
@@ -3000,6 +2997,8 @@ static_library("ui") {
"webui/ash/login/lacros_data_backward_migration_screen_handler.h",
"webui/ash/login/lacros_data_migration_screen_handler.cc",
"webui/ash/login/lacros_data_migration_screen_handler.h",
+ "webui/ash/login/local_password_setup_handler.cc",
+ "webui/ash/login/local_password_setup_handler.h",
"webui/ash/login/local_state_error_screen_handler.cc",
"webui/ash/login/local_state_error_screen_handler.h",
"webui/ash/login/locale_switch_screen_handler.cc",
@@ -3032,6 +3031,8 @@ static_library("ui") {
"webui/ash/login/packaged_license_screen_handler.h",
"webui/ash/login/parental_handoff_screen_handler.cc",
"webui/ash/login/parental_handoff_screen_handler.h",
+ "webui/ash/login/password_selection_screen_handler.cc",
+ "webui/ash/login/password_selection_screen_handler.h",
"webui/ash/login/pin_setup_screen_handler.cc",
"webui/ash/login/pin_setup_screen_handler.h",
@@ -3077,6 +3078,12 @@ static_library("ui") {
"webui/ash/login/welcome_screen_handler.h",
"webui/ash/login/wrong_hwid_screen_handler.cc",
"webui/ash/login/wrong_hwid_screen_handler.h",
+ "webui/ash/mako/mako_source.cc",
+ "webui/ash/mako/mako_source.h",
+ "webui/ash/mako/mako_ui.cc",
+ "webui/ash/mako/mako_ui.h",
+ "webui/ash/mako/url_constants.cc",
+ "webui/ash/mako/url_constants.h",
"webui/ash/manage_mirrorsync/manage_mirrorsync_dialog.cc",
"webui/ash/manage_mirrorsync/manage_mirrorsync_dialog.h",
"webui/ash/manage_mirrorsync/manage_mirrorsync_page_handler.cc",
@@ -3126,8 +3133,22 @@ static_library("ui") {
"webui/ash/power_ui.h",
"webui/ash/remote_maintenance_curtain_ui.cc",
"webui/ash/remote_maintenance_curtain_ui.h",
+ "webui/ash/scalable_iph/scalable_iph_debug_ui.cc",
+ "webui/ash/scalable_iph/scalable_iph_debug_ui.h",
+ "webui/ash/sensor_info/sensor_info_ui.cc",
+ "webui/ash/sensor_info/sensor_info_ui.h",
"webui/ash/set_time_ui.cc",
"webui/ash/set_time_ui.h",
+ "webui/ash/settings/app_management/app_management_uma.h",
+ "webui/ash/settings/calculator/size_calculator.cc",
+ "webui/ash/settings/calculator/size_calculator.h",
+ "webui/ash/settings/constants/constants_util.cc",
+ "webui/ash/settings/constants/constants_util.h",
+ "webui/ash/settings/search/search_concept.h",
+ "webui/ash/settings/search/search_handler.cc",
+ "webui/ash/settings/search/search_handler.h",
+ "webui/ash/settings/search/search_tag_registry.cc",
+ "webui/ash/settings/search/search_tag_registry.h",
"webui/ash/shimless_rma_dialog.cc",
"webui/ash/shimless_rma_dialog.h",
"webui/ash/slow_trace_ui.cc",
@@ -3142,6 +3163,10 @@ static_library("ui") {
"webui/ash/smb_shares/smb_share_dialog.h",
"webui/ash/smb_shares/smb_shares_localized_strings_provider.cc",
"webui/ash/smb_shares/smb_shares_localized_strings_provider.h",
+ "webui/ash/status_area_internals/status_area_internals_handler.cc",
+ "webui/ash/status_area_internals/status_area_internals_handler.h",
+ "webui/ash/status_area_internals/status_area_internals_ui.cc",
+ "webui/ash/status_area_internals/status_area_internals_ui.h",
"webui/ash/sync/os_sync_handler.cc",
"webui/ash/sync/os_sync_handler.h",
"webui/ash/sys_internals/sys_internals_message_handler.cc",
@@ -3196,17 +3221,12 @@ static_library("ui") {
"webui/settings/ash/account_manager_ui_handler.h",
"webui/settings/ash/android_apps_handler.cc",
"webui/settings/ash/android_apps_handler.h",
- "webui/settings/ash/app_management/app_management_uma.h",
"webui/settings/ash/apps_section.cc",
"webui/settings/ash/apps_section.h",
"webui/settings/ash/bluetooth_handler.cc",
"webui/settings/ash/bluetooth_handler.h",
"webui/settings/ash/bluetooth_section.cc",
"webui/settings/ash/bluetooth_section.h",
- "webui/settings/ash/calculator/size_calculator.cc",
- "webui/settings/ash/calculator/size_calculator.h",
- "webui/settings/ash/constants/constants_util.cc",
- "webui/settings/ash/constants/constants_util.h",
"webui/settings/ash/crostini_handler.cc",
"webui/settings/ash/crostini_handler.h",
"webui/settings/ash/crostini_section.cc",
@@ -3307,6 +3327,8 @@ static_library("ui") {
"webui/settings/ash/pdf_ocr_handler.h",
"webui/settings/ash/people_section.cc",
"webui/settings/ash/people_section.h",
+ "webui/settings/ash/per_session_settings_user_action_tracker.cc",
+ "webui/settings/ash/per_session_settings_user_action_tracker.h",
"webui/settings/ash/peripheral_data_access_handler.cc",
"webui/settings/ash/peripheral_data_access_handler.h",
"webui/settings/ash/personalization_hub_handler.cc",
@@ -3327,13 +3349,6 @@ static_library("ui") {
"webui/settings/ash/quick_unlock_handler.h",
"webui/settings/ash/reset_section.cc",
"webui/settings/ash/reset_section.h",
- "webui/settings/ash/search/per_session_settings_user_action_tracker.cc",
- "webui/settings/ash/search/per_session_settings_user_action_tracker.h",
- "webui/settings/ash/search/search_concept.h",
- "webui/settings/ash/search/search_handler.cc",
- "webui/settings/ash/search/search_handler.h",
- "webui/settings/ash/search/search_tag_registry.cc",
- "webui/settings/ash/search/search_tag_registry.h",
"webui/settings/ash/search_section.cc",
"webui/settings/ash/search_section.h",
"webui/settings/ash/select_to_speak_handler.cc",
@@ -3348,10 +3363,10 @@ static_library("ui") {
"webui/settings/ash/settings_with_tts_preview_handler.h",
"webui/settings/ash/switch_access_handler.cc",
"webui/settings/ash/switch_access_handler.h",
+ "webui/settings/ash/system_preferences_section.cc",
+ "webui/settings/ash/system_preferences_section.h",
"webui/settings/ash/tts_handler.cc",
"webui/settings/ash/tts_handler.h",
- "webui/settings/chromeos/constants/routes_util.cc",
- "webui/settings/chromeos/constants/routes_util.h",
"webui/signin/ash/inline_login_dialog.cc",
"webui/signin/ash/inline_login_dialog.h",
"webui/signin/ash/inline_login_dialog_onboarding.cc",
@@ -3391,6 +3406,7 @@ static_library("ui") {
"//ash/webui/camera_app_ui",
"//ash/webui/color_internals",
"//ash/webui/common:chrome_os_webui_config",
+ "//ash/webui/common:trusted_types_util",
"//ash/webui/common/resources/office_fallback:resources",
"//ash/webui/connectivity_diagnostics",
"//ash/webui/demo_mode_app_ui",
@@ -3420,11 +3436,13 @@ static_library("ui") {
"//ash/webui/scanning",
"//ash/webui/shimless_rma",
"//ash/webui/shortcut_customization_ui",
+ "//ash/webui/shortcut_customization_ui/backend/search:mojo_bindings",
"//ash/webui/system_apps/public:system_web_app_type",
"//ash/webui/system_extensions_internals_ui",
"//ash/webui/web_applications",
"//build:chromeos_buildflags",
"//chrome/app:generated_resources",
+ "//chrome/browser/apps/app_discovery_service",
"//chrome/browser/ash",
"//chrome/browser/ash/app_list/search/ranking:proto",
"//chrome/browser/ash/app_list/search/util:proto",
@@ -3432,6 +3450,7 @@ static_library("ui") {
"//chrome/browser/ash/crosapi",
"//chrome/browser/ash/crosapi:browser_util",
"//chrome/browser/ash/crostini:crostini_installer_types_mojom",
+ "//chrome/browser/ash/input_method/mojom:mojom_js",
"//chrome/browser/ash/login/oobe_quick_start",
"//chrome/browser/ash/system_web_apps",
"//chrome/browser/ash/system_web_apps/types",
@@ -3441,6 +3460,7 @@ static_library("ui") {
"//chrome/browser/chromeos/launcher_search:search_util",
"//chrome/browser/chromeos/office_web_app",
"//chrome/browser/enterprise/connectors/device_trust:prefs",
+ "//chrome/browser/enterprise/connectors/device_trust/attestation/ash",
"//chrome/browser/media/router/discovery/access_code:access_code_cast_feature",
"//chrome/browser/media/router/discovery/access_code:discovery_resources_proto",
"//chrome/browser/metrics/structured:features",
@@ -3457,6 +3477,7 @@ static_library("ui") {
"//chrome/browser/ui/quick_answers",
"//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings",
"//chrome/browser/ui/webui/ash/audio:mojo_bindings",
+ "//chrome/browser/ui/webui/ash/borealis_installer:mojo_bindings",
"//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings",
"//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings",
"//chrome/browser/ui/webui/ash/launcher_internals:mojo_bindings",
@@ -3464,12 +3485,12 @@ static_library("ui") {
"//chrome/browser/ui/webui/ash/office_fallback:mojo_bindings",
"//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
"//chrome/browser/ui/webui/ash/parent_access:proto",
+ "//chrome/browser/ui/webui/ash/settings/search/mojom",
"//chrome/browser/ui/webui/ash/vm:mojo_bindings",
"//chrome/browser/ui/webui/nearby_share:mojom",
"//chrome/browser/ui/webui/settings/ash/files_page/mojom",
"//chrome/browser/ui/webui/settings/ash/input_device_settings:mojom",
"//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom",
- "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings",
"//chrome/browser/web_applications",
"//chrome/browser/webshare:storage",
"//chrome/services/file_util/public/cpp",
@@ -3498,6 +3519,7 @@ static_library("ui") {
"//chromeos/ash/components/drivefs",
"//chromeos/ash/components/drivefs/mojom:mojom",
"//chromeos/ash/components/fwupd",
+ "//chromeos/ash/components/heatmap",
"//chromeos/ash/components/human_presence",
"//chromeos/ash/components/install_attributes",
"//chromeos/ash/components/local_search_service/public/cpp",
@@ -3508,12 +3530,15 @@ static_library("ui") {
"//chromeos/ash/components/multidevice",
"//chromeos/ash/components/multidevice/logging",
"//chromeos/ash/components/nearby/presence",
+ "//chromeos/ash/components/nearby/presence/credentials",
"//chromeos/ash/components/network",
"//chromeos/ash/components/network/portal_detector",
"//chromeos/ash/components/peripheral_notification",
"//chromeos/ash/components/phonehub",
"//chromeos/ash/components/phonehub:debug",
"//chromeos/ash/components/proximity_auth",
+ "//chromeos/ash/components/scalable_iph:constants",
+ "//chromeos/ash/components/scalable_iph:scalable_iph",
"//chromeos/ash/components/settings",
"//chromeos/ash/components/string_matching",
"//chromeos/ash/components/system",
@@ -3687,11 +3712,13 @@ static_library("ui") {
"//chrome/app:generated_resources",
"//chrome/browser/chromeos",
"//chrome/browser/metrics/structured:features",
+ "//chrome/browser/ui/views/editor_menu",
"//chromeos/components/kiosk",
"//chromeos/components/security_token_pin",
"//chromeos/constants",
"//chromeos/dbus/power",
"//chromeos/strings",
+ "//chromeos/version",
"//components/account_manager_core:account_manager_core",
"//components/metrics/structured:structured_events",
"//ui/chromeos/styles:cros_styles_views",
@@ -3743,8 +3770,8 @@ static_library("ui") {
if (is_chromeos) {
deps += [
- "//chrome/browser/ui/webui/settings/chromeos/constants:gen_routes",
- "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
+ "//ash/webui/settings/public/constants",
+ "//ash/webui/settings/public/constants:mojom",
]
}
@@ -3765,14 +3792,14 @@ static_library("ui") {
"frame/window_frame_util.cc",
"frame/window_frame_util.h",
"incognito_clear_browsing_data_dialog_interface.h",
- "signin_modal_dialog.cc",
- "signin_modal_dialog.h",
- "signin_modal_dialog_impl.cc",
- "signin_modal_dialog_impl.h",
- "signin_view_controller.cc",
- "signin_view_controller.h",
- "signin_view_controller_delegate.cc",
- "signin_view_controller_delegate.h",
+ "signin/signin_modal_dialog.cc",
+ "signin/signin_modal_dialog.h",
+ "signin/signin_modal_dialog_impl.cc",
+ "signin/signin_modal_dialog_impl.h",
+ "signin/signin_view_controller.cc",
+ "signin/signin_view_controller.h",
+ "signin/signin_view_controller_delegate.cc",
+ "signin/signin_view_controller_delegate.h",
"startup/web_app_startup_utils.cc",
"startup/web_app_startup_utils.h",
"tab_contents/chrome_web_contents_menu_helper.cc",
@@ -3836,8 +3863,6 @@ static_library("ui") {
"webui/app_home/app_home_page_handler.h",
"webui/app_home/app_home_ui.cc",
"webui/app_home/app_home_ui.h",
- "webui/app_launcher_page_ui.cc",
- "webui/app_launcher_page_ui.h",
"webui/app_settings/web_app_settings_navigation_throttle.cc",
"webui/app_settings/web_app_settings_navigation_throttle.h",
"webui/app_settings/web_app_settings_ui.cc",
@@ -3848,18 +3873,25 @@ static_library("ui") {
"webui/ntp/app_launcher_handler.h",
]
- deps += [ "//chrome/browser/ui/webui/app_home:mojo_bindings" ]
+ deps += [
+ "//chrome/app:generated_resources",
+ "//chrome/browser/ui/webui/app_home:mojo_bindings",
+ ]
}
if (is_win || is_mac || is_fuchsia || is_linux || is_chromeos_lacros) {
sources += [
- "profile_picker.cc",
- "profile_picker.h",
- "signin/profile_customization_bubble_sync_controller.h",
- "signin/profile_customization_synced_theme_waiter.cc",
- "signin/profile_customization_synced_theme_waiter.h",
- "signin/profile_customization_util.cc",
- "signin/profile_customization_util.h",
+ "passwords/account_storage_auth_helper.cc",
+ "passwords/account_storage_auth_helper.h",
+ "profiles/profile_customization_bubble_sync_controller.h",
+ "profiles/profile_customization_synced_theme_waiter.cc",
+ "profiles/profile_customization_synced_theme_waiter.h",
+ "profiles/profile_customization_util.cc",
+ "profiles/profile_customization_util.h",
+ "profiles/profile_picker.cc",
+ "profiles/profile_picker.h",
+ "signin/signin_reauth_view_controller.cc",
+ "signin/signin_reauth_view_controller.h",
"startup/default_browser_infobar_delegate.cc",
"startup/default_browser_infobar_delegate.h",
"startup/default_browser_prompt.cc",
@@ -3910,8 +3942,6 @@ static_library("ui") {
"webui/signin/enterprise_profile_welcome_handler.h",
"webui/signin/enterprise_profile_welcome_ui.cc",
"webui/signin/enterprise_profile_welcome_ui.h",
- "webui/signin/profile_creation_customize_themes_handler.cc",
- "webui/signin/profile_creation_customize_themes_handler.h",
"webui/signin/profile_customization_handler.cc",
"webui/signin/profile_customization_handler.h",
"webui/signin/profile_customization_ui.cc",
@@ -3928,6 +3958,10 @@ static_library("ui") {
"webui/signin/signin_error_handler.h",
"webui/signin/signin_error_ui.cc",
"webui/signin/signin_error_ui.h",
+ "webui/signin/signin_reauth_handler.cc",
+ "webui/signin/signin_reauth_handler.h",
+ "webui/signin/signin_reauth_ui.cc",
+ "webui/signin/signin_reauth_ui.h",
"webui/signin/signin_ui_error.cc",
"webui/signin/signin_ui_error.h",
"webui/signin/signin_utils_desktop.cc",
@@ -3947,14 +3981,10 @@ static_library("ui") {
if (enable_dice_support) {
sources += [
- "passwords/account_storage_auth_helper.cc",
- "passwords/account_storage_auth_helper.h",
+ "profiles/signin_intercept_first_run_experience_dialog.cc",
+ "profiles/signin_intercept_first_run_experience_dialog.h",
"signin/dice_web_signin_interceptor_delegate.cc",
"signin/dice_web_signin_interceptor_delegate.h",
- "signin_intercept_first_run_experience_dialog.cc",
- "signin_intercept_first_run_experience_dialog.h",
- "signin_reauth_view_controller.cc",
- "signin_reauth_view_controller.h",
"startup/first_run_service_dice_statics.cc",
"views/profiles/dice_web_signin_interception_bubble_view.cc",
"views/profiles/dice_web_signin_interception_bubble_view.h",
@@ -3970,10 +4000,6 @@ static_library("ui") {
"webui/signin/dice_web_signin_intercept_ui.h",
"webui/signin/inline_login_handler_impl.cc",
"webui/signin/inline_login_handler_impl.h",
- "webui/signin/signin_reauth_handler.cc",
- "webui/signin/signin_reauth_handler.h",
- "webui/signin/signin_reauth_ui.cc",
- "webui/signin/signin_reauth_ui.h",
"webui/welcome/bookmark_handler.cc",
"webui/welcome/bookmark_handler.h",
"webui/welcome/bookmark_item.cc",
@@ -4020,11 +4046,35 @@ static_library("ui") {
if (is_mac) {
sources += [
+ "autofill/autofill_popup_controller_impl_mac.h",
+ "autofill/autofill_popup_controller_impl_mac.mm",
+ "browser_commands_mac.h",
+ "browser_commands_mac.mm",
"browser_mac.cc",
"browser_mac.h",
"cocoa/accelerator_utils_cocoa.mm",
"cocoa/accelerators_cocoa.h",
"cocoa/accelerators_cocoa.mm",
+ "cocoa/applescript/apple_event_util.h",
+ "cocoa/applescript/apple_event_util.mm",
+ "cocoa/applescript/bookmark_folder_applescript.h",
+ "cocoa/applescript/bookmark_folder_applescript.mm",
+ "cocoa/applescript/bookmark_item_applescript.h",
+ "cocoa/applescript/bookmark_item_applescript.mm",
+ "cocoa/applescript/bookmark_node_applescript.h",
+ "cocoa/applescript/bookmark_node_applescript.mm",
+ "cocoa/applescript/browsercrapplication+applescript.h",
+ "cocoa/applescript/browsercrapplication+applescript.mm",
+ "cocoa/applescript/constants_applescript.h",
+ "cocoa/applescript/constants_applescript.mm",
+ "cocoa/applescript/element_applescript.h",
+ "cocoa/applescript/element_applescript.mm",
+ "cocoa/applescript/error_applescript.h",
+ "cocoa/applescript/error_applescript.mm",
+ "cocoa/applescript/tab_applescript.h",
+ "cocoa/applescript/tab_applescript.mm",
+ "cocoa/applescript/window_applescript.h",
+ "cocoa/applescript/window_applescript.mm",
"cocoa/apps/app_shim_menu_controller_mac.h",
"cocoa/apps/app_shim_menu_controller_mac.mm",
"cocoa/apps/quit_with_apps_controller_mac.cc",
@@ -4043,8 +4093,14 @@ static_library("ui") {
"cocoa/color_chooser_mac.mm",
"cocoa/confirm_quit.cc",
"cocoa/confirm_quit.h",
+ "cocoa/confirm_quit_panel_controller.h",
+ "cocoa/confirm_quit_panel_controller.mm",
"cocoa/dock_icon.h",
"cocoa/dock_icon.mm",
+ "cocoa/first_run_dialog_cocoa.h",
+ "cocoa/first_run_dialog_cocoa.mm",
+ "cocoa/first_run_dialog_controller.h",
+ "cocoa/first_run_dialog_controller.mm",
"cocoa/fullscreen/fullscreen_menubar_tracker.h",
"cocoa/fullscreen/fullscreen_menubar_tracker.mm",
"cocoa/fullscreen/fullscreen_toolbar_animation_controller.h",
@@ -4063,12 +4119,12 @@ static_library("ui") {
"cocoa/history_overlay_controller.mm",
"cocoa/javascript_app_modal_dialog_cocoa.h",
"cocoa/javascript_app_modal_dialog_cocoa.mm",
- "cocoa/key_equivalent_constants.h",
- "cocoa/key_equivalent_constants.mm",
"cocoa/l10n_util.h",
"cocoa/l10n_util.mm",
"cocoa/last_active_browser_cocoa.cc",
"cocoa/last_active_browser_cocoa.h",
+ "cocoa/main_menu_builder.h",
+ "cocoa/main_menu_builder.mm",
"cocoa/main_menu_item.h",
"cocoa/profiles/profile_menu_controller.h",
"cocoa/profiles/profile_menu_controller.mm",
@@ -4076,10 +4132,34 @@ static_library("ui") {
"cocoa/renderer_context_menu/render_view_context_menu_mac.mm",
"cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa.h",
"cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa.mm",
+ "cocoa/scoped_menu_bar_lock.h",
+ "cocoa/scoped_menu_bar_lock.mm",
+ "cocoa/screentime/fake_webpage_controller.h",
+ "cocoa/screentime/fake_webpage_controller.mm",
+ "cocoa/screentime/history_bridge.h",
+ "cocoa/screentime/history_bridge.mm",
+ "cocoa/screentime/history_bridge_factory.h",
+ "cocoa/screentime/history_bridge_factory.mm",
+ "cocoa/screentime/history_deleter.h",
+ "cocoa/screentime/history_deleter_impl.h",
+ "cocoa/screentime/history_deleter_impl.mm",
"cocoa/screentime/screentime_features.cc",
"cocoa/screentime/screentime_features.h",
"cocoa/screentime/screentime_policy.cc",
"cocoa/screentime/screentime_policy.h",
+ "cocoa/screentime/tab_helper.h",
+ "cocoa/screentime/tab_helper.mm",
+ "cocoa/screentime/webpage_controller.h",
+ "cocoa/screentime/webpage_controller_impl.h",
+ "cocoa/screentime/webpage_controller_impl.mm",
+ "cocoa/share_menu_controller.h",
+ "cocoa/share_menu_controller.mm",
+ "cocoa/simple_message_box_cocoa.h",
+ "cocoa/simple_message_box_cocoa.mm",
+ "cocoa/status_icons/status_icon_mac.h",
+ "cocoa/status_icons/status_icon_mac.mm",
+ "cocoa/status_icons/status_tray_mac.h",
+ "cocoa/status_icons/status_tray_mac.mm",
"cocoa/tab_contents/web_drag_bookmark_handler_mac.h",
"cocoa/tab_contents/web_drag_bookmark_handler_mac.mm",
"cocoa/tab_menu_bridge.h",
@@ -4088,6 +4168,14 @@ static_library("ui") {
"cocoa/task_manager_mac.mm",
"cocoa/task_manager_mac_table_view.h",
"cocoa/task_manager_mac_table_view.mm",
+ "cocoa/touchbar/browser_window_default_touch_bar.h",
+ "cocoa/touchbar/browser_window_default_touch_bar.mm",
+ "cocoa/touchbar/browser_window_touch_bar_controller.h",
+ "cocoa/touchbar/browser_window_touch_bar_controller.mm",
+ "cocoa/touchbar/credit_card_autofill_touch_bar_controller.h",
+ "cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm",
+ "cocoa/touchbar/web_textfield_touch_bar_controller.h",
+ "cocoa/touchbar/web_textfield_touch_bar_controller.mm",
"cocoa/window_size_autosaver.h",
"cocoa/window_size_autosaver.mm",
"color_chooser.h",
@@ -4097,6 +4185,8 @@ static_library("ui") {
"views/apps/chrome_app_window_client_views_mac.mm",
"views/certificate_viewer_mac_views.mm",
"views/dropdown_bar_host_mac.mm",
+ "views/eye_dropper/eye_dropper_view_mac.h",
+ "views/eye_dropper/eye_dropper_view_mac.mm",
"views/frame/browser_frame_mac.h",
"views/frame/browser_frame_mac.mm",
"views/frame/browser_non_client_frame_view_factory_mac.mm",
@@ -4109,6 +4199,9 @@ static_library("ui") {
"views/tab_contents/chrome_web_contents_view_delegate_views_mac.mm",
"webui/help/version_updater_mac.h",
"webui/help/version_updater_mac.mm",
+ "webui/settings/mac_system_settings_handler.cc",
+ "webui/settings/mac_system_settings_handler.h",
+ "webui/settings/settings_utils_mac.mm",
"window_sizer/window_sizer_mac.mm",
]
@@ -4119,12 +4212,8 @@ static_library("ui") {
]
}
- allow_circular_includes_from += [
- ":ui_arc",
- "//chrome/browser/apps/app_shim",
- ]
+ allow_circular_includes_from += [ "//chrome/browser/apps/app_shim" ]
- public_deps += [ ":ui_arc" ]
deps += [
"//chrome/browser/apps/app_shim",
"//chrome/browser/mac:keystone_glue",
@@ -4407,6 +4496,11 @@ static_library("ui") {
"autofill/autofill_bubble_base.h",
"autofill/autofill_bubble_controller_base.cc",
"autofill/autofill_bubble_controller_base.h",
+ "autofill/delete_address_profile_dialog_controller.h",
+ "autofill/delete_address_profile_dialog_controller_impl.cc",
+ "autofill/delete_address_profile_dialog_controller_impl.h",
+ "autofill/delete_address_profile_dialog_view.cc",
+ "autofill/delete_address_profile_dialog_view.h",
"autofill/edit_address_profile_dialog_controller.h",
"autofill/edit_address_profile_dialog_controller_impl.cc",
"autofill/edit_address_profile_dialog_controller_impl.h",
@@ -4437,10 +4531,6 @@ static_library("ui") {
"autofill/payments/save_iban_ui.h",
"autofill/payments/save_payment_icon_controller.cc",
"autofill/payments/save_payment_icon_controller.h",
- "autofill/payments/save_upi_bubble.h",
- "autofill/payments/save_upi_bubble_controller.h",
- "autofill/payments/save_upi_bubble_controller_impl.cc",
- "autofill/payments/save_upi_bubble_controller_impl.h",
"autofill/payments/virtual_card_enroll_bubble_controller_impl.cc",
"autofill/payments/virtual_card_enroll_bubble_controller_impl.h",
"autofill/payments/virtual_card_manual_fallback_bubble_controller.h",
@@ -4594,6 +4684,8 @@ static_library("ui") {
"views/autofill/payments/payments_view_util.h",
"views/autofill/payments/promo_code_label_button.cc",
"views/autofill/payments/promo_code_label_button.h",
+ "views/autofill/payments/promo_code_label_view.cc",
+ "views/autofill/payments/promo_code_label_view.h",
"views/autofill/payments/save_card_bubble_views.cc",
"views/autofill/payments/save_card_bubble_views.h",
"views/autofill/payments/save_card_failure_bubble_views.cc",
@@ -4606,8 +4698,6 @@ static_library("ui") {
"views/autofill/payments/save_iban_bubble_view.h",
"views/autofill/payments/save_payment_icon_view.cc",
"views/autofill/payments/save_payment_icon_view.h",
- "views/autofill/payments/save_upi_offer_bubble_views.cc",
- "views/autofill/payments/save_upi_offer_bubble_views.h",
"views/autofill/payments/virtual_card_enroll_bubble_views.cc",
"views/autofill/payments/virtual_card_enroll_bubble_views.h",
"views/autofill/payments/virtual_card_enroll_icon_view.cc",
@@ -4703,8 +4793,6 @@ static_library("ui") {
"views/chrome_views_delegate.h",
"views/chrome_web_dialog_view.cc",
"views/chrome_web_dialog_view.h",
- "views/collected_cookies_views.cc",
- "views/collected_cookies_views.h",
"views/color_provider_browser_helper.cc",
"views/color_provider_browser_helper.h",
"views/commander_frontend_views.cc",
@@ -4715,10 +4803,14 @@ static_library("ui") {
"views/commerce/price_insights_icon_view.h",
"views/commerce/price_tracking_bubble_dialog_view.cc",
"views/commerce/price_tracking_bubble_dialog_view.h",
+ "views/commerce/price_tracking_email_dialog_view.cc",
+ "views/commerce/price_tracking_email_dialog_view.h",
"views/commerce/price_tracking_icon_view.cc",
"views/commerce/price_tracking_icon_view.h",
"views/commerce/price_tracking_view.cc",
"views/commerce/price_tracking_view.h",
+ "views/commerce/shopping_collection_iph_view.cc",
+ "views/commerce/shopping_collection_iph_view.h",
"views/confirm_bubble_views.cc",
"views/confirm_bubble_views.h",
"views/constrained_web_dialog_delegate_views.cc",
@@ -4734,6 +4826,8 @@ static_library("ui") {
"views/controls/obscurable_label_with_toggle_button.h",
"views/controls/page_switcher_view.cc",
"views/controls/page_switcher_view.h",
+ "views/controls/rich_controls_container_view.cc",
+ "views/controls/rich_controls_container_view.h",
"views/controls/rich_hover_button.cc",
"views/controls/rich_hover_button.h",
"views/cookie_info_view.cc",
@@ -4742,12 +4836,18 @@ static_library("ui") {
"views/desktop_capture/desktop_media_list_controller.h",
"views/desktop_capture/desktop_media_list_view.cc",
"views/desktop_capture/desktop_media_list_view.h",
+ "views/desktop_capture/desktop_media_pane_view.cc",
+ "views/desktop_capture/desktop_media_pane_view.h",
"views/desktop_capture/desktop_media_picker_views.cc",
"views/desktop_capture/desktop_media_picker_views.h",
"views/desktop_capture/desktop_media_source_view.cc",
"views/desktop_capture/desktop_media_source_view.h",
"views/desktop_capture/desktop_media_tab_list.cc",
"views/desktop_capture/desktop_media_tab_list.h",
+ "views/desktop_capture/rounded_corner_image_view.cc",
+ "views/desktop_capture/rounded_corner_image_view.h",
+ "views/desktop_capture/share_audio_view.cc",
+ "views/desktop_capture/share_audio_view.h",
"views/desktop_capture/share_this_tab_dialog_views.cc",
"views/desktop_capture/share_this_tab_dialog_views.h",
"views/desktop_capture/share_this_tab_source_view.cc",
@@ -4760,6 +4860,8 @@ static_library("ui") {
"views/download/bubble/download_bubble_contents_view.h",
"views/download/bubble/download_bubble_partial_view.cc",
"views/download/bubble/download_bubble_partial_view.h",
+ "views/download/bubble/download_bubble_password_prompt_view.cc",
+ "views/download/bubble/download_bubble_password_prompt_view.h",
"views/download/bubble/download_bubble_primary_view.cc",
"views/download/bubble/download_bubble_primary_view.h",
"views/download/bubble/download_bubble_row_list_view.cc",
@@ -4810,6 +4912,7 @@ static_library("ui") {
"views/extensions/extension_installed_bubble_view.cc",
"views/extensions/extension_keybinding_registry_views.cc",
"views/extensions/extension_keybinding_registry_views.h",
+ "views/extensions/extension_multiple_uninstall_dialog.cc",
"views/extensions/extension_permissions_view.cc",
"views/extensions/extension_permissions_view.h",
"views/extensions/extension_uninstall_dialog_view.cc",
@@ -4916,6 +5019,8 @@ static_library("ui") {
"views/global_media_controls/media_dialog_view.cc",
"views/global_media_controls/media_dialog_view.h",
"views/global_media_controls/media_dialog_view_observer.h",
+ "views/global_media_controls/media_item_ui_cast_footer_view.cc",
+ "views/global_media_controls/media_item_ui_cast_footer_view.h",
"views/global_media_controls/media_item_ui_device_selector_observer.h",
"views/global_media_controls/media_item_ui_device_selector_view.cc",
"views/global_media_controls/media_item_ui_device_selector_view.h",
@@ -4958,8 +5063,18 @@ static_library("ui") {
"views/layout/interpolating_layout_manager.h",
"views/location_bar/content_setting_image_view.cc",
"views/location_bar/content_setting_image_view.h",
- "views/location_bar/cookie_controls_icon_view.cc",
- "views/location_bar/cookie_controls_icon_view.h",
+ "views/location_bar/cookie_controls/cookie_controls_bubble_coordinator.cc",
+ "views/location_bar/cookie_controls/cookie_controls_bubble_coordinator.h",
+ "views/location_bar/cookie_controls/cookie_controls_bubble_view.cc",
+ "views/location_bar/cookie_controls/cookie_controls_bubble_view.h",
+ "views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.cc",
+ "views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.h",
+ "views/location_bar/cookie_controls/cookie_controls_bubble_view_impl.cc",
+ "views/location_bar/cookie_controls/cookie_controls_bubble_view_impl.h",
+ "views/location_bar/cookie_controls/cookie_controls_content_view.cc",
+ "views/location_bar/cookie_controls/cookie_controls_content_view.h",
+ "views/location_bar/cookie_controls/cookie_controls_icon_view.cc",
+ "views/location_bar/cookie_controls/cookie_controls_icon_view.h",
"views/location_bar/custom_tab_bar_view.cc",
"views/location_bar/custom_tab_bar_view.h",
"views/location_bar/find_bar_icon.cc",
@@ -4982,6 +5097,8 @@ static_library("ui") {
"views/location_bar/location_icon_view.h",
"views/location_bar/old_cookie_controls_bubble_view.cc",
"views/location_bar/old_cookie_controls_bubble_view.h",
+ "views/location_bar/old_cookie_controls_icon_view.cc",
+ "views/location_bar/old_cookie_controls_icon_view.h",
"views/location_bar/omnibox_chip_button.cc",
"views/location_bar/omnibox_chip_button.h",
"views/location_bar/omnibox_chip_theme.h",
@@ -5025,10 +5142,14 @@ static_library("ui") {
"views/media_router/web_contents_display_observer_view.h",
"views/message_box_dialog.cc",
"views/message_box_dialog.h",
+ "views/omnibox/omnibox_header_view.cc",
+ "views/omnibox/omnibox_header_view.h",
"views/omnibox/omnibox_match_cell_view.cc",
"views/omnibox/omnibox_match_cell_view.h",
"views/omnibox/omnibox_mouse_enter_exit_handler.cc",
"views/omnibox/omnibox_mouse_enter_exit_handler.h",
+ "views/omnibox/omnibox_popup_presenter.cc",
+ "views/omnibox/omnibox_popup_presenter.h",
"views/omnibox/omnibox_popup_view_views.cc",
"views/omnibox/omnibox_popup_view_views.h",
"views/omnibox/omnibox_popup_view_webui.cc",
@@ -5047,8 +5168,6 @@ static_library("ui") {
"views/omnibox/remove_suggestion_bubble.h",
"views/omnibox/rounded_omnibox_results_frame.cc",
"views/omnibox/rounded_omnibox_results_frame.h",
- "views/omnibox/webui_omnibox_popup_view.cc",
- "views/omnibox/webui_omnibox_popup_view.h",
"views/overlay/back_to_tab_label_button.cc",
"views/overlay/back_to_tab_label_button.h",
"views/overlay/close_image_button.cc",
@@ -5109,8 +5228,6 @@ static_library("ui") {
"views/page_info/page_info_navigation_handler.h",
"views/page_info/page_info_permission_content_view.cc",
"views/page_info/page_info_permission_content_view.h",
- "views/page_info/page_info_row_view.cc",
- "views/page_info/page_info_row_view.h",
"views/page_info/page_info_security_content_view.cc",
"views/page_info/page_info_security_content_view.h",
"views/page_info/page_info_view_factory.cc",
@@ -5151,14 +5268,14 @@ static_library("ui") {
"views/passwords/password_generation_confirmation_view.h",
"views/passwords/password_generation_popup_view_views.cc",
"views/passwords/password_generation_popup_view_views.h",
- "views/passwords/password_items_view.cc",
- "views/passwords/password_items_view.h",
"views/passwords/password_save_unsynced_credentials_locally_view.cc",
"views/passwords/password_save_unsynced_credentials_locally_view.h",
"views/passwords/password_save_update_view.cc",
"views/passwords/password_save_update_view.h",
"views/passwords/post_save_compromised_bubble_view.cc",
"views/passwords/post_save_compromised_bubble_view.h",
+ "views/passwords/shared_passwords_notification_view.cc",
+ "views/passwords/shared_passwords_notification_view.h",
"views/passwords/views_utils.cc",
"views/passwords/views_utils.h",
"views/payments/contact_info_editor_view_controller.cc",
@@ -5221,6 +5338,8 @@ static_library("ui") {
"views/permissions/chip_controller.cc",
"views/permissions/chip_controller.h",
"views/permissions/chooser_bubble_ui.cc",
+ "views/permissions/embedded_permission_prompt.cc",
+ "views/permissions/embedded_permission_prompt.h",
"views/permissions/permission_prompt_bubble.cc",
"views/permissions/permission_prompt_bubble.h",
"views/permissions/permission_prompt_bubble_base_view.cc",
@@ -5368,6 +5487,8 @@ static_library("ui") {
"views/side_panel/side_panel_registry_observer.h",
"views/side_panel/side_panel_resize_area.cc",
"views/side_panel/side_panel_resize_area.h",
+ "views/side_panel/side_panel_rounded_corner.cc",
+ "views/side_panel/side_panel_rounded_corner.h",
"views/side_panel/side_panel_toolbar_container.cc",
"views/side_panel/side_panel_toolbar_container.h",
"views/side_panel/side_panel_util.cc",
@@ -5479,6 +5600,8 @@ static_library("ui") {
"views/tabs/tab_slot_view.h",
"views/tabs/tab_strip.cc",
"views/tabs/tab_strip.h",
+ "views/tabs/tab_strip_control_button.cc",
+ "views/tabs/tab_strip_control_button.h",
"views/tabs/tab_strip_controller.h",
"views/tabs/tab_strip_layout.cc",
"views/tabs/tab_strip_layout.h",
@@ -5588,6 +5711,7 @@ static_library("ui") {
"views/web_apps/protocol_handler_launch_dialog_view.h",
"views/web_apps/pwa_confirmation_bubble_view.cc",
"views/web_apps/pwa_confirmation_bubble_view.h",
+ "views/web_apps/sub_apps_install_dialog.cc",
"views/web_apps/web_app_confirmation_view.cc",
"views/web_apps/web_app_confirmation_view.h",
"views/web_apps/web_app_detailed_install_dialog.cc",
@@ -5606,8 +5730,12 @@ static_library("ui") {
"views/webauthn/authenticator_client_pin_entry_sheet_view.h",
"views/webauthn/authenticator_client_pin_entry_view.cc",
"views/webauthn/authenticator_client_pin_entry_view.h",
+ "views/webauthn/authenticator_multi_source_picker_sheet_view.cc",
+ "views/webauthn/authenticator_multi_source_picker_sheet_view.h",
"views/webauthn/authenticator_paask_sheet_view.cc",
"views/webauthn/authenticator_paask_sheet_view.h",
+ "views/webauthn/authenticator_priority_mechanism_sheet_view.cc",
+ "views/webauthn/authenticator_priority_mechanism_sheet_view.h",
"views/webauthn/authenticator_qr_sheet_view.cc",
"views/webauthn/authenticator_qr_sheet_view.h",
"views/webauthn/authenticator_request_dialog_view.cc",
@@ -5656,7 +5784,7 @@ static_library("ui") {
"//chrome/browser/apps:icon_standardizer",
"//chrome/browser/promos:utils",
"//chrome/browser/ui/views",
- "//chromeos/components/kiosk",
+ "//components/commerce/core:pref_names",
"//components/commerce/core:public",
"//components/commerce/core:shopping_service",
"//components/constrained_window",
@@ -5668,6 +5796,7 @@ static_library("ui") {
"//components/live_caption",
"//components/live_caption:constants",
"//components/media_message_center",
+ "//components/media_message_center/vector_icons",
"//components/offline_items_collection/core",
"//components/page_info",
"//components/page_info/core",
@@ -5680,7 +5809,7 @@ static_library("ui") {
"//components/power_bookmarks/storage",
"//components/reading_list/features:flags",
"//components/segmentation_platform/embedder/default_model:default_model",
- "//components/services/app_service/public/cpp:intents",
+ "//components/services/app_service",
"//components/services/screen_ai/buildflags",
"//components/soda",
"//components/soda:constants",
@@ -5742,7 +5871,10 @@ static_library("ui") {
"sharing_hub/sharing_hub_bubble_controller_chromeos_impl.h",
"views/status_icons/status_tray_chromeos.cc",
]
- deps += [ "//components/arc/common:arc_intent_helper_constants" ]
+ deps += [
+ "//chromeos/components/kiosk",
+ "//components/arc/common:arc_intent_helper_constants",
+ ]
}
if (is_fuchsia) {
sources += [
@@ -5778,6 +5910,7 @@ static_library("ui") {
"views/policy/enterprise_startup_dialog_mac_util.h",
"views/policy/enterprise_startup_dialog_mac_util.mm",
]
+ deps += [ "//components/crash/core/common:crash_key" ]
} else {
sources += [
"views/create_application_shortcut_view.cc",
@@ -5838,7 +5971,6 @@ static_library("ui") {
"views/relaunch_notification/relaunch_recommended_timer.h",
"views/relaunch_notification/relaunch_required_dialog_view.cc",
"views/relaunch_notification/relaunch_required_dialog_view.h",
- "views/screen_capture_notification_ui_views.cc",
"views/sync/bubble_sync_promo_signin_button_view.cc",
"views/sync/bubble_sync_promo_signin_button_view.h",
"views/sync/bubble_sync_promo_view.cc",
@@ -5859,6 +5991,7 @@ static_library("ui") {
"views/frame/opaque_browser_frame_view_layout_delegate.h",
"views/policy/idle_dialog_view.cc",
"views/policy/idle_dialog_view.h",
+ "views/screen_capture_notification_ui_views.cc",
"views/sharing_hub/sharing_hub_bubble_view_impl.cc",
"views/sharing_hub/sharing_hub_bubble_view_impl.h",
]
@@ -5909,6 +6042,8 @@ static_library("ui") {
"aura/accessibility/automation_manager_aura.h",
"aura/tab_contents/web_drag_bookmark_handler_aura.cc",
"aura/tab_contents/web_drag_bookmark_handler_aura.h",
+ "overscroll_pref_manager.cc",
+ "overscroll_pref_manager.h",
"views/accelerator_utils_aura.cc",
"views/apps/app_window_easy_resize_window_targeter.cc",
"views/apps/app_window_easy_resize_window_targeter.h",
@@ -5952,6 +6087,7 @@ static_library("ui") {
"//chrome/browser/apps/platform_apps/api",
"//chrome/browser/extensions",
"//chrome/browser/web_applications",
+ "//chrome/browser/web_applications/app_service",
"//chrome/browser/web_share_target",
"//chrome/common/extensions/api",
"//components/drive",
@@ -6047,14 +6183,14 @@ static_library("ui") {
"web_applications/draggable_region_host_impl.h",
"web_applications/share_target_utils.cc",
"web_applications/share_target_utils.h",
+ "web_applications/sub_apps_install_dialog_controller.cc",
+ "web_applications/sub_apps_install_dialog_controller.h",
"web_applications/sub_apps_service_impl.cc",
"web_applications/sub_apps_service_impl.h",
"web_applications/tabbed_web_app_navigation_throttle.cc",
"web_applications/tabbed_web_app_navigation_throttle.h",
"web_applications/web_app_browser_controller.cc",
"web_applications/web_app_browser_controller.h",
- "web_applications/web_app_dialog_manager.cc",
- "web_applications/web_app_dialog_manager.h",
"web_applications/web_app_dialog_utils.cc",
"web_applications/web_app_dialog_utils.h",
"web_applications/web_app_launch_process.cc",
@@ -6075,7 +6211,6 @@ static_library("ui") {
"web_applications/web_app_ui_manager_impl.h",
"web_applications/web_app_ui_utils.cc",
"web_applications/web_app_ui_utils.h",
- "web_applications/web_app_uninstall_dialog.h",
"web_applications/webui_web_app_navigation_throttle.cc",
"web_applications/webui_web_app_navigation_throttle.h",
"webui/extensions/extension_basic_info.cc",
@@ -6086,6 +6221,11 @@ static_library("ui") {
if (is_chromeos) {
deps += [ "//chromeos/components/kiosk" ]
+
+ sources += [
+ "web_applications/web_app_run_on_os_login_notification.cc",
+ "web_applications/web_app_run_on_os_login_notification.h",
+ ]
}
if (is_chromeos_ash) {
deps += [
@@ -6122,8 +6262,8 @@ static_library("ui") {
if (enable_pdf) {
sources += [
- "pdf/chrome_pdf_web_contents_helper_client.cc",
- "pdf/chrome_pdf_web_contents_helper_client.h",
+ "pdf/chrome_pdf_document_helper_client.cc",
+ "pdf/chrome_pdf_document_helper_client.h",
]
deps += [
"//chrome/browser/pdf",
@@ -6320,7 +6460,6 @@ static_library("test_support") {
if (is_mac) {
assert(toolkit_views)
- configs += [ "//build/config/compiler:enable_arc" ]
sources += [
"cocoa/test/cocoa_test_helper.h",
"cocoa/test/cocoa_test_helper.mm",
@@ -6440,7 +6579,7 @@ static_library("test_support") {
"//ash",
"//ash/public/cpp",
"//ash/public/mojom",
- "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
+ "//ash/webui/settings/public/constants:mojom",
"//chromeos/ash/components/dbus",
"//chromeos/ui/base",
]
@@ -6475,7 +6614,4 @@ if (is_android) {
java_cpp_enum("adaptive_toolbar_enums_java") {
sources = [ "android/toolbar/adaptive_toolbar_enums.h" ]
}
- java_cpp_enum("duplicate_download_enums_java") {
- sources = [ "android/infobars/duplicate_download_infobar.h" ]
- }
}
diff --git a/chromium/chrome/browser/ui/actions/BUILD.gn b/chromium/chrome/browser/ui/actions/BUILD.gn
new file mode 100644
index 00000000000..7ef440043ae
--- /dev/null
+++ b/chromium/chrome/browser/ui/actions/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//ui/base/ui_features.gni")
+
+source_set("actions_headers") {
+ sources = [ "chrome_action_id.h" ]
+
+ public_deps = [
+ "//chrome/app:command_ids",
+ "//ui/actions:actions_headers",
+ ]
+}
+
+if (!is_ios && !is_android) {
+ executable("dump_actions") {
+ testonly = true
+
+ sources = [ "tools/dump_actions.cc" ]
+
+ deps = [
+ ":actions_headers",
+ "//ui/actions:actions_headers",
+ ]
+ }
+}
diff --git a/chromium/chrome/browser/ui/android/appmenu/BUILD.gn b/chromium/chrome/browser/ui/android/appmenu/BUILD.gn
index d539a1e8de6..b0215febca6 100644
--- a/chromium/chrome/browser/ui/android/appmenu/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/appmenu/BUILD.gn
@@ -24,6 +24,7 @@ android_library("java") {
":java_resources",
"//chrome/browser/android/lifecycle:java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_interpolator_interpolator_java",
"//ui/android:ui_java",
]
resources_package = "org.chromium.chrome.browser.ui.appmenu"
diff --git a/chromium/chrome/browser/ui/android/appmenu/internal/BUILD.gn b/chromium/chrome/browser/ui/android/appmenu/internal/BUILD.gn
index 9a6496a110a..950d1b91a63 100644
--- a/chromium/chrome/browser/ui/android/appmenu/internal/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/appmenu/internal/BUILD.gn
@@ -84,6 +84,7 @@ android_library("unit_device_javatests") {
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
diff --git a/chromium/chrome/browser/ui/android/appmenu/test/BUILD.gn b/chromium/chrome/browser/ui/android/appmenu/test/BUILD.gn
index e94bfc62a3e..3f124f4ca10 100644
--- a/chromium/chrome/browser/ui/android/appmenu/test/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/appmenu/test/BUILD.gn
@@ -13,6 +13,7 @@ android_library("test_support_java") {
"java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuObserver.java",
]
deps = [
+ "//base:base_java",
"//base:base_java_test_support",
"//chrome/browser/ui/android/appmenu:java",
"//chrome/browser/ui/android/appmenu/internal:java",
diff --git a/chromium/chrome/browser/ui/android/autofill/internal/BUILD.gn b/chromium/chrome/browser/ui/android/autofill/internal/BUILD.gn
index 9514f4c122e..66ec43fc8cb 100644
--- a/chromium/chrome/browser/ui/android/autofill/internal/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/autofill/internal/BUILD.gn
@@ -68,6 +68,7 @@ robolectric_library("junit") {
":java_resources",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//base/test:test_support_java",
"//components/autofill/android:main_autofill_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
diff --git a/chromium/chrome/browser/ui/android/cars/BUILD.gn b/chromium/chrome/browser/ui/android/cars/BUILD.gn
new file mode 100644
index 00000000000..56a2f62ee06
--- /dev/null
+++ b/chromium/chrome/browser/ui/android/cars/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+ sources = [ "java/src/org/chromium/chrome/browser/ui/cars/DrivingRestrictionsManager.java" ]
+
+ deps = [ "//base:base_java" ]
+
+ public_deps = [ ":delegate_java" ]
+}
+
+android_library("delegate_java") {
+ sources = [
+ "java/src/org/chromium/chrome/browser/ui/cars/DrivingRestrictionsDelegate.java",
+ "java/src/org/chromium/chrome/browser/ui/cars/DrivingRestrictionsDelegateImpl.java",
+ ]
+
+ deps = [ "//base:base_java" ]
+
+ # Add the actual implementation where necessary so that downstream targets
+ # can provide their own implementations.
+ jar_excluded_patterns = [ "*/DrivingRestrictionsDelegateImpl.class" ]
+}
+
+android_library("delegate_public_impl_java") {
+ sources = [ "java/src/org/chromium/chrome/browser/ui/cars/DrivingRestrictionsDelegateImpl.java" ]
+
+ deps = [
+ ":delegate_java",
+ "//base:base_java",
+ ]
+}
+
+robolectric_library("junit") {
+ sources = [ "java/src/org/chromium/chrome/browser/ui/cars/DrivingRestrictionsManagerTest.java" ]
+
+ deps = [
+ ":java",
+ "//base:base_java",
+ "//base:base_junit_test_support",
+ "//third_party/junit",
+ "//third_party/mockito:mockito_java",
+ ]
+}
diff --git a/chromium/chrome/browser/ui/android/device_lock/BUILD.gn b/chromium/chrome/browser/ui/android/device_lock/BUILD.gn
index 263e3184006..be604e2e811 100644
--- a/chromium/chrome/browser/ui/android/device_lock/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/device_lock/BUILD.gn
@@ -9,11 +9,18 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockCoordinator.java",
"java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockMediator.java",
"java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockProperties.java",
+ "java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockUtils.java",
"java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockView.java",
"java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinder.java",
+ "java/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockCoordinator.java",
+ "java/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockMediator.java",
+ "java/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockProperties.java",
+ "java/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockView.java",
+ "java/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockViewBinder.java",
]
deps = [
":java_resources",
+ "//base:base_java",
"//chrome/browser/android/lifecycle:java",
"//chrome/browser/device_reauth/android:java",
"//components/browser_ui/widget/android:java",
@@ -31,6 +38,8 @@ android_library("javatests") {
sources = [
"javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockCoordinatorTest.java",
"javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinderTest.java",
+ "javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockCoordinatorTest.java",
+ "javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockViewBinderTest.java",
]
deps = [
":java",
@@ -38,6 +47,7 @@ android_library("javatests") {
"//base:base_java",
"//base:base_java_test_support",
"//chrome/android:chrome_java",
+ "//chrome/browser/device_reauth/android:java",
"//chrome/test/android:chrome_java_unit_test_support",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
@@ -54,7 +64,10 @@ android_library("javatests") {
robolectric_library("junit") {
resources_package = "org.chromium.chrome.browser.ui.device_lock"
- sources = [ "junit/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockMediatorUnitTest.java" ]
+ sources = [
+ "junit/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockMediatorUnitTest.java",
+ "junit/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockMediatorUnitTest.java",
+ ]
deps = [
":java",
@@ -87,7 +100,11 @@ android_resources("java_resources") {
sources = [
"java/res/drawable-night/device_lock_illustration.xml",
"java/res/drawable/device_lock_illustration.xml",
+ "java/res/drawable/missing_device_lock_illustration.xml",
"java/res/layout/device_lock_view.xml",
+ "java/res/layout/missing_device_lock_view.xml",
+ "java/res/values-night/colors.xml",
+ "java/res/values/colors.xml",
]
deps = [
"//chrome/browser/ui/android/strings:ui_strings_grd",
diff --git a/chromium/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn b/chromium/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn
index 09a56f82f9c..481ae097394 100644
--- a/chromium/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/fast_checkout/internal/BUILD.gn
@@ -56,6 +56,7 @@ android_library("java") {
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//ui/android:ui_no_recycler_view_java",
"//ui/android:ui_recycler_view_java",
+ "//ui/android:ui_utils_java",
]
resources_package = "org.chromium.chrome.browser.ui.fast_checkout"
@@ -100,6 +101,7 @@ android_library("test_java") {
deps = [
"//chrome/browser/ui/android/fast_checkout:java",
"//components/autofill/android:main_autofill_java",
+ "//url:gurl_java",
]
}
@@ -132,6 +134,7 @@ robolectric_library("junit") {
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_ext_junit_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
@@ -161,10 +164,12 @@ android_library("javatests") {
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/bottomsheet/android:java_resources",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/browser_ui/bottomsheet/android/test:java",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_full_java",
diff --git a/chromium/chrome/browser/ui/android/hats/BUILD.gn b/chromium/chrome/browser/ui/android/hats/BUILD.gn
new file mode 100644
index 00000000000..36a8b5637ee
--- /dev/null
+++ b/chromium/chrome/browser/ui/android/hats/BUILD.gn
@@ -0,0 +1,78 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+import("//chrome/android/features/android_library_factory_tmpl.gni")
+
+android_library("java") {
+ srcjar_deps = [ ":jni_headers" ]
+ sources = [
+ "java/src/org/chromium/chrome/browser/ui/hats/SurveyClient.java",
+ "java/src/org/chromium/chrome/browser/ui/hats/SurveyConfig.java",
+ ]
+ deps = [
+ "//base:base_java",
+ "//base:jni_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ ]
+ public_deps = [ ":utils_java" ]
+}
+
+android_library_factory("factory_java") {
+ deps = [ ":java" ]
+
+ # This internal file will be replaced by a generated file so the resulting
+ # android_library target does not actually depend on this internal file.
+ sources = [ "internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientFactory.java" ]
+}
+
+# Temporarily public to the rest of components during code movement.
+# TODO(crbug/1400731): Move into internal once public references are removed.
+android_library("utils_java") {
+ sources =
+ [ "java/src/org/chromium/chrome/browser/ui/hats/SurveyThrottler.java" ]
+
+ deps = [
+ "//base:base_java",
+ "//chrome/browser/first_run/android:java",
+ "//chrome/browser/flags:java",
+ "//chrome/browser/preferences:java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ ]
+}
+
+robolectric_library("junit") {
+ testonly = true
+ sources = [
+ "java/src/org/chromium/chrome/browser/ui/hats/SurveyThrottlerUnitTest.java",
+ ]
+ deps = [
+ ":utils_java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//base:base_junit_test_support",
+ "//chrome/browser/first_run/android:java",
+ "//chrome/browser/preferences:java",
+ "//chrome/browser/ui/android/hats:java",
+ "//third_party/junit",
+ ]
+}
+
+android_library("unit_device_javatests") {
+ testonly = true
+ sources =
+ [ "java/src/org/chromium/chrome/browser/ui/hats/SurveyConfigTest.java" ]
+ deps = [
+ ":java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//content/public/test/android:content_java_test_support",
+ "//third_party/androidx:androidx_test_runner_java",
+ "//third_party/junit",
+ ]
+}
+
+generate_jni("jni_headers") {
+ sources = [ "java/src/org/chromium/chrome/browser/ui/hats/SurveyConfig.java" ]
+}
diff --git a/chromium/chrome/browser/ui/android/hats/internal/BUILD.gn b/chromium/chrome/browser/ui/android/hats/internal/BUILD.gn
new file mode 100644
index 00000000000..711fe46cda2
--- /dev/null
+++ b/chromium/chrome/browser/ui/android/hats/internal/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+import("//chrome/android/features/android_library_factory_tmpl.gni")
+
+android_library("java") {
+ visibility = [
+ ":*",
+ "//chrome/android:chrome_all_java",
+ ]
+ sources = [
+ "java/src/org/chromium/chrome/browser/ui/hats/SurveyClientFactory.java",
+ "java/src/org/chromium/chrome/browser/ui/hats/SurveyClientImpl.java",
+ ]
+ deps = [
+ ":controller_java",
+ "//base:base_java",
+ "//base:jni_java",
+ "//chrome/browser/android/lifecycle:java",
+ "//chrome/browser/ui/android/hats:java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ ]
+}
+
+robolectric_library("junit") {
+ testonly = true
+ sources = [
+ "java/src/org/chromium/chrome/browser/ui/hats/SurveyClientUnitTest.java",
+ ]
+ deps = [
+ ":controller_java",
+ ":java",
+ "//base:base_java_test_support",
+ "//base:base_junit_test_support",
+ "//chrome/browser/ui/android/hats:java",
+ "//third_party/junit",
+ ]
+}
+
+android_library("controller_java") {
+ sources = [
+ "java/src/org/chromium/chrome/browser/ui/hats/SurveyController.java",
+ "java/src/org/chromium/chrome/browser/ui/hats/SurveyControllerProvider.java",
+ ]
+
+ deps = [
+ "//base:base_java",
+ "//chrome/browser/android/lifecycle:java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ "//ui/android:ui_java",
+ ]
+
+ # Add the actual implementation where necessary so that downstream targets
+ # can provide their own implementations.
+ jar_excluded_patterns = [ "*/SurveyControllerProvider*.class" ]
+}
+
+android_library("provider_public_impl_java") {
+ sources = [ "java/src/org/chromium/chrome/browser/ui/hats/SurveyControllerProvider.java" ]
+
+ deps = [ ":controller_java" ]
+}
diff --git a/chromium/chrome/browser/ui/android/logo/BUILD.gn b/chromium/chrome/browser/ui/android/logo/BUILD.gn
index a5195fc099a..0f5245fbcff 100644
--- a/chromium/chrome/browser/ui/android/logo/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/logo/BUILD.gn
@@ -12,6 +12,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/logo/LogoCoordinator.java",
"java/src/org/chromium/chrome/browser/logo/LogoMediator.java",
"java/src/org/chromium/chrome/browser/logo/LogoProperties.java",
+ "java/src/org/chromium/chrome/browser/logo/LogoUtils.java",
"java/src/org/chromium/chrome/browser/logo/LogoView.java",
"java/src/org/chromium/chrome/browser/logo/LogoViewBinder.java",
]
@@ -24,6 +25,7 @@ android_library("java") {
"//chrome/browser/preferences:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/search_engines/android:java",
+ "//components/embedder_support/android:simple_factory_key_java",
"//components/image_fetcher:java",
"//components/search_engines/android:java",
"//content/public/android:content_full_java",
@@ -52,7 +54,9 @@ android_resources("java_resources") {
"java/res/drawable-xxhdpi/google_logo.png",
"java/res/drawable-xxxhdpi/google_logo.png",
"java/res/values-night/colors.xml",
+ "java/res/values-sw600dp/dimens.xml",
"java/res/values/colors.xml",
+ "java/res/values/dimens.xml",
]
deps = [ "//chrome/browser/ui/android/strings:ui_strings_grd" ]
@@ -63,6 +67,7 @@ robolectric_library("junit") {
sources = [
"java/src/org/chromium/chrome/browser/logo/CachedTintedBitmapUnitTest.java",
"java/src/org/chromium/chrome/browser/logo/LogoMediatorUnitTest.java",
+ "java/src/org/chromium/chrome/browser/logo/LogoUtilsUnitTest.java",
"java/src/org/chromium/chrome/browser/logo/LogoViewBinderUnitTest.java",
"java/src/org/chromium/chrome/browser/logo/LogoViewTest.java",
]
@@ -73,6 +78,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android:chrome_java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
diff --git a/chromium/chrome/browser/ui/android/management/BUILD.gn b/chromium/chrome/browser/ui/android/management/BUILD.gn
index 1cdae297083..79c05e023cf 100644
--- a/chromium/chrome/browser/ui/android/management/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/management/BUILD.gn
@@ -15,6 +15,7 @@ android_library("java") {
]
deps = [
":java_resources",
+ "//base:base_java",
"//chrome/browser/enterprise/util:java",
"//chrome/browser/profiles/android:java",
"//chrome/browser/tab:java",
@@ -37,7 +38,6 @@ android_resources("java_resources") {
]
deps = [
"//chrome/app:chromium_strings",
- "//chrome/app:google_chrome_strings",
"//chrome/browser/ui/android/strings:ui_strings_grd",
"//components/browser_ui/styles/android:java_resources",
"//components/strings:components_locale_settings_grd",
diff --git a/chromium/chrome/browser/ui/android/multiwindow/BUILD.gn b/chromium/chrome/browser/ui/android/multiwindow/BUILD.gn
index 99765fb6be6..f7e51c4382b 100644
--- a/chromium/chrome/browser/ui/android/multiwindow/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/multiwindow/BUILD.gn
@@ -102,6 +102,7 @@ android_library("javatests") {
deps = [
":java",
":java_resources",
+ "//base:base_java",
"//base:base_java_test_support",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
@@ -119,5 +120,6 @@ android_library("javatests") {
"//third_party/mockito:mockito_java",
"//ui/android:ui_full_java",
"//ui/android:ui_java_test_support",
+ "//url:gurl_java",
]
}
diff --git a/chromium/chrome/browser/ui/android/night_mode/BUILD.gn b/chromium/chrome/browser/ui/android/night_mode/BUILD.gn
index e8aeb02e80b..79facb6f269 100644
--- a/chromium/chrome/browser/ui/android/night_mode/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/night_mode/BUILD.gn
@@ -86,6 +86,7 @@ android_library("unit_device_javatests") {
":night_mode_java_test_support",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/browser/feature_engagement:java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
@@ -98,6 +99,7 @@ android_library("unit_device_javatests") {
"//components/feature_engagement/public:public_java",
"//content/public/android:content_full_java",
"//content/public/test/android:content_java_test_support",
+ "//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
@@ -120,6 +122,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/browser/feature_engagement:java",
"//chrome/browser/feedback/android:java",
"//chrome/browser/flags:java",
diff --git a/chromium/chrome/browser/ui/android/omnibox/BUILD.gn b/chromium/chrome/browser/ui/android/omnibox/BUILD.gn
index c2160d311a1..cb1ccdec044 100644
--- a/chromium/chrome/browser/ui/android/omnibox/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -58,7 +58,10 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java",
"java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java",
"java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java",
+ "java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxDrawableState.java",
+ "java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplier.java",
"java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java",
+ "java/src/org/chromium/chrome/browser/omnibox/styles/SuggestionSpannable.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteControllerProvider.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java",
@@ -70,7 +73,6 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfo.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListManager.java",
- "java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcher.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java",
@@ -102,12 +104,10 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProperties.java",
- "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessor.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutView.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleVerticalLayoutView.java",
- "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionDrawableState.java",
+ "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SpacingRecyclerViewItemDecoration.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionLayout.java",
- "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionSpannable.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java",
@@ -131,6 +131,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderView.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinder.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewProperties.java",
+ "java/src/org/chromium/chrome/browser/omnibox/suggestions/history_clusters/HistoryClustersProcessor.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/AlignmentManager.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java",
@@ -166,6 +167,7 @@ android_library("java") {
"//chrome/browser/prefetch/android:java",
"//chrome/browser/privacy:java",
"//chrome/browser/profiles/android:java",
+ "//chrome/browser/quick_delete:java",
"//chrome/browser/search_engines/android:java",
"//chrome/browser/share:java",
"//chrome/browser/signin/services/android:java",
@@ -189,6 +191,8 @@ android_library("java") {
"//components/browser_ui/widget/android:java",
"//components/browser_ui/widget/android:java_resources",
"//components/content_settings/android:content_settings_enums_java",
+ "//components/content_settings/android:java",
+ "//components/embedder_support/android:simple_factory_key_java",
"//components/embedder_support/android:util_java",
"//components/externalauth/android:java",
"//components/favicon/android:java",
@@ -214,8 +218,10 @@ android_library("java") {
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_collection_collection_java",
"//third_party/androidx:androidx_core_core_java",
+ "//third_party/androidx:androidx_interpolator_interpolator_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/metrics_proto:metrics_proto_java",
+ "//ui/accessibility:ax_base_java",
"//ui/android:ui_full_java",
"//ui/android:ui_utils_java",
"//ui/base/mojom:mojom_java",
@@ -325,6 +331,10 @@ android_resources("java_resources") {
"java/res/drawable/ic_wb_sunny_round.xml",
"java/res/drawable/logo_partly_cloudy.xml",
"java/res/drawable/logo_translate_round.xml",
+ "java/res/drawable/omnibox_button_highlight_layer_list.xml",
+ "java/res/drawable/omnibox_button_ripple.xml",
+ "java/res/drawable/status_view_highlight_layer_list.xml",
+ "java/res/drawable/status_view_ripple.xml",
"java/res/drawable/switch_to_tab.xml",
"java/res/drawable/trending_up_black_24dp.xml",
"java/res/layout-sw600dp/location_bar.xml",
@@ -335,7 +345,6 @@ android_resources("java_resources") {
"java/res/layout/location_status_incognito_badge.xml",
"java/res/layout/omnibox_answer_suggestion.xml",
"java/res/layout/omnibox_basic_suggestion.xml",
- "java/res/layout/omnibox_entity_suggestion.xml",
"java/res/layout/omnibox_results_container.xml",
"java/res/layout/suggestions_tile_view.xml",
"java/res/layout/url_action_container.xml",
@@ -374,6 +383,8 @@ android_library("unit_device_javatests") {
"//base:base_java_test_support",
"//chrome/android:chrome_app_java_resources",
"//chrome/browser/flags:java",
+ "//chrome/browser/profiles/android:java",
+ "//chrome/browser/tab:java",
"//chrome/browser/ui/android/toolbar:java",
"//chrome/test/android:chrome_java_test_support_common",
"//components/browser_ui/site_settings/android:java",
@@ -387,6 +398,7 @@ android_library("unit_device_javatests") {
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
@@ -415,13 +427,14 @@ robolectric_library("junit") {
"java/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTest.java",
"java/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTrackerTest.java",
"java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java",
+ "java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxImageSupplierUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProviderTest.java",
+ "java/src/org/chromium/chrome/browser/omnibox/styles/SuggestionSpannableUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteControllerProviderUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManagerUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListManagerUnitTest.java",
- "java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcherUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/PreWarmingRecycledViewPoolTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHorizontalDividerTest.java",
@@ -433,14 +446,16 @@ robolectric_library("junit") {
"java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextNewLayoutUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/ActionChipsAdapterUnitTest.java",
+ "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/ActionChipsProcessorUnitTest.java",
+ "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/ActionChipsViewUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java",
- "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/HistoryClustersProcessorTest.java",
+ "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutViewTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleVerticalLayoutViewTest.java",
- "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SuggestionSpannableUnitTest.java",
+ "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SpacingRecyclerViewItemDecorationUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionProcessorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionSelectionManagerUnitTest.java",
@@ -448,9 +463,11 @@ robolectric_library("junit") {
"java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/dividerline/DividerLineProcessorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/dividerline/DividerLineViewBinderUnitTest.java",
- "java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java",
+ "java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java",
+ "java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinderUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinderUnitTest.java",
+ "java/src/org/chromium/chrome/browser/omnibox/suggestions/history_clusters/HistoryClustersProcessorTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/AlignmentManagerUnitTest.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessorUnitTest.java",
@@ -464,6 +481,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//build/android:build_java",
"//chrome/android:base_module_java",
"//chrome/android:chrome_app_java_resources",
@@ -493,6 +511,7 @@ robolectric_library("junit") {
"//components/browser_ui/theme/android:java_resources",
"//components/browser_ui/widget/android:java",
"//components/content_settings/android:content_settings_enums_java",
+ "//components/content_settings/android:java",
"//components/embedder_support/android:util_java",
"//components/externalauth/android:java",
"//components/favicon/android:java",
@@ -505,12 +524,14 @@ robolectric_library("junit") {
"//components/security_state/core:security_state_enums_java",
"//components/signin/public/android:java",
"//components/signin/public/android:signin_java_test_support",
+ "//components/ukm/android:java",
"//components/user_prefs/android:java",
"//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//testing/android/junit:junit_test_support",
"//third_party/android_deps:espresso_java",
"//third_party/android_deps:material_design_java",
+ "//third_party/android_deps:protobuf_lite_runtime_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_core_core_java",
@@ -523,11 +544,11 @@ robolectric_library("junit") {
"//third_party/junit",
"//third_party/metrics_proto:metrics_proto_java",
"//third_party/mockito:mockito_java",
+ "//ui/accessibility:ax_base_java",
"//ui/android:ui_java_test_support",
"//ui/android:ui_no_recycler_view_java",
"//ui/android:ui_recycler_view_java",
"//url:gurl_java",
- "//url:gurl_junit_shadows",
"//url:gurl_junit_test_support",
]
resources_package = "org.chromium.chrome.browser.omnibox.test"
diff --git a/chromium/chrome/browser/ui/android/page_insights/BUILD.gn b/chromium/chrome/browser/ui/android/page_insights/BUILD.gn
index f4d343a687b..eec4a777ee8 100644
--- a/chromium/chrome/browser/ui/android/page_insights/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/page_insights/BUILD.gn
@@ -7,19 +7,41 @@ import("//build/config/android/rules.gni")
android_library("java") {
sources = [
+ "java/src/org/chromium/chrome/browser/page_insights/PageInsightsActionHandlerImpl.java",
+ "java/src/org/chromium/chrome/browser/page_insights/PageInsightsActivator.java",
"java/src/org/chromium/chrome/browser/page_insights/PageInsightsCoordinator.java",
+ "java/src/org/chromium/chrome/browser/page_insights/PageInsightsDataLoader.java",
"java/src/org/chromium/chrome/browser/page_insights/PageInsightsMediator.java",
"java/src/org/chromium/chrome/browser/page_insights/PageInsightsSheetContent.java",
+ "java/src/org/chromium/chrome/browser/page_insights/PageInsightsSurfaceScopeDependencyProviderImpl.java",
+ "java/src/org/chromium/chrome/browser/page_insights/PageInsightsSwaaChecker.java",
]
+ srcjar_deps = [ ":jni_headers" ]
+
deps = [
":java_resources",
+ ":proto_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//chrome/browser/browser_controls/android:java",
+ "//chrome/browser/flags:java",
+ "//chrome/browser/preferences:java",
+ "//chrome/browser/profiles/android:java",
+ "//chrome/browser/share:java",
+ "//chrome/browser/signin/services/android:java",
+ "//chrome/browser/sync/android:java",
"//chrome/browser/tab:java",
+ "//chrome/browser/xsurface:java",
+ "//chrome/browser/xsurface_provider:java",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/bottomsheet/android:manager_java",
+ "//components/browser_ui/share/android:java",
"//components/browser_ui/widget/android:java",
+ "//components/sync/android:sync_java",
+ "//content/public/android:content_full_java",
+ "//third_party/android_deps:protobuf_lite_runtime_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//ui/android:ui_no_recycler_view_java",
"//url:gurl_java",
@@ -27,10 +49,16 @@ android_library("java") {
resources_package = "org.chromium.chrome.browser.page_insights"
}
+generate_jni("jni_headers") {
+ sources = [ "java/src/org/chromium/chrome/browser/page_insights/PageInsightsSwaaChecker.java" ]
+}
+
android_resources("java_resources") {
sources = [
"java/res/drawable/page_insights_icon.xml",
+ "java/res/layout/page_insights_sheet_content.xml",
"java/res/layout/page_insights_sheet_toolbar.xml",
+ "java/res/values/dimens.xml",
]
deps = [
"//chrome/browser/ui/android/strings:ui_strings_grd",
@@ -39,6 +67,52 @@ android_resources("java_resources") {
]
}
+proto_java_library("proto_java") {
+ proto_path = "java/src/org/chromium/chrome/browser/page_insights/proto"
+ sources = [ "$proto_path/page_insights.proto" ]
+}
+
+robolectric_library("junit") {
+ sources = [
+ "java/src/org/chromium/chrome/browser/page_insights/PageInsightsActivatorUnitTest.java",
+ "java/src/org/chromium/chrome/browser/page_insights/PageInsightsMediatorTest.java",
+ "java/src/org/chromium/chrome/browser/page_insights/PageInsightsSwaaCheckerUnitTest.java",
+ ]
+ deps = [
+ ":java",
+ ":jni_headers",
+ ":proto_java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//base:base_junit_test_support",
+ "//base:jni_java",
+ "//chrome/browser/browser_controls/android:java",
+ "//chrome/browser/flags:java",
+ "//chrome/browser/preferences:java",
+ "//chrome/browser/profiles/android:java",
+ "//chrome/browser/share:java",
+ "//chrome/browser/signin/services/android:java",
+ "//chrome/browser/sync/android:java",
+ "//chrome/browser/tab:java",
+ "//chrome/browser/xsurface:java",
+ "//chrome/browser/xsurface_provider:java",
+ "//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/bottomsheet/android:manager_java",
+ "//components/browser_ui/bottomsheet/android/test:java",
+ "//components/browser_ui/share/android:java",
+ "//components/dom_distiller/core/android:dom_distiller_core_java",
+ "//components/sync/android:sync_java",
+ "//content/public/android:content_full_java",
+ "//content/public/test/android:content_java_test_support",
+ "//third_party/android_deps:com_google_protobuf_protobuf_javalite_java",
+ "//third_party/androidx:androidx_test_runner_java",
+ "//third_party/junit",
+ "//third_party/mockito:mockito_java",
+ "//ui/android:ui_no_recycler_view_java",
+ "//url:gurl_java",
+ ]
+}
+
android_library("unit_device_javatests") {
testonly = true
resources_package = "org.chromium.chrome.browser.page_insights"
@@ -55,7 +129,10 @@ android_library("unit_device_javatests") {
"//chrome/android:chrome_java",
"//chrome/browser/browser_controls/android:java",
"//chrome/browser/flags:java",
+ "//chrome/browser/share:java",
"//chrome/browser/tab:java",
+ "//chrome/browser/xsurface:java",
+ "//chrome/browser/xsurface_provider:java",
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/bottomsheet/android:factory_java",
"//components/browser_ui/bottomsheet/android:java",
@@ -69,5 +146,6 @@ android_library("unit_device_javatests") {
"//ui/android:ui_java_test_support",
"//ui/android:ui_no_recycler_view_java",
"//ui/android:ui_utils_java",
+ "//url:gurl_java",
]
}
diff --git a/chromium/chrome/browser/ui/android/quickactionsearchwidget/BUILD.gn b/chromium/chrome/browser/ui/android/quickactionsearchwidget/BUILD.gn
index f850bb22a56..367da3a1dc3 100644
--- a/chromium/chrome/browser/ui/android/quickactionsearchwidget/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/quickactionsearchwidget/BUILD.gn
@@ -46,6 +46,7 @@ android_library("javatests") {
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//url:gurl_java",
diff --git a/chromium/chrome/browser/ui/android/signin/BUILD.gn b/chromium/chrome/browser/ui/android/signin/BUILD.gn
index 33bda2a74b9..039b7e969c0 100644
--- a/chromium/chrome/browser/ui/android/signin/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/signin/BUILD.gn
@@ -32,6 +32,7 @@ android_library("java") {
"//components/sync/android:sync_java",
"//components/user_prefs/android:java",
"//content/public/android:content_java",
+ "//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
@@ -148,6 +149,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/android/features/start_surface:public_java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
@@ -192,6 +194,7 @@ android_library("javatests") {
"//chrome/browser/ui/android/signin:java_resources",
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/bottomsheet/android:java",
+ "//components/browser_ui/bottomsheet/android:manager_java",
"//components/signin/core/browser:signin_enums_java",
"//components/signin/public/android:java",
"//components/signin/public/android:signin_java_test_support",
@@ -225,6 +228,7 @@ android_library("unit_device_javatests") {
":java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//chrome/browser/flags:java",
"//chrome/browser/preferences:java",
"//chrome/browser/profiles/android:java",
@@ -240,6 +244,7 @@ android_library("unit_device_javatests") {
"//components/signin/public/android:java",
"//components/signin/public/android:signin_java_test_support",
"//components/user_prefs/android:java",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chromium/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 2722feea2d0..c348b93b3f0 100644
--- a/chromium/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chromium/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -204,6 +204,11 @@ CHAR_LIMIT guidelines:
<messages fallback_to_english="true">
<!-- NOTE: Generic strings used across multiple features belong in //components/browser_ui/strings/android. -->
+ <!-- Cookie Controls -->
+ <message name="IDS_COOKIE_CONTROLS_IPH_MESSAGE" desc="In-context Product Help - Appears right under the page info icon to highlight the Cookie Controls entrypoint.">
+ Site not working? Third-party cookies are blocked
+ </message>
+
<!-- Main Preferences -->
<message name="IDS_PREFS_SECTION_BASICS" desc='Title of "Basics" section of preferences. [CHAR_LIMIT=32]'>
Basics
@@ -352,7 +357,7 @@ CHAR_LIMIT guidelines:
No preloading
</message>
<message name="IDS_PRELOAD_PAGES_NO_PRELOADING_SUMMARY" desc="Short explanation of what the no preloading mode does in the Preload Pages setting.">
- Pages load only after you open them.
+ Pages load only after you open them
</message>
<message name="IDS_PRELOAD_PAGES_STANDARD_PRELOADING_TITLE" desc="Name of the standard preloading option for the Preload Pages settings page. This option enables preloading pages that Chrome believes the user is likely to navigate to. [CHAR_LIMIT=32]">
Standard preloading
@@ -521,12 +526,6 @@ CHAR_LIMIT guidelines:
<message name="IDS_AUTOFILL_DESCRIBE_LOCAL_COPY" desc="Text label that describes a Wallet credit card which has been copied to the local Chrome instance.">
Copied to Chrome
</message>
- <message name="IDS_AUTOFILL_PAYMENTS_AUTHENTICATOR_SELECTION_DIALOG_TITLE" desc="Title for the dialog where the user selects an authenticator from a list of options.">
- Verify your card
- </message>
- <message name="IDS_AUTOFILL_PAYMENTS_AUTHENTICATOR_SELECTION_DIALOG_HEADER" desc="Header for the dialog where the user selects an authenticator from a list of options.">
- Your bank wants to confirm it\u2019s you.
- </message>
<message name="IDS_AUTOFILL_PAYMENTS_AUTHENTICATOR_SELECTION_DIALOG_FOOTER" desc="Footer for the dialog where the user selects an authenticator from a list of options.">
Not seeing your current info? Please contact your bank to update it.
</message>
@@ -557,6 +556,10 @@ CHAR_LIMIT guidelines:
<message name="IDS_AUTOFILL_CARD_EDITOR_VIRTUAL_CARD_TURN_OFF_BUTTON_LABEL" desc="Label shown on the button that the user clicks on to unenroll from virtual cards.">
Turn off
</message>
+ <!-- TODO(crbug.com/1425882): Use finalized string.-->
+ <message translateable="false" name="IDS_AUTOFILL_OPTIONS_TITLE" desc="Description below switch toggling the use of third-party autofill in Chrome." formatter_data="android_java">
+ Autofill Options
+ </message>
<!-- Payment Request section preview strings -->
<message name="IDS_PAYMENT_REQUEST_PAYMENT_METHODS_PREVIEW" desc="This is a snippet of a payment method a user has saved to Chrome, plus an indication of the number of additional payment methods the user has saved.
@@ -582,11 +585,11 @@ CHAR_LIMIT guidelines:
</message>
<!-- Password Manager -->
- <message name="IDS_PASSWORD_SETTINGS_TITLE" desc="Title for the settings screen with saved passwords and the header for the list of saved passwords in that screen. [CHAR_LIMIT=32]">
+ <message name="IDS_PASSWORD_LIST_TITLE" desc="Title for a list of saved passwords, e.g. the list of passwords displayed in Chrome > Settings > Password Manager. [CHAR_LIMIT=32]">
Passwords
</message>
- <message name="IDS_PASSWORD_SETTINGS_TITLE_GPM" desc="Title for the settings menu item leading to the Password Manager UI surface. This allows users to e.g. manage their saved passwords, toggle saving and auto-sign-in on/off, etc. The NEW label is displayed until the user clicks on this menu item at least once. [CHAR_LIMIT=32]">
- Password Manager <ph name="BEGIN_NEW">&lt;new&gt;</ph>New<ph name="END_NEW">&lt;/new&gt;</ph>
+ <message name="IDS_PASSWORD_MANAGER_SETTINGS_TITLE" desc="Title for the settings menu item leading to the Password Manager UI surface. This allows users to e.g. manage their saved passwords, toggle saving and auto-sign-in on/off, etc. [CHAR_LIMIT=32]">
+ Password Manager
</message>
<message name="IDS_PASSWORD_SETTINGS_SAVE_PASSWORDS" desc="Title for the checkbox toggling whether passwords are saved or not. [CHAR_LIMIT=32]">
Save passwords
@@ -600,6 +603,12 @@ CHAR_LIMIT guidelines:
<message name="IDS_PASSWORDS_LEAK_DETECTION_SWITCH_TITLE" desc="Title for the switch toggling whether Chrome should check that entered credentials have been part of a leak.">
Warn you if passwords are exposed in a data breach
</message>
+ <message name="IDS_PASSWORDS_LEAK_DETECTION_SWITCH_TITLE_UPDATED" desc="Title for the switch toggling whether Chrome should check that entered credentials have been part of a leak.">
+ Warn you if a password was compromised in a data breach
+ </message>
+ <message name="IDS_PASSWORDS_LEAK_DETECTION_SWITCH_SUMMARY" desc="Summary for the switch toggling whether Chrome should check that entered credentials have been part of a leak.">
+ When you use a password, Chrome warns you if it has been published online. When doing this, your passwords and usernames are encrypted, so they can’t be read by anyone, including Google.
+ </message>
<message name="IDS_PASSWORDS_CHECK_TITLE" desc="Title for the check passwords button which allows to check whether the user's passwords have been compromised.">
Check passwords
</message>
@@ -739,7 +748,7 @@ CHAR_LIMIT guidelines:
We’re changing how passwords are saved on this device
</message>
<message name="IDS_PASSWORD_MIGRATION_WARNING_SUBTITLE" desc="The subtitle of the password migration warning sheet." formatter_data="android_java">
- Your lists of saved passwords for Chrome and Chrome <ph name="ERROR_DESCRIPTION">%1$s<ex>Dev</ex></ph> will merge after version 121. You’ll be able to autofill all your saved passwords on both apps.
+ Right now, passwords saved on this device cannot be used across Chrome channels. After Chrome 121, passwords saved on your device for Chrome and <ph name="CHROME_CHANNEL">%1$s<ex>Chrome Dev</ex></ph> will be merged and can be used in both apps.
</message>
<message name="IDS_PASSWORD_MIGRATION_WARNING_ACKNOWLEDGE" desc="The text on the button that acknowledges the password migration warning.">
Got it
@@ -763,7 +772,7 @@ CHAR_LIMIT guidelines:
Export &amp; delete passwords saved to this device
</message>
<message name="IDS_PASSWORD_MIGRATION_WARNING_PASSWORD_EXPORT_SUBTITLE" desc="The subtitle that explaint the password export option in the password migration warning sheet.">
- All passwords will be downloaded on your device and removed from Chrome <ph name="CHROME_CHANNEL">%1$s<ex>Dev</ex></ph>
+ All passwords will be downloaded on your device and removed from <ph name="CHROME_CHANNEL">%1$s<ex>Chrome Dev</ex></ph>
</message>
<message name="IDS_PASSWORD_MIGRATION_WARNING_NEXT" desc="The text for the next button in the password migration warning sheet. It starts one of the flows offered on the sheet.">
Next
@@ -774,12 +783,27 @@ CHAR_LIMIT guidelines:
<message name="IDS_EXPORTED_PASSWORDS_DELETION_DIALOG_TITLE" desc="The title of the dialog that offers to delete passwords from Google Password Manager." formatter_data="android_java">
Delete passwords from Google Password Manager?
</message>
+ <message name="IDS_EXPORTED_PASSWORDS_DELETION_IN_PROGRESS_TITLE" desc="The title of the progress bar that is visible while the passwords are being deleted." formatter_data="android_java">
+ Deleting
+ </message>
+ <message name="IDS_PASSWORDS_EXPORT_IN_PROGRESS_TITLE" desc="The title of the progress bar that is visible while the passwords are being exported." formatter_data="android_java">
+ Exporting
+ </message>
<message name="IDS_EXPORTED_PASSWORDS_DELETION_DIALOG_TEXT" desc="The text that describes the option to delete the passwords that were just exported." formatter_data="android_java">
- Your passwords will be deleted from Google Password Manager for Chrome <ph name="CHROME_CHANNEL">%1$s<ex>Dev</ex></ph>. You will keep the passwords file you just downloaded.
+ Your passwords will be deleted from Google Password Manager for <ph name="CHROME_CHANNEL">%1$s<ex>Chrome Dev</ex></ph>. You will keep the passwords file you just downloaded.
</message>
<message name="IDS_EXPORTED_PASSWORDS_DELETE_BUTTON" desc="The text on the button that confirms the action of deleting the passwords that were exported." formatter_data="android_java">
Delete passwords
</message>
+ <message name="IDS_CHROME_CHANNEL_NAME_CANARY" desc="The Chrome channel name for canary channel">
+ Chrome Canary
+ </message>
+ <message name="IDS_CHROME_CHANNEL_NAME_DEV" desc="The Chrome channel name for dev channel">
+ Chrome Dev
+ </message>
+ <message name="IDS_CHROME_CHANNEL_NAME_BETA" desc="The Chrome channel name for beta channel">
+ Chrome Beta
+ </message>
<!-- Lock Screen Fragment -->
<message name="IDS_LOCKSCREEN_DESCRIPTION_COPY" desc="When a user attempts to copy a password for a particular website into clipboard in Chrome's settings, Chrome launches a lock screen to verify the user's identity and displays the following explanation.">
@@ -1197,10 +1221,10 @@ Private state tokens improve privacy on the web and can’t be used to find out
You can block sites you don’t want. Chrome also auto-deletes sites from the list that are older than 30 days. <ph name="BEGIN_LINK">&lt;link&gt;</ph>Learn more<ph name="END_LINK">&lt;/link&gt;</ph>
</message>
<message name="IDS_SETTINGS_FLEDGE_PAGE_CURRENT_SITES_DESCRIPTION_DISABLED" desc="1 of 3 possible descriptions that appear beneath the 'Sites' title. If this setting is off, no sites appear. The link opens a dialog box that provides more information about Site-suggested ads.">
- When on, a list of sites you visit that guess your interests appears here.
+ When on, a list of sites you visit that guess your interests appears here
</message>
<message name="IDS_SETTINGS_FLEDGE_PAGE_CURRENT_SITES_DESCRIPTION_EMPTY" desc="1 of 3 possible descriptions that appear beneath the 'Sites' title. This setting could be on but no topics appear in the list. This text explains why.">
- It can take up to a week for a list of sites to appear here based on your browsing history.
+ No sites to show right now
</message>
<message name="IDS_SETTINGS_FLEDGE_PAGE_SEE_ALL_SITES_LABEL" desc="Text that serves as a button. Because the list of sites might be long, we only show the most recent sites. This button allows the user to browse all sites (because sites are auto-deleted every 4 weeks, the list is never older than 4 weeks).">
See all sites
@@ -1348,16 +1372,16 @@ Your Google account may have other forms of browsing history like searches and a
Cookies and site data
</message>
<message name="IDS_CLEAR_COOKIES_AND_SITE_DATA_SUMMARY_BASIC" desc="A summary for the 'Cookies and site data' option in the 'Clear Browsing Data' screen, explaining that deleting cookies and site data will sign the user out of most websites.">
- Signs you out of most sites.
+ Signs you out of most sites
</message>
<message name="IDS_CLEAR_COOKIES_AND_SITE_DATA_SUMMARY_BASIC_SIGNED_IN" desc="A summary for the 'Cookies and site data' option in the 'Clear Browsing Data' screen, explaining that deleting cookies and site data will sign the user out of most websites but you Google sign in will stay.">
Signs you out of most sites. You won't be signed out of your Google Account.
</message>
<message name="IDS_CLEAR_BROWSING_HISTORY_SUMMARY" desc="A text for the basic tab explaining browsing history.">
- Clears history, including in the search box.
+ Clears history, including in the search box
</message>
<message name="IDS_CLEAR_BROWSING_HISTORY_SUMMARY_SYNCED_NO_LINK" desc="A text for the basic tab explaining browsing history for users with history sync. This version is shown when the link to MyActivity is displayed separately.">
- Clears history from all synced devices.
+ Clears history from all synced devices
</message>
<message name="IDS_CLEAR_SEARCH_HISTORY_LINK" desc="Text informing the user that they can clear search history and other data using MyActivity.">
<ph name="BEGIN_LINK1">&lt;link1&gt;</ph>Search history<ph name="END_LINK1">&lt;/link1&gt;</ph> and <ph name="BEGIN_LINK2">&lt;link2&gt;</ph>other forms of activity<ph name="END_LINK2">&lt;/link2&gt;</ph> may be saved in your Google Account when you’re signed in. You can delete them anytime.
@@ -1392,6 +1416,9 @@ Your Google account may have other forms of browsing history like searches and a
<message name="IDS_CLEAR_BROWSING_DATA_TAB_PERIOD_TITLE" desc="Label of the dropdown that selects the time range for which browsing data will be deleted.">
Time range
</message>
+ <message name="IDS_CLEAR_BROWSING_DATA_TAB_PERIOD_15_MINUTES" desc="The option to delete browsing data from the last 15 minutes.">
+ Last 15 minutes
+ </message>
<message name="IDS_CLEAR_BROWSING_DATA_TAB_PERIOD_HOUR" desc="The option to delete browsing data from the last hour.">
Last hour
</message>
@@ -1475,6 +1502,18 @@ Your Google account may have other forms of browsing history like searches and a
<message name="IDS_PRIVACY_GUIDE_MSBB_ITEM_FOUR" desc="Fourth bullet point that describes the details of the MSBB toggle.">
If you also share Chrome usage reports, those reports include the URLs you visit
</message>
+ <message name="IDS_PRIVACY_GUIDE_MSBB_ITEM_TWO_V3" desc="Second bullet point that describes the details of the MSBB toggle.">
+ From the address bar, you can open page info to see additional info about the page you're visiting
+ </message>
+ <message name="IDS_PRIVACY_GUIDE_MSBB_ITEM_THREE_V3" desc="Third bullet point that describes the details of the MSBB toggle.">
+ If you also save your bookmarks in your Google Account, you can track product prices in Chrome and get notified when the price drops
+ </message>
+ <message name="IDS_PRIVACY_GUIDE_MSBB_ITEM_FOUR_V3" desc="Forth bullet point that describes the details of the MSBB toggle.">
+ URLs you visit are sent to Google to predict what sites you might visit next and to show you additional info about the page you're visiting
+ </message>
+ <message name="IDS_PRIVACY_GUIDE_MSBB_ITEM_FIVE_V3" desc="Fifth bullet point that describes the details of the MSBB toggle.">
+ If you also share Chrome usage reports, those reports include the URLs you visit
+ </message>
<message name="IDS_PRIVACY_GUIDE_HISTORY_SYNC_TOGGLE" desc="Title for the history sync toggle.">
History sync
</message>
@@ -1491,10 +1530,10 @@ Your Google account may have other forms of browsing history like searches and a
Choose when to block third-party cookies
</message>
<message name="IDS_PRIVACY_GUIDE_COOKIES_BLOCK_INCOGNITO_TITLE" desc="Title for the radio button for blocking third-party cookies in incognito.">
- Block while using incognito
+ Block while using Incognito
</message>
<message name="IDS_PRIVACY_GUIDE_COOKIES_BLOCK_INCOGNITO_DESCRIPTION" desc="Description for the radio button for blocking third-party cookies in incognito.">
- While in incognito, sites can’t use your cookies to see your browsing activity across different sites, for example, to personalize ads. Features on some sites may break.
+ While in Incognito, sites can’t use your cookies to see your browsing activity across different sites, for example, to personalize ads. Features on some sites may break.
</message>
<message name="IDS_PRIVACY_GUIDE_COOKIES_BLOCK_ALWAYS_TITLE" desc="Title for the radio button for blocking third-party cookies always.">
Block all the time
@@ -1556,9 +1595,15 @@ Your Google account may have other forms of browsing history like searches and a
<message name="IDS_PRIVACY_GUIDE_SB_STANDARD_ITEM_TWO" desc="Second bullet point that describes the details of the Safe Browsing control.">
Checks URLs with a list of unsafe sites stored in Chrome
</message>
+ <message name="IDS_PRIVACY_GUIDE_SB_STANDARD_ITEM_TWO_PROXY" desc="Second bullet point that describes the details of the Safe Browsing control with proxy.">
+ Sends an obfuscated portion of the URL to Google through a privacy server that hides your IP address
+ </message>
<message name="IDS_PRIVACY_GUIDE_SB_STANDARD_ITEM_THREE" desc="Third bullet point that describes the details of the Safe Browsing control.">
If a site tries to steal your password, or when you download a harmful file, Chrome may send URLs including bits of page content to Safe Browsing
</message>
+ <message name="IDS_PRIVACY_GUIDE_SB_STANDARD_ITEM_THREE_PROXY" desc="Third bullet point that describes the details of the Safe Browsing control with proxy.">
+ If a site tries to steal your password, or when you download a harmful file, Chrome may send URLs including bits of page content to Google
+ </message>
<message name="IDS_PRIVACY_GUIDE_SB_ENHANCED_ITEM_ONE" desc="First bullet point that describes the details of the Safe Browsing control.">
Predicts and warns you about dangerous events before they happen
</message>
@@ -1580,6 +1625,15 @@ Your Google account may have other forms of browsing history like searches and a
<message name="IDS_PRIVACY_GUIDE_SB_ENHANCED_ITEM_SEVEN" desc="Seventh bullet point that describes the details of the Safe Browsing control.">
Temporarily links this data to your Google Account when you’re signed in, to protect you across Google apps
</message>
+ <message name="IDS_PRIVACY_GUIDE_SEARCH_SUGGESTIONS_ITEM_ONE" desc="First bullet point that describes the details of the search suggestions toggle.">
+ When you tap or type in the address bar or search box, you'll see suggestions from your default search engine. This is off in Incognito.
+ </message>
+ <message name="IDS_PRIVACY_GUIDE_SEARCH_SUGGESTIONS_ITEM_TWO" desc="Second bullet point that describes the details of the search suggestions toggle.">
+ What you type in the address bar or search box is sent to your default search engine
+ </message>
+ <message name="IDS_PRIVACY_GUIDE_SEARCH_SUGGESTIONS_ITEM_THREE" desc="Third bullet point that describes the details of the search suggestions toggle.">
+ Depending on your settings, Chrome may also send cookies and your current URL
+ </message>
<!-- Safety check -->
<message name="IDS_PREFS_SAFETY_CHECK" desc="Title of the Safety check element in settings, allowing the user to check multiple areas of browser safety. [CHAR_LIMIT=32]">
@@ -1694,52 +1748,103 @@ Your Google account may have other forms of browsing history like searches and a
<message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_SUMMARY" desc="Summary for Safe Browsing enhanced protection mode.">
Faster, proactive protection against dangerous websites, downloads, and extensions. Warns you about password breaches. Requires browsing data to be sent to Google.
</message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_SUMMARY_UPDATED" desc="Summary for Safe Browsing enhanced protection mode.">
+ Real-time, proactive protection against dangerous sites, downloads, and extensions that’s based on your browsing data getting sent to Google
+ </message>
<message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_TITLE" desc="Title for Safe Browsing standard protection mode. [CHAR_LIMIT=32]">
Standard protection
</message>
<message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_SUMMARY" desc="Summary for Safe Browsing standard protection mode.">
Standard protection against websites, downloads, and extensions that are known to be dangerous.
</message>
+ <message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_SUMMARY_UPDATED" desc="Summary for Safe Browsing standard protection mode.">
+ Protects against sites, downloads, and extensions that are known to be dangerous. If a page does something suspicious, URLs and bits of page content are sent to Google Safe Browsing.
+ </message>
+ <message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_SUMMARY_UPDATED_PROXY" desc="Summary for Safe Browsing standard protection mode with proxy.">
+ Protects against sites, downloads, and extensions that are known to be dangerous. When you visit a site, Chrome sends an obfuscated portion of the URL to Google through a privacy server that hides your IP address. If a site does something suspicious, full URLs and bits of page content are also sent.
+ </message>
<message name="IDS_SAFE_BROWSING_NO_PROTECTION_TITLE" desc="Title for Safe Browsing no protection mode. [CHAR_LIMIT=32]">
No protection (not recommended)
</message>
<message name="IDS_SAFE_BROWSING_NO_PROTECTION_SUMMARY" desc="Summary for Safe Browsing no protection mode.">
Does not protect you against dangerous websites, downloads, and extensions. You’ll still get Safe Browsing protection, where available, in other Google services, like Gmail and Search.
</message>
+ <message name="IDS_SAFE_BROWSING_NO_PROTECTION_SUMMARY_UPDATED" desc="Summary for Safe Browsing no protection mode.">
+ Does not protect you against dangerous websites, downloads, and extensions. Your Safe Browsing settings in other Google products won't be affected.
+ </message>
<!-- Safe Browsing enhanced protection preferences -->
<message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_SUBTITLE" desc="Subtitle for Safe Browsing enhanced protection mode.">
Enhanced protection:
</message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_SUBTITLE_UPDATED" desc="Subtitle for Safe Browsing enhanced protection mode.">
+ Enhanced protection
+ </message>
<message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_ONE" desc="First bullet point under the Safe Browsing enhanced protection mode">
Predicts and warns you about dangerous events before they happen.
</message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_ONE_UPDATED" desc="First bullet point that describes the details of the Safe Browsing control.">
+ Warns you about dangerous sites, even ones Google didn’t know about before, by analyzing more data from sites than standard protection. You can choose to skip Chrome warnings.
+ </message>
<message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO" desc="Second bullet point under the Safe Browsing enhanced protection mode">
Keeps you safe on Chrome and may be used to improve your security in other Google apps when you are signed in.
</message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_TWO_UPDATED" desc="Second bullet point that describes the details of the Safe Browsing control.">
+ In-depth scans for suspicious downloads.
+ </message>
<message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_THREE" desc="Third bullet point under the Safe Browsing enhanced protection mode">
Improves security for you and everyone on the web.
</message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_THREE_UPDATED" desc="Third bullet point that describes the details of the Safe Browsing control.">
+ When you're signed in, protects you across Google services.
+ </message>
<message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FOUR" desc="Fourth bullet point under the Safe Browsing enhanced protection mode">
Warns you if passwords are exposed in a data breach.
</message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FOUR_UPDATED" desc="Fourth bullet point that describes the details of the Safe Browsing control.">
+ Improves security for you and everyone on the web.
+ </message>
<message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FIVE" desc="Fifth bullet point under the Safe Browsing enhanced protection mode">
Sends URLs to Safe Browsing to check them. Also sends a small sample of pages, downloads, extension activity, and system information to help discover new threats. Temporarily links this data to your Google Account when you’re signed in, to protect you across Google apps.
</message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_FIVE_UPDATED" desc="Fifth bullet point that describes the details of the Safe Browsing control.">
+ Warns you if you use a password that has been compromised in a data breach.
+ </message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_SIX_UPDATED" desc="Sixth bullet point that describes the details of the Safe Browsing control.">
+ Sends the URLs of sites you visit and a small sample of page content, downloads, extension activity, and system information to Google Safe Browsing to check if they’re harmful.
+ </message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_SEVEN_UPDATED" desc="Seventh bullet point that describes the details of the Safe Browsing control.">
+ When you’re signed in, this data is linked to your Google Account to protect you across Google services, for example increasing protection in Gmail after a security incident.
+ </message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_BULLET_EIGHT_UPDATED" desc="Eighth bullet point that describes the details of the Safe Browsing control.">
+ Doesn’t noticeably slow down your browser or device.
+ </message>
+ <message name="IDS_SAFE_BROWSING_ENHANCED_PROTECTION_LEARN_MORE_LABEL" desc="The text for a link to a help center article that gives more information about Safe Browsing.">
+ Learn more about <ph name="BEGIN_LINK">&lt;link&gt;</ph>how Chrome keeps your data private<ph name="END_LINK">&lt;/link&gt;</ph>
+ </message>
<!-- Safe Browsing standard protection preferences -->
<message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_SUBTITLE" desc="Subtitle for Safe Browsing standard protection mode.">
Standard protection:
</message>
+ <message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_SUBTITLE_UPDATED" desc="Subtitle for Safe Browsing standard protection mode.">
+ Standard protection
+ </message>
<message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_BULLET_ONE" desc="First bullet point under the Safe Browsing standard protection mode.">
Detects and warns you about dangerous events when they happen.
</message>
<message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_BULLET_TWO" desc="Second bullet point under the Safe Browsing standard protection mode.">
Checks URLs with a list of unsafe sites stored in Chrome. If a site tries to steal your password, or when you download a harmful file, Chrome may also send URLs, including bits of page content, to Safe Browsing.
</message>
+ <message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_BULLET_TWO_PROXY" desc="Second bullet point under the Safe Browsing standard protection mode when proxy is enabled.">
+ Sends an obfuscated portion of the URL to Google through a privacy server that hides your IP address. If a site tries to steal your password, or when you download a harmful file, Chrome may also send URLs, including bits of page content, to Google.
+ </message>
<message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_EXTENDED_REPORTING_TITLE" desc="Title for Safe Browsing extended reporting.">
Help improve security on the web
</message>
+ <message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_EXTENDED_REPORTING_TITLE_UPDATED" desc="Title for Safe Browsing extended reporting.">
+ Help improve security on the web for everyone
+ </message>
<message name="IDS_SAFE_BROWSING_STANDARD_PROTECTION_EXTENDED_REPORTING_SUMMARY" desc="Summary for Safe Browsing extended reporting.">
Sends URLs of some pages you visit, limited system information, and some page content to Google, to help discover new threats and protect everyone on the web.
</message>
@@ -1795,9 +1900,6 @@ Your Google account may have other forms of browsing history like searches and a
<message name="IDS_LANGUAGES_ITEM_OPTION_OFFER_TO_TRANSLATE" desc="Option in language item menu. User can click the 'Offer to translate' option to toggle whether they want Chrome to translate pages in this language. [CHAR_LIMIT=32]">
Offer to translate
</message>
- <message name="IDS_LANGUAGES_EXPLICIT_ASK_TITLE" desc="Title of the dialog that explicitly asks the user which languages they can read.">
- What languages do you read?
- </message>
<message name="IDS_LANGUAGES_SRP_TITLE" desc="Title of the dialog that explicitly asks the user what language they want Chrome on Android to be in.">
Pick Chrome’s language
</message>
@@ -2149,7 +2251,12 @@ Your Google account may have other forms of browsing history like searches and a
<message name="IDS_NTP_RECENT_TABS_SYNC_PROMO_INSTRUCTIONS" desc="Information about sync displayed on the NTP when the user has signed in on mobile but not on desktop">
Tabs that you've opened in Chrome on your other devices will appear here.
</message>
-
+ <message name="IDS_RECENT_TABS_NO_TABS_EMPTY_STATE" desc="Text appearing on an empty recent tab page that indicates that recently closed tabs will appear here">
+ You’ll find your recent tabs here
+ </message>
+ <message name="IDS_RECENT_TABS_SIGN_IN_ON_OTHER_DEVICES" desc="Text appearing on an empty recent tab page that indicates user can see tabs from other devices here">
+ To see your tabs from wherever you use Chrome, sign in on all your devices
+ </message>
<message name="IDS_SEARCH_AND_BROWSE_CATEGORY" desc="Category to show the search and browse improvement toggle. [CHAR_LIMIT=32]">
Search and browse
</message>
@@ -2356,9 +2463,6 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_ACCESSIBILITY_TAB_SWITCHER_INCOGNITO_STACK_SELECTED" desc="Announcement when the Incognito tab stack is selected.">
Switched to Incognito tabs
</message>
- <message name="IDS_ACCESSIBILITY_TAB_SWITCHER_UNDO_TAB_CLOSED" desc="Content description for the undo tab closed 'button.">
- Reopen closed tab
- </message>
<message name="IDS_ACCESSIBILITY_UNDO_CLOSED_TAB_ANNOUNCEMENT_MESSAGE" desc="Message spoken out loud for accessibility when a user decides to undo a closed tab.">
Restored <ph name="TAB_TITLE">%1$s<ex>YouTube</ex></ph>, tab
</message>
@@ -2368,9 +2472,6 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_ACCESSIBILITY_TABSTRIP_TAB" desc="Content description for the tab title.">
<ph name="TAB_TITLE">%1$s<ex>Welcome to Facebook</ex></ph>, tab
</message>
- <message name="IDS_ACCESSIBILITY_TABSTRIP_TAB_SELECTED" desc="Content description for the tab title of the currently selected tab.">
- <ph name="TAB_TITLE">%1$s<ex>Welcome to Facebook</ex></ph>, tab, selected
- </message>
<message name="IDS_ACCESSIBILITY_TABSTRIP_BTN_CLOSE_TAB" desc="Content description for the close tab button.">
Close <ph name="TAB_TITLE">%1$s<ex>Google</ex></ph> tab
</message>
@@ -2431,6 +2532,10 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
Translate…
</message>
+ <message name="IDS_MENU_LISTEN_TO_THIS_PAGE" desc="Menu item for making the browser read out the page's text in spoken audio form. [CHAR_LIMIT=27]">
+ Listen to this page
+ </message>
+
<message name="IDS_MENU_PRINT" desc="Menu item for printing the current page. [CHAR_LIMIT=27]">
Print…
</message>
@@ -2457,10 +2562,7 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
</message>
<!-- Password manager dialogs -->
- <message name="IDS_PASSWORD_GENERATION_DIALOG_TITLE" desc="Text shown in a modal dialog that displays a generated password. This dialog is triggered by the user requesting to generate a password.">
- Suggested password
- </message>
- <message name="IDS_PASSWORD_GENERATION_DIALOG_TITLE_UPM_BRANDED" desc="Text shown in a modal dialog that displays a generated password. This dialog is triggered by the user requesting to generate a password. Unified Password Manager version.">
+ <message name="IDS_PASSWORD_GENERATION_DIALOG_TITLE" desc="Text shown in a modal dialog that displays a generated password. This dialog is triggered by the user requesting to generate a password. Unified Password Manager version.">
Use strong password?
</message>
<message name="IDS_PASSWORD_GENERATION_DIALOG_CANCEL_BUTTON" desc="Text for the cancel button belonging to the modal dialog that displays a generated password. Can be used by the user to cancel the password generation flow.">
@@ -2871,7 +2973,7 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
{FILE_COUNT, plural, =1 {# Page} other {# Pages}}
</message>
<message name="IDS_DOWNLOAD_PAGE" desc="Download this page.">
- Download page
+ Download this page
</message>
<message name="IDS_DOWNLOAD_MANAGER_NO_DOWNLOADS" desc="Text appearing on an empty tab that indicates that downloaded files appear on this tab.">
Files that you download appear here
@@ -2927,6 +3029,9 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_DOWNLOAD_MANAGER_EXPLORE_OFFLINE" desc="Tab text for the offline content in download home which contains recommended offline content for the user.">
Explore Offline
</message>
+ <message name="IDS_DOWNLOAD_FILE_TYPE_NOT_SUPPORTED" desc="Error message shown to user when a file can't de downloaded due to it's file format (e.g. .pdf, .docx, .apk).">
+ Cannot download file. File format not supported.
+ </message>
<!-- Browsing History UI -->
<message name="IDS_HISTORY_MANAGER_EMPTY" desc="Indicates that there are no browsing history items.">
@@ -3081,21 +3186,35 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
Your info is secured with a profile lock
</message>
<message name="IDS_DEVICE_LOCK_DESCRIPTION" desc="Text description explaining that a profile lock is required for automotive devices to protect data privacy. 'Sync' is short for synchronization.">
- To sync your info and keep your data secure in the car, you must create a profile lock. You’ll use a code or password every time you enter the car.
+ To sync your Chrome info and keep your data secure in the car, you must create a profile lock. You’ll use a code or password every time you enter the car.
</message>
<message name="IDS_DEVICE_LOCK_THROUGH_SETTINGS_DESCRIPTION" desc="Text description explaining that a profile lock is required for automotive devices to protect data privacy, and that such a lock can be created in the device's security settings. 'Sync' is short for synchronization.">
- To sync your info and keep your data secure in the car, you must create a profile lock in your security settings. You’ll use a code or password every time you enter the car.
+ To sync your Chrome info and keep your data secure in the car, you must create a profile lock in your security settings. You’ll use a code or password every time you enter the car.
</message>
<message name="IDS_DEVICE_LOCK_EXISTING_LOCK_DESCRIPTION" desc="Text description explaining to users who have already set an existing lock that a profile lock is required for automotive devices to protect data privacy. 'Sync' is short for synchronization.">
Your profile lock keeps your info secure in the car, including synced passwords, payments and more.
</message>
<message name="IDS_DEVICE_LOCK_NOTICE" desc="Notice appearing on the profile lock page informing users that data saved on Chrome will be erased if they remove the profile lock from the device.">
- Your saved info will be erased if you remove profile lock later.
+ Your saved data will be erased if the profile lock is removed later.
</message>
<message name="IDS_DEVICE_LOCK_CREATE_LOCK_BUTTON" desc="Text for the button that navigates the user to create a profile lock on the device.">
Create a profile lock
</message>
+ <!-- Strings for a missing device lock. -->
+ <message name="IDS_MISSING_DEVICE_LOCK_TITLE" translateable="false" desc="">
+ Continue without a car profile lock?
+ </message>
+ <message name="IDS_MISSING_DEVICE_LOCK_DESCRIPTION" translateable="false" desc="">
+ Opening Chrome without a profile lock will remove your your saved passwords and payment methods from the car. Using a profile lock keeps this data secure.
+ </message>
+ <message name="IDS_MISSING_DEVICE_LOCK_REMOVE_LOCAL_DATA" translateable="false" desc="">
+ Also clear bookmarks, history, and more from this car
+ </message>
+ <message name="IDS_DELETE_AND_CONTINUE" translateable="false" desc="">
+ Delete &amp; continue
+ </message>
+
<!-- Strings for Streamlined Signin and Unified Consent. -->
<message name="IDS_SIGNIN_TITLE" desc="Title for the screen that asks users to sign-in and turn on Sync. [CHAR_LIMIT=27]">
Turn on sync?
@@ -3145,6 +3264,12 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_SIGNIN_ACCOUNT_PICKER_DIALOG_TITLE" desc="The title for the dialog that shows the list of accounts on the device and asks the user to select one of these accounts. [CHAR_LIMIT=27]">
Choose an account
</message>
+ <message name="IDS_SIGNIN_ACCOUNT_PICKER_DESCRIPTION_WITH_EMAIL" is_accessibility_with_no_ui="true" desc="The content description for the account picker. Opens the 'Choose an account' dialog.">
+ Choose an account. Currently selected <ph name="EMAIL">%1$s<ex>john.doe@example.com</ex></ph>.
+ </message>
+ <message name="IDS_SIGNIN_ACCOUNT_PICKER_DESCRIPTION" is_accessibility_with_no_ui="true" desc="The content description for the account picker. Opens the 'Choose an account' dialog. Used when the user's email cannot be displayed.">
+ Choose an account.
+ </message>
<message name="IDS_SIGNIN_ACCOUNT_PICKER_BOTTOM_SHEET_TITLE_FOR_SEND_TAB_TO_SELF" desc="The title for the bottom sheet that shows the list of accounts on the device and asks the user to select one of these accounts, when send-tab-to-self triggered the UI. [CHAR_LIMIT=27]">
Sign in to Chrome
</message>
@@ -3530,7 +3655,7 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
New tab
</message>
<message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_PREFERENCE_SHARE" desc="Title name for the share option in the preference.">
- Share
+ Share this page
</message>
<message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_PREFERENCE_VOICE_SEARCH" desc="Title name for the voice search option in the preference.">
Voice search
@@ -3541,6 +3666,9 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_PREFERENCE_TRANSLATE" desc="Title name for the translate option in the preference.">
Translate
</message>
+ <message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_PREFERENCE_READ_ALOUD" desc="Title name for the read aloud option in the preference.">
+ Listen to this page
+ </message>
<message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_PREFERENCE_BASED_ON_YOUR_USAGE" desc="Title name for the based on your usage option in the preference.">
Based on your usage
</message>
@@ -3565,10 +3693,13 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_TRANSLATE_IPH" desc="An in-product-help message for the voice search button.">
Quickly translate this page. To edit this shortcut, touch and hold.
</message>
+ <message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_READ_ALOUD_IPH" desc="An in-product-help message for the Listen to this page button.">
+ Listen to this page. To edit this shortcut, touch and hold.
+ </message>
<message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_NEW_TAB_IPH_SETTINGS" desc="An in-product-help message for the new tab button referring to toolbar settings.">
Quickly open a new tab. To edit this shortcut, go to Settings.
</message>
- <message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_SHARE_IPH_SETTINGS" desc="An in-product-help message for the share button referring to toolbar settings.">
+ <message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_SHARE_IPH_SETTINGS" desc="An in-product-help message for the share button referring to toolbar settings.">
Quickly share this page. To edit this shortcut, go to Settings.
</message>
<message name="IDS_ADAPTIVE_TOOLBAR_BUTTON_VOICE_SEARCH_IPH_SETTINGS" desc="An in-product-help message for the voice search button referring to toolbar settings.">
@@ -3633,8 +3764,8 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_MENU_HISTORY" desc="Menu item for opening the history page. [CHAR_LIMIT=27]">
History
</message>
- <message name="IDS_MENU_QUICK_DELETE" desc="Menu item for quick delete functionality that allows users to quickly delete their last 15 minutes browsing data. [CHAR_LIMIT=27]">
- Delete last 15 minutes
+ <message name="IDS_MENU_QUICK_DELETE" desc="Menu item for quick delete functionality that allows users to delete their browsing data. [CHAR_LIMIT=27]">
+ Clear browsing data
</message>
<message name="IDS_MENU_DOWNLOADS" desc="Menu item for opening the downloads page. [CHAR_LIMIT=27]">
Downloads
@@ -3783,6 +3914,9 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_BOOKMARK_TOOLBAR_SEARCH" desc="Button text for bookmark search action on the bookmark action bar [CHAR_LIMIT=32]">
Search your bookmarks
</message>
+ <message name="IDS_BOOKMARK_TOOLBAR_SEARCH_TITLE" desc="Toolbar search title [CHAR_LIMIT=32]">
+ Search
+ </message>
<message name="IDS_BOOKMARK_NO_RESULT" desc="Text explaining that no bookmarks are found to match a search query">
Can’t find that bookmark. Check your spelling or add a new bookmark.
</message>
@@ -3881,12 +4015,18 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_SORT_SUBMENU" desc="Sub-menu in the bookmarks manager for sort/view options. [CHAR_LIMIT=24]">
Sort and view options
</message>
+ <message name="IDS_SORT_BY_MANUAL" desc="Option in the bookmarks manager to sort manually. [CHAR_LIMIT=24]">
+ Sort by manual order
+ </message>
<message name="IDS_SORT_BY_NEWEST" desc="Option in the bookmarks manager to sort by newest. [CHAR_LIMIT=24]">
Sort by newest
</message>
<message name="IDS_SORT_BY_OLDEST" desc="Option in the bookmarks manager to sort by newest. [CHAR_LIMIT=24]">
Sort by oldest
</message>
+ <message name="IDS_SORT_BY_LAST_OPENED" desc="Option in the bookmarks manager to sort by last opened. [CHAR_LIMIT=24]">
+ Sort by last opened
+ </message>
<message name="IDS_SORT_BY_ALPHA" desc="Option in the bookmarks manager to sort alphabetically. A and Z should be substituted with the first/last letter in the localized language. [CHAR_LIMIT=24]">
Sort by A to Z
</message>
@@ -3902,6 +4042,15 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_CREATE_NEW_FOLDER" desc="Option in the bookmarks manager to show the create a new folder in the current context. [CHAR_LIMIT=24]">
Create new folder
</message>
+ <message name="IDS_FOLDER_PICKER_ROOT" desc="Toolbar title for when the user is selecting a folder and the current selected folder is the root.">
+ Move to\u2026
+ </message>
+ <message name="IDS_FOLDER_PICKER_MOVE_HERE" desc="Button the user clicks to move the given bookmark (or folder) to the selected folder.">
+ Move here
+ </message>
+ <message name="IDS_FOLDER_PICKER_CANCEL" desc="Button the user clicks to cancel the move action.">
+ Cancel
+ </message>
<!-- Read later strings -->
<message name="IDS_READING_LIST_READ" desc="The header for the read section in the reading list UI.">
@@ -3991,13 +4140,13 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
Tab opened in background.
</message>
<message name="IDS_MENU_OPEN_IN_CHROME" desc="Context sensitive menu item for opening a link in Chrome. [CHAR_LIMIT=30]">
- Open in Chrome
+ Open in Chrome browser
</message>
<message name="IDS_MENU_OPEN_IN_INCOGNITO_CHROME" desc="Context sensitive menu item for opening a link in Chrome in an Incognito tab. [CHAR_LIMIT=30]" translateable="false">
Open in Incognito Chrome
</message>
- <message name="IDS_MENU_OPEN_IN_PRODUCT" desc="App menu item for opening link in the browser. [CHAR_LIMIT=30]">
- Open in <ph name="PRODUCT_NAME">%1$s<ex>Chrome</ex></ph>
+ <message name="IDS_MENU_OPEN_IN_PRODUCT" desc="App menu item for opening link in the browser. [CHAR_LIMIT=40]">
+ Open in <ph name="PRODUCT_NAME">%1$s<ex>Chrome</ex></ph> browser
</message>
<message name="IDS_MENU_OPEN_IN_PRODUCT_DEFAULT" desc="Default title for menu item for opening link in browser. [CHAR_LIMIT=30]">
Open in browser
@@ -4048,9 +4197,6 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_RDS_SITE_SETTINGS_GENERIC_IPH_TEXT_MOBILE" desc="Message on the generic text bubble shown to educate the user about using the desktop site site-level setting to switch to mobile mode.">
You can request the mobile site for <ph name="HOST_NAME">%1$s<ex>www.example.com</ex></ph>
</message>
- <message name="IDS_RDS_SITE_SETTINGS_SPECIFIC_IPH_TEXT" desc="Message on the text bubble shown on specific websites more functional in desktop mode to educate the user about using the desktop site site-level setting.">
- Switch to desktop site for a better view of this site
- </message>
<!-- Accessibility -->
@@ -4079,6 +4225,12 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
Incognito mode
</message>
+ <message name="IDS_TABSWITCHER_NO_TABS_EMPTY_STATE" desc="Text appearing on an empty tab switcher that indicates that tabs opened will appear here.">
+ You’ll find your tabs here
+ </message>
+ <message name="IDS_TABSWITCHER_NO_TABS_OPEN_TO_VISIT_DIFFERENT_PAGES" desc="Text appearing on an empty tab switcher that indicates that users can open tabs here to visit different pages.">
+ Open tabs to visit different pages at the same time
+ </message>
<message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_TABSWITCHER_TOGGLE_DEFAULT" desc="Placeholder content description for the button that enters or leaves the tab switcher.">
Switch or close tabs
</message>
@@ -4086,19 +4238,19 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
Manage account
</message>
<message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_IDENTITY_DISC_WITH_NAME_AND_EMAIL" desc="The content description for a toolbar button displaying user profile picture. Tapping on this button navigates to 'Sync and Google services' page in settings">
- Signed in as <ph name="USER_NAME">%1$s<ex>Peter Parker</ex></ph>. <ph name="USER_EMAIL">%2$s<ex>peter.parker@gmail.com</ex></ph>. Button. Opens settings.
+ Signed in as <ph name="USER_NAME">%1$s<ex>Peter Parker</ex></ph>. <ph name="USER_EMAIL">%2$s<ex>peter.parker@gmail.com</ex></ph>. Opens settings.
</message>
<message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_IDENTITY_DISC_WITH_NAME" desc="The content description for a toolbar button displaying user profile picture, for users who don't have a readable email address. Tapping on this button navigates to 'Sync and Google services' page in settings">
- Signed in as <ph name="USER_NAME">%s<ex>Bruce Wayne</ex></ph>. Button. Opens settings.
+ Signed in as <ph name="USER_NAME">%s<ex>Bruce Wayne</ex></ph>. Opens settings.
</message>
<message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_SIGNED_OUT_IDENTITY_DISC" desc="The content description for a toolbar button displaying the signed-out avatar. Tapping on this button navigates to 'Turn on Sync' page in settings">
- Signed out. Button. Opens dialog to sign in and turn on sync.
+ Signed out. Opens dialog to sign in and turn on sync.
</message>
<message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_HOME" desc="Content description for the home button.">
- Home
+ Open the home page
</message>
<message name="IDS_ACCESSIBILITY_BTN_REFRESH" desc="Content description for the refresh page button.">
- Refresh page
+ Reload this page
</message>
<message name="IDS_ACCESSIBILITY_BTN_STOP_LOADING" desc="Content description for the stop loading page button.">
Stop page loading
@@ -4334,6 +4486,15 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_PAGE_INSIGHTS_SHEET_CLOSED" desc="Accessibility string read when the page insights sheet is closed.">
Page insights bottom sheet is closed
</message>
+ <message name="IDS_PAGE_INSIGHTS_HUB_TITLE_TEXT" desc="The title of Page Insights Sheet.">
+ Related Insights
+ </message>
+ <message name="IDS_PAGE_INSIGHTS_BACK_BUTTON_DESCRIPTION" desc="The content description of back button in page insights sheet.">
+ Page insights back button
+ </message>
+ <message name="IDS_PAGE_INSIGHTS_LOADING_INDICATOR_DESCRIPTION" desc="The loading indicator for Page Insights Hub while the related content is being fetched from server.">
+ Page insights Loading Indicator
+ </message>
<!-- WebFeed -->
<message name="IDS_WEB_FEED_FOLLOW_LOADING_DESCRIPTION" desc="The content description of the loading spinner after the user presses Follow and is waiting for a response.">
@@ -4372,9 +4533,15 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_WEB_FEED_POST_FOLLOW_DIALOG_STORIES_READY_DESCRIPTION" desc="The body text of the dialog presented a few times after a successful Web Feed follow request, when content from that Web Feed is immediately available. Please use the branded term for Discover, as listed under Product Names in the Google Glossary Manager (TC ID 1799975766543019278).">
You'll now see stories from <ph name="SITE_NAME">%1$s<ex>Reuters</ex></ph> when you open a new tab. Sites you follow are saved in your Google account. You can manage them in Discover settings.
</message>
+ <message name="IDS_WEB_FEED_POST_FOLLOW_DIALOG_STORIES_READY_DESCRIPTION_WITH_UI_UPDATE" desc="The body text of the dialog presented a few times after a successful Web Feed follow request, when content from that Web Feed is immediately available.">
+ You'll now see content from and about <ph name="SITE_NAME">%1$s<ex>Reuters</ex></ph> in Following. The sites and searches you follow are saved in your Google Account. You can manage your follows in settings at any time.
+ </message>
<message name="IDS_WEB_FEED_POST_FOLLOW_DIALOG_STORIES_NOT_READY_DESCRIPTION" desc="The body text of the dialog presented a few times after a successful Web Feed follow request, when content from that Web Feed is not readily available. Please use the branded term for Discover, as listed under Product Names in the Google Glossary Manager (TC ID 1799975766543019278).">
Soon, you’ll see stories from <ph name="SITE_NAME">%1$s<ex>Reuters</ex></ph> when you open a new tab. Sites you follow are saved in your Google account. You can manage them in Discover settings.
</message>
+ <message name="IDS_WEB_FEED_POST_FOLLOW_DIALOG_STORIES_NOT_READY_DESCRIPTION_WITH_UI_UPDATE" desc="The body text of the dialog presented a few times after a successful Web Feed follow request, when content from that Web Feed is not readily available.">
+ Soon, you'll see content from and about <ph name="SITE_NAME">%1$s<ex>Reuters</ex></ph> in Following. The sites and searches you follow are saved in your Google Account. You can manage your follows in settings at any time.
+ </message>
<message name="IDS_WEB_FEED_POST_FOLLOW_DIALOG_GO_TO_FOLLOWING" desc="The main action of the dialog presented a few times after a successful Web Feed follow request, when content from from that Web Feed is immediately available. It will take the user to the Following feed.">
Go to Following
</message>
@@ -5177,10 +5344,6 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
Share
</message>
- <message name="IDS_QR_CODE_SCAN_TAB_LABEL" desc="Scan tab label for QR Code sharing activity.">
- Scan
- </message>
-
<message name="IDS_QR_CODE_SHARE_DESCRIPTION" desc="Text on QR Code sharing tab describing how to use the QR Code.">
To share with people nearby, let them scan this QR Code
</message>
@@ -5288,46 +5451,10 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
GIF Copied
</message>
- <message name="IDS_QR_CODE_CAMERA_FRAMING_RECT_DESCRIPTION" desc="Text under the framing rectangle in QR Code camera preview.">
- Position QR Code/barcode in this frame.
- </message>
-
- <message name="IDS_QR_CODE_PERMISSION_DESCRIPTION" desc="Text on QR Code sharing tab indicating that permissions need to be given.">
- To scan a QR Code, let Chrome use your camera
- </message>
-
- <message name="IDS_QR_CODE_OPEN_SETTINGS_DESCRIPTION" desc="Text on QR Code sharing tab indicating that user needs to open settings to give camera permission.">
- To scan a QR Code, change your settings so that Chrome can use your camera
- </message>
-
- <message name="IDS_QR_CODE_PERMISSION_CONTINUE_LABEL" desc="Text on button on QR Code sharing tab triggering camera permission dialog.">
- Continue
- </message>
-
- <message name="IDS_QR_CODE_NO_CAMERA_ERROR" desc="Error text shown when no camera is found.">
- To scan a QR Code, use a device with a camera.
- </message>
-
- <message name="IDS_QR_CODE_DISABLED_CAMERA_ERROR" desc="Error text shown when the camera is disabled by the device policy manager.">
- The organization that manages your device has turned off your camera.
- </message>
-
- <message name="IDS_QR_CODE_IN_USE_CAMERA_ERROR" desc="Error text shown when the camera is in use by other apps.">
- Can't open your camera. Restart your device and try again.
- </message>
-
- <message name="IDS_QR_CODE_HARDWARE_CAMERA_ERROR" desc="Error text shown when the camera cannot start due to a hardware error.">
- Can't open your camera. Something went wrong.
- </message>
-
<message name="IDS_QR_CODE_OPEN_SETTINGS_LABEL" desc="Text on button on QR Code sharing tab triggering Android settings.">
Open Settings
</message>
- <message name="IDS_QR_CODE_NOT_A_URL_LABEL" desc="Text on toast on QR Code scanning tab indicating that the QR Code does not correspond to a URL and can not be opened.">
- This QR Code is not a URL: <ph name="QrCodeValue">%1$s<ex>QR Code Text</ex></ph>
- </message>
-
<message name="IDS_QR_CODE_FILENAME_PREFIX" desc="File name prefix for downloaded qrcode that is followed by timestamp.">
chrome_qrcode_<ph name="CURRENT_TIMESTAMP_MS">%1$s<ex>1582667748515</ex></ph>
</message>
@@ -5581,6 +5708,12 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_ACCOUNT_SELECTION_SHEET_CLOSED" desc="Accessibility string read when the Account Selection bottom sheet showing a list of the user's accounts is closed." is_accessibility_with_no_ui="true">
Sign in bottom sheet is closed.
</message>
+ <message name="IDS_IDP_SIGNIN_STATUS_MISMATCH_DIALOG_BODY" desc="Body for mismatch dialog which is shown to prompt the user to sign in to a website using an account from an identity provider." translateable="false">
+ You can use your <ph name="IDENTITY_PROVIDER_ETLD_PLUS_ONE">%1$s<ex>idp.com</ex></ph> account on this site. To continue, sign in to <ph name="IDENTITY_PROVIDER_ETLD_PLUS_ONE">%1$s<ex>idp.com</ex></ph>.
+ </message>
+ <message name="IDS_IDP_SIGNIN_STATUS_MISMATCH_DIALOG_CONTINUE" desc="Title of the button that allows the user to continue with signing in to an account from an identity provider." translateable="false">
+ Continue
+ </message>
<message name="IDS_VERIFY_SHEET_TITLE_AUTO_REAUTHN" desc="Header for verify sheet for auto re-authentication.">
Signing you in…
</message>
@@ -5596,7 +5729,7 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
chrome_stylized_highlight_
</message>
<message name="IDS_CONTENT_CREATION_NOTE_TEMPLATE_SELECTED" desc="Announcment string for accessibility when selected template changes.">
- <ph name="TEMPLATE_TITLE">%1$s<ex>Classic</ex> template selected</ph>
+ <ph name="TEMPLATE_TITLE">%1$s<ex>Classic</ex></ph> template selected
</message>
<message name="IDS_CONTENT_CREATION_NOTE_DIALOG_DESCRIPTION" desc="Accessibility information for note template picking dialog.">
Select a template for your highlight.
@@ -5698,24 +5831,6 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_PRICE_DROP_SPOTTED_IPH" desc="This text appears in the IPH when a user opens a new tab page and an item in any of their open tabs has a price drop.">
Price drop spotted
</message>
-
- <!-- Restore Custom Tab strings -->
- <message name="IDS_RESTORE_CUSTOM_TAB_TITLE" desc="Appears in pop-up message title when the previous Custom Tab session a user engaged with can be restored.">
- Resume your last task?
- </message>
- <message name="IDS_RESTORE_CUSTOM_TAB_DESCRIPTION" desc="Appears in pop-up message description when the previous Custom Tab session a user engaged with can be restored and in pop-up which gives the option to undo that restoration.">
- Easily continue where you left off
- </message>
- <message name="IDS_RESTORE_CUSTOM_TAB_BUTTON_TEXT" desc="Appears as pop-up message button text to direct the user to restore a previously engaged with Custom Tab session.">
- Restore
- </message>
- <message name="IDS_UNDO_RESTORATION_TITLE" desc="Appears in pop-up message title when a Custom Tab session is restored, giving the user the option to undo the restoration.">
- Task resumed
- </message>
- <message name="IDS_UNDO_RESTORATION_BUTTON_TEXT" desc="Appears in pop-up message button text when a Custom Tab session is restored, giving the user the option to undo the restoration.">
- Undo
- </message>
-
<!-- Partial Custom Tab accessibility -->
<message name="IDS_ACCESSIBILITY_PARTIAL_CUSTOM_TAB_BOTTOM_SHEET" desc="Content description for partial custom tab of bottom sheet type">
Bottom sheet
@@ -5728,24 +5843,65 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
</message>
<!-- Quick Delete strings -->
- <message name="IDS_QUICK_DELETE_DIALOG_TITLE" desc="Title of the dialog when asking users to confirm deleting the last 15 minutes of browsing data.">
- Delete last 15 minutes?
- </message>
- <message name="IDS_QUICK_DELETE_DIALOG_DESCRIPTION" desc="Description of the dialog when asking users to confirm deleting the last 15 minutes of browsing data.">
- History from the last 15 minutes will be deleted:
+ <message name="IDS_QUICK_DELETE_DIALOG_TITLE" desc="Title of the dialog when asking users to confirm deleting browsing data.">
+ Clear browsing data
</message>
- <message name="IDS_QUICK_DELETE_DIALOG_TABS_CLOSED_TEXT" desc="Text indicating that the tabs which a user recently used to visit a website within 15 minutes would be closed.">
+ <message name="IDS_QUICK_DELETE_DIALOG_TABS_CLOSED_TEXT" desc="Text indicating that the tabs which a user recently used to visit a website within the specified time range would be closed.">
{NUM_TABS, plural, =1 {1 tab on this device} other {# tabs on this device}}
</message>
+ <message name="IDS_QUICK_DELETE_DIALOG_ZERO_TABS_CLOSED_ALL_TIME_TEXT" desc="Text indicating that no tabs would be closed.">
+ No tabs on this device
+ </message>
+ <message name="IDS_QUICK_DELETE_DIALOG_ZERO_TABS_CLOSED_TEXT" desc="Text indicating that no tabs within the specified time range would be closed.">
+ No tabs from the <ph name="TIME_PERIOD">%1$s<ex>last 15 minutes</ex></ph>
+ </message>
+ <message name="IDS_QUICK_DELETE_DIALOG_BROWSING_HISTORY_DOMAIN_COUNT_TEXT" desc="This text would start by mentioning the last visited domain, and, then followed by showing the count of other unique domains that the user has visited. For example: if a user visits/revisits facebook.com, followed by figma.com, within the specified time range, then this text would say 'figma.com + 1 site'.">
+ {DOMAIN_COUNT, plural,
+ =1 {+ 1 site}
+ other {+ # sites}}
+ </message>
+ <message name="IDS_QUICK_DELETE_DIALOG_ZERO_BROWSING_HISTORY_DOMAIN_COUNT_ALL_TIME_TEXT" desc="Text indicating that no websites were visited from the beginning of time.">
+ No sites
+ </message>
+ <message name="IDS_QUICK_DELETE_DIALOG_ZERO_BROWSING_HISTORY_DOMAIN_COUNT_TEXT" desc="Text indicating that no websites were visited within the specified time range.">
+ No sites from the <ph name="TIME_PERIOD">%1$s<ex>last 15 minutes</ex></ph>
+ </message>
+ <message name="IDS_QUICK_DELETE_DIALOG_BROWSING_HISTORY_SECONDARY_TEXT" desc="Secondary text for browsing history item for syncing users, indicating that there could be more domains on other devices that the user has visited within the specified time range, which would be deleted from history.">
+ More on synced devices
+ </message>
<message name="IDS_QUICK_DELETE_DIALOG_COOKIES_CACHE_AND_OTHER_SITE_DATA_TEXT" desc="Text indicating that browsing data like cookies, cache, and other site data would be deleted.">
Cookies, cache, and other site data
</message>
- <message name="IDS_QUICK_DELETE_DIALOG_SEARCH_HISTORY_DISAMBIGUATION_TEXT" desc="Text for signed in users only in the Quick Delete dialog, that is shown when the user clicks on 'Delete last 15 minutes' option in the three dots menu, informing signed in users that search history and other forms of Activity saved in their Google account will not be deleted.">
+ <message name="IDS_QUICK_DELETE_DIALOG_SEARCH_HISTORY_DISAMBIGUATION_TEXT" desc="Text for signed in users only in the Quick Delete dialog, that is shown when the user clicks on 'Clear browsing data' option in the three dots menu, informing signed in users that search history and other forms of Activity saved in their Google account will not be deleted.">
<ph name="BEGIN_LINK1">&lt;link1&gt;</ph>Search history<ph name="END_LINK1">&lt;/link1&gt;</ph> and <ph name="BEGIN_LINK2">&lt;link2&gt;</ph>other forms of activity<ph name="END_LINK2">&lt;/link2&gt;</ph> may be saved in your Google Account
</message>
- <message name="IDS_QUICK_DELETE_SNACKBAR_MESSAGE" desc="Text inside the snackbar which is shown once the user confirms deletion via the 'Delete last 15 minutes' option present inside the three dots menu.">
+ <message name="IDS_QUICK_DELETE_DIALOG_MORE_OPTIONS_BUTTON_TEXT" desc="Text inside the more options button which redirects users to the advanced page of 'Clear Browsing Data' where the users can customize more deletion options.">
+ More options
+ </message>
+ <message name="IDS_QUICK_DELETE_DIALOG_DATA_PENDING" desc="Text that is shown when data in the dialog is being fetched.">
+ Calculating...
+ </message>
+ <message name="IDS_QUICK_DELETE_SNACKBAR_MESSAGE" desc="Text inside the snackbar which is shown once the user confirms deletion via the 'Clear browsing data' option present inside the three dots menu.">
+ <ph name="TIME_PERIOD">%1$s<ex>Last 15 minutes</ex></ph> deleted
+ </message>
+ <message name="IDS_QUICK_DELETE_SNACKBAR_ALL_TIME_MESSAGE" desc="Text inside the snackbar which is shown once the user confirms deletion via the 'Clear browsing data' option present inside the three dots menu.">
Deleted
</message>
+ <message name="IDS_QUICK_DELETE_TIME_PERIOD_15_MINUTES" desc="The option to delete browsing data from the last 15 minutes.">
+ last 15 minutes
+ </message>
+ <message name="IDS_QUICK_DELETE_TIME_PERIOD_HOUR" desc="The option to delete browsing data from the last hour.">
+ last hour
+ </message>
+ <message name="IDS_QUICK_DELETE_TIME_PERIOD_24_HOURS" desc="The option to delete browsing data from the last 24 hours.">
+ last 24 hours
+ </message>
+ <message name="IDS_QUICK_DELETE_TIME_PERIOD_7_DAYS" desc="The option to delete browsing data from the last seven days.">
+ last 7 days
+ </message>
+ <message name="IDS_QUICK_DELETE_TIME_PERIOD_FOUR_WEEKS" desc="The option to delete browsing data from the last 4 weeks.">
+ last 4 weeks
+ </message>
<!-- Password generation bottom sheet -->
<message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_CONTENT_DESCRIPTION" desc="Accessibility string read when the bottom sheet is opened. It describes that the bottom sheet proposes a generated password to be automatically filled into the sign up form and saved to the Password Manager.">
@@ -5769,6 +5925,44 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
<message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_DISMISS_BUTTON" desc="Appears on the button, which reflects that the user has rejected the proposed generated password and decided to continue with creating their own password.">
Create my own
</message>
+ <message name="IDS_PLEASE_WAIT_PROGRESS_MESSAGE" desc='Message on the progress dialog used when waiting for an operation to complete.'>
+ Please wait…
+ </message>
+
+ <!-- Read Aloud ("Listen to this page") player strings -->
+ <message name="IDS_READALOUD_CLOSE_PLAYER_BUTTON_CONTENT_DESCRIPTION" desc="Accessibility string for the button that closes the 'Listen to this page' player.">
+ Tap to close “Listen to this page”.
+ </message>
+ <message name="IDS_READALOUD_PLAYER_NAME" desc="Name of the Read Aloud player for accessiblity.">
+ “Listen to this page” player.
+ </message>
+ <message name="IDS_READALOUD_PLAYER_OPENED_AT_FULL_HEIGHT" desc="Accessibility string spoken when the 'Listen to this page' player bottom sheet expands to its full height.">
+ “Listen to this page” player opened at full height.
+ </message>
+ <message name="IDS_READALOUD_PLAYER_MINIMIZED" desc="Accessibility string spoken when the 'Listen to this page' player bottom sheet shrinks to its minimized height.">
+ “Listen to this page” player minimized.
+ </message>
+ <message name="IDS_READALOUD_CHROME_NOW_PLAYING" desc="Text in 'Listen to this page' player appearing before the title of the currently playing page.">
+ Chrome now playing
+ </message>
+ <message name="IDS_READALOUD_REPLAY" desc="Accessibility string for the button that rewinds 'Listen to this page' playback by the specified number of seconds.">
+ Back <ph name="NUMBER_OF_SECONDS">%1$s<ex>10</ex></ph> seconds
+ </message>
+ <message name="IDS_READALOUD_FORWARD" desc="Accessibility string for the button that seeks 'Listen to this page' playback forward by the specified number of seconds.">
+ Forward <ph name="NUMBER_OF_SECONDS">%1$s<ex>30</ex></ph> seconds
+ </message>
+ <message name="IDS_READALOUD_PLAY" desc="Accessibility string for the 'Listen to this page' play button.">
+ Play
+ </message>
+ <message name="IDS_READALOUD_PAUSE" desc="Accessibility string for the 'Listen to this page' pause button.">
+ Pause
+ </message>
+ <message name="IDS_READALOUD_SPEED_MENU_BUTTON" desc="Accessibility string for the 'Listen to this page' playback speed menu button.">
+ Playback speed: <ph name="PLAYBACK_SPEED">%1$s<ex>1.0</ex></ph>. Click to change.
+ </message>
+ <message name="IDS_READALOUD_OPTIONS_MENU_BUTTON" desc="Accessibility string for the 'Listen to this page' options menu button.">
+ More options
+ </message>
</messages>
</release>
</grit>
diff --git a/chromium/chrome/browser/ui/android/theme/BUILD.gn b/chromium/chrome/browser/ui/android/theme/BUILD.gn
index 4f9a4924555..a9ea6299a78 100644
--- a/chromium/chrome/browser/ui/android/theme/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/theme/BUILD.gn
@@ -10,6 +10,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/theme/ThemeUtils.java",
"java/src/org/chromium/chrome/browser/theme/TopUiThemeColorProvider.java",
"java/src/org/chromium/chrome/browser/ui/theme/BrandedColorScheme.java",
+ "java/src/org/chromium/chrome/browser/ui/theme/ChromeSemanticColorUtils.java",
]
deps = [
":java_resources",
diff --git a/chromium/chrome/browser/ui/android/toolbar/BUILD.gn b/chromium/chrome/browser/ui/android/toolbar/BUILD.gn
index 605b0ecb320..1562440ac47 100644
--- a/chromium/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -139,6 +139,7 @@ android_library("java") {
"//chrome/browser/profiles/android:java",
"//chrome/browser/search_engines/android:java",
"//chrome/browser/segmentation_platform:factory_java",
+ "//chrome/browser/signin/services/android:java",
"//chrome/browser/tab:java",
"//chrome/browser/tab_group:java",
"//chrome/browser/tabmodel:java",
@@ -181,6 +182,7 @@ android_library("java") {
"//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
"//third_party/metrics_proto:metrics_proto_java",
+ "//ui/accessibility:ax_base_java",
"//ui/android:ui_full_java",
"//ui/android:ui_utils_java",
"//url:gurl_java",
@@ -232,6 +234,7 @@ android_resources("java_resources") {
"java/res/drawable-mdpi/incognito_switch.png",
"java/res/drawable-mdpi/modern_location_bar.9.png",
"java/res/drawable-mdpi/popup_bg_bottom.9.png",
+ "java/res/drawable-night/home_surface_search_box_background_colorful.xml",
"java/res/drawable-sw600dp-xhdpi/toolbar_background.9.png",
"java/res/drawable-v21/ntp_search_box.xml",
"java/res/drawable-v31/menu_bg_bottom_tinted.xml",
@@ -264,9 +267,15 @@ android_resources("java_resources") {
"java/res/drawable-xxxhdpi/incognito_small.png",
"java/res/drawable-xxxhdpi/incognito_switch.png",
"java/res/drawable-xxxhdpi/popup_bg_bottom.9.png",
+ "java/res/drawable/home_surface_search_box_background_colorful.xml",
+ "java/res/drawable/home_surface_search_box_background_neutral.xml",
"java/res/drawable/menu_bg_bottom_tinted.xml",
"java/res/drawable/modern_toolbar_text_box_background_with_primary_color.xml",
"java/res/drawable/new_tab_icon.xml",
+ "java/res/drawable/toolbar_button_highlight_layer_list.xml",
+ "java/res/drawable/toolbar_button_ripple.xml",
+ "java/res/drawable/toolbar_menu_button_ripple.xml",
+ "java/res/drawable/toolbar_tab_switcher_button_ripple.xml",
"java/res/layout/adaptive_toolbar_header_preference.xml",
"java/res/layout/bottom_control_container.xml",
"java/res/layout/control_container.xml",
@@ -334,15 +343,18 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//cc:cc_java",
"//chrome/android:chrome_app_java_resources",
"//chrome/browser/android/lifecycle:java",
"//chrome/browser/browser_controls/android:java",
"//chrome/browser/feature_engagement:java",
"//chrome/browser/flags:java",
+ "//chrome/browser/fullscreen/android:java",
"//chrome/browser/language/android:java",
"//chrome/browser/preferences:java",
"//chrome/browser/profiles/android:java",
+ "//chrome/browser/signin/services/android:java",
"//chrome/browser/tab:java",
"//chrome/browser/tabmodel:java",
"//chrome/browser/ui/android/appmenu:java",
@@ -368,8 +380,10 @@ robolectric_library("junit") {
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_fragment_fragment_testing_java",
+ "//third_party/androidx:androidx_preference_preference_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
diff --git a/chromium/chrome/browser/ui/android/webid/BUILD.gn b/chromium/chrome/browser/ui/android/webid/BUILD.gn
index 894dc053e10..50170c40564 100644
--- a/chromium/chrome/browser/ui/android/webid/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/webid/BUILD.gn
@@ -8,6 +8,7 @@ android_library("public_java") {
deps = [
"//base:jni_java",
"//components/browser_ui/bottomsheet/android:java",
+ "//content/public/android:content_full_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//ui/android:ui_java",
"//url:gurl_java",
diff --git a/chromium/chrome/browser/ui/android/webid/internal/BUILD.gn b/chromium/chrome/browser/ui/android/webid/internal/BUILD.gn
index 360dd816744..e0288390661 100644
--- a/chromium/chrome/browser/ui/android/webid/internal/BUILD.gn
+++ b/chromium/chrome/browser/ui/android/webid/internal/BUILD.gn
@@ -28,12 +28,15 @@ android_library("java") {
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/util/android:java",
"//components/browser_ui/widget/android:java",
+ "//components/embedder_support/android:simple_factory_key_java",
"//components/embedder_support/android:util_java",
"//components/favicon/android:java",
"//components/image_fetcher:java",
"//components/url_formatter/android:url_formatter_java",
+ "//content/public/android:content_full_java",
"//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_browser_browser_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//ui/android:ui_java",
"//url:gurl_java",
@@ -64,6 +67,7 @@ android_resources("java_resources") {
"java/res/layout/account_selection_data_sharing_consent_item.xml",
"java/res/layout/account_selection_header_item.xml",
"java/res/layout/account_selection_sheet.xml",
+ "java/res/layout/idp_signin_text_item.xml",
"java/res/values/dimens.xml",
]
}
@@ -81,6 +85,7 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//chrome/browser/tab:java",
"//chrome/browser/ui/android/favicon:java",
"//chrome/browser/ui/android/webid:public_java",
"//chrome/browser/ui/android/webid/internal:java",
@@ -88,6 +93,7 @@ robolectric_library("junit") {
"//components/favicon/android:java",
"//components/image_fetcher:java",
"//components/url_formatter/android:url_formatter_java",
+ "//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_core_java",
@@ -96,8 +102,8 @@ robolectric_library("junit") {
"//third_party/mockito:mockito_java",
"//ui/android:ui_full_java",
"//ui/android:ui_java_test_support",
+ "//ui/android:ui_utils_java",
"//url:gurl_java",
- "//url:gurl_junit_shadows",
"//url:gurl_junit_test_support",
]
}
@@ -119,16 +125,20 @@ android_library("javatests") {
"//chrome/android:chrome_test_java",
"//chrome/browser/flags:java",
"//chrome/browser/tab:java",
+ "//chrome/browser/tabmodel:java",
"//chrome/browser/ui/android/webid:public_java",
"//chrome/test/android:chrome_java_integration_test_support",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/bottomsheet/android/test:java",
+ "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
"//net/android:net_java_test_support",
"//third_party/android_deps:espresso_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
"//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/hamcrest:hamcrest_core_java",
+ "//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_full_java",
diff --git a/chromium/chrome/browser/ui/color/BUILD.gn b/chromium/chrome/browser/ui/color/BUILD.gn
index 3dba3ad26c4..a90f6aabfa6 100644
--- a/chromium/chrome/browser/ui/color/BUILD.gn
+++ b/chromium/chrome/browser/ui/color/BUILD.gn
@@ -23,6 +23,8 @@ source_set("mixers") {
"chrome_color_provider_utils.h",
"material_chrome_color_mixer.cc",
"material_chrome_color_mixer.h",
+ "material_new_tab_page_color_mixer.cc",
+ "material_new_tab_page_color_mixer.h",
"material_omnibox_color_mixer.cc",
"material_omnibox_color_mixer.h",
"material_side_panel_color_mixer.cc",
@@ -45,9 +47,11 @@ source_set("mixers") {
":color_headers",
"//build:branding_buildflags",
"//build:chromeos_buildflags",
+ "//chrome/app/theme:theme_resources",
"//chrome/browser:theme_properties",
"//chrome/common/themes:autogenerated_theme_util",
"//components/omnibox/common:common",
+ "//components/safe_browsing/core/common:common",
"//components/search:search",
"//ui/base:buildflags",
"//ui/color:color",
@@ -69,7 +73,6 @@ source_set("mixers") {
if (is_win) {
sources += [ "win/native_chrome_color_mixer_win.cc" ]
deps += [
- "//chrome/app/theme:theme_resources",
"//chrome/browser:browser_themes",
"//chrome/browser:titlebar_config",
"//ui/base",
@@ -103,8 +106,6 @@ if (!is_ios && !is_android) {
"//chrome/app_shim",
"//components/upload_list",
]
-
- configs += [ "//build/config/compiler:enable_arc" ]
}
if (is_win) {
@@ -114,10 +115,7 @@ if (!is_ios && !is_android) {
]
}
if (is_chromeos_ash) {
- deps += [
- "//components/exo/wayland:ui_controls_protocol_stub",
- "//components/exo/wayland:weston_test_stub",
- ]
+ deps += [ "//components/exo/wayland:ui_controls_protocol_stub" ]
}
}
}
diff --git a/chromium/chrome/browser/ui/messages/android/BUILD.gn b/chromium/chrome/browser/ui/messages/android/BUILD.gn
index 665d1e058b7..c8fd54fdf56 100644
--- a/chromium/chrome/browser/ui/messages/android/BUILD.gn
+++ b/chromium/chrome/browser/ui/messages/android/BUILD.gn
@@ -88,6 +88,7 @@ android_library("unit_device_javatests") {
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit",
"//ui/accessibility:ax_base_java",
+ "//ui/android:ui_java",
"//ui/android:ui_java_test_support",
]
}
diff --git a/chromium/chrome/browser/ui/quick_answers/BUILD.gn b/chromium/chrome/browser/ui/quick_answers/BUILD.gn
index 24301b15534..4a3bba794a6 100644
--- a/chromium/chrome/browser/ui/quick_answers/BUILD.gn
+++ b/chromium/chrome/browser/ui/quick_answers/BUILD.gn
@@ -12,10 +12,6 @@ source_set("quick_answers") {
"quick_answers_controller_impl.h",
"quick_answers_ui_controller.cc",
"quick_answers_ui_controller.h",
- "ui/quick_answers_focus_search.cc",
- "ui/quick_answers_focus_search.h",
- "ui/quick_answers_pre_target_handler.cc",
- "ui/quick_answers_pre_target_handler.h",
"ui/quick_answers_text_label.cc",
"ui/quick_answers_text_label.h",
"ui/quick_answers_util.cc",
@@ -41,6 +37,7 @@ source_set("quick_answers") {
deps = [
"//chrome/browser/profiles:profile",
"//chrome/browser/ui/color:color_headers",
+ "//chrome/browser/ui/views/editor_menu:utils",
"//chromeos/components/quick_answers",
"//chromeos/components/quick_answers/public/cpp:cpp",
"//chromeos/components/quick_answers/public/cpp:prefs",
@@ -62,6 +59,7 @@ source_set("quick_answers") {
]
deps += [
"//ash",
+ "//chromeos/components/kiosk:kiosk",
"//components/language/core/browser:browser",
]
}
@@ -73,6 +71,7 @@ source_set("quick_answers") {
]
deps += [
"//chrome/browser/ui",
+ "//chromeos/components/kiosk:kiosk",
"//chromeos/lacros:lacros",
]
}
diff --git a/chromium/chrome/browser/ui/views/editor_menu/BUILD.gn b/chromium/chrome/browser/ui/views/editor_menu/BUILD.gn
new file mode 100644
index 00000000000..ab28c029749
--- /dev/null
+++ b/chromium/chrome/browser/ui/views/editor_menu/BUILD.gn
@@ -0,0 +1,70 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos)
+
+source_set("utils") {
+ sources = [
+ "utils/focus_search.cc",
+ "utils/focus_search.h",
+ "utils/pre_target_handler.cc",
+ "utils/pre_target_handler.h",
+ "utils/utils.h",
+ ]
+
+ deps = [
+ "//base",
+ "//ui/aura",
+ "//ui/events",
+ "//ui/gfx",
+ "//ui/views",
+ ]
+}
+
+source_set("editor_menu") {
+ sources = [
+ "editor_menu_controller_impl.cc",
+ "editor_menu_controller_impl.h",
+ ]
+
+ deps = [
+ ":views",
+ "//base",
+ "//chromeos/components/editor_menu/public/cpp",
+ "//ui/gfx",
+ "//ui/views",
+ ]
+
+ if (is_chromeos_ash) {
+ deps += [ "//chrome/browser/ash" ]
+ }
+}
+
+source_set("views") {
+ sources = [
+ "editor_menu_chip_view.cc",
+ "editor_menu_chip_view.h",
+ "editor_menu_promo_card_view.cc",
+ "editor_menu_promo_card_view.h",
+ "editor_menu_textfield_view.cc",
+ "editor_menu_textfield_view.h",
+ "editor_menu_view.cc",
+ "editor_menu_view.h",
+ "editor_menu_view_delegate.h",
+ ]
+
+ deps = [
+ ":utils",
+ "//base",
+ "//chromeos/strings:strings_grit",
+ "//components/vector_icons",
+ "//ui/aura",
+ "//ui/base",
+ "//ui/chromeos/styles:cros_tokens_color_mappings_generator",
+ "//ui/gfx",
+ "//ui/views",
+ ]
+}
diff --git a/chromium/chrome/browser/ui/web_applications/BUILD.gn b/chromium/chrome/browser/ui/web_applications/BUILD.gn
index d050c00683a..fd64cab01a5 100644
--- a/chromium/chrome/browser/ui/web_applications/BUILD.gn
+++ b/chromium/chrome/browser/ui/web_applications/BUILD.gn
@@ -13,6 +13,10 @@ source_set("unit_tests") {
"web_app_launch_utils_unittest.cc",
]
+ if (is_chromeos) {
+ sources += [ "sub_apps_install_dialog_controller_unittest.cc" ]
+ }
+
deps = [
"//chrome/browser",
"//chrome/browser/apps/app_service",
@@ -20,7 +24,8 @@ source_set("unit_tests") {
"//chrome/browser/web_applications",
"//chrome/browser/web_applications:web_applications_test_support",
"//chrome/browser/web_applications:web_applications_unit_tests",
- "//components/services/app_service/public/cpp:intents",
+ "//chrome/test:test_support",
+ "//components/services/app_service",
"//content/test:test_support",
"//storage/browser:test_support",
]
@@ -40,6 +45,8 @@ source_set("browser_tests") {
"web_app_engagement_browsertest.cc",
"web_app_file_handling_browsertest.cc",
"web_app_launch_handler_browsertest.cc",
+ "web_app_launch_prevent_close_browsertest.cc",
+ "web_app_menu_model_browsertest.cc",
"web_app_metrics_browsertest.cc",
"web_app_navigate_browsertest.cc",
"web_app_ui_manager_impl_browsertest.cc",
@@ -48,8 +55,9 @@ source_set("browser_tests") {
if (!is_chromeos_lacros) {
sources += [
- # Test not valid on Lacros as web apps are only enabled in the main
+ # Tests are not valid on Lacros as web apps are only enabled in the main
# profile which can never be deleted.
+ "web_app_link_capturing_browsertest.cc",
"web_app_profile_deletion_browsertest.cc",
]
}
@@ -68,7 +76,9 @@ source_set("browser_tests") {
"//chrome/browser:browser_themes",
"//chrome/browser:theme_properties",
"//chrome/browser/apps/app_service",
+ "//chrome/browser/apps/app_service:app_registry_cache_waiter",
"//chrome/browser/apps/app_service:test_support",
+ "//chrome/browser/apps/link_capturing",
"//chrome/browser/browsing_data:constants",
"//chrome/browser/devtools",
"//chrome/browser/web_applications:web_applications_test_support",
@@ -77,8 +87,7 @@ source_set("browser_tests") {
"//chrome/test:test_support_ui",
"//components/embedder_support",
"//components/page_load_metrics/browser:test_support",
- "//components/services/app_service/public/cpp:preferred_apps",
- "//components/services/app_service/public/cpp:protocol_handling",
+ "//components/services/app_service",
"//components/site_engagement/content",
"//components/webapps/browser",
]
@@ -109,7 +118,6 @@ source_set("app_service_browser_tests") {
sources = [
"launch_web_app_browsertest.cc",
"web_app_badging_browsertest.cc",
- "web_app_link_capturing_browsertest.cc",
"web_app_protocol_handling_browsertest.cc",
"web_app_tab_restore_browsertest.cc",
"web_app_url_handling_browsertest.cc",
@@ -127,6 +135,7 @@ source_set("app_service_browser_tests") {
sources += [
"lacros_web_app_browsertest.cc",
"lacros_web_app_shelf_browsertest.cc",
+ "web_app_link_capturing_browsertest.cc",
]
}
@@ -135,6 +144,8 @@ source_set("app_service_browser_tests") {
deps = [
"//chrome/app:command_ids",
"//chrome/browser/apps/app_service",
+ "//chrome/browser/apps/app_service:app_registry_cache_waiter",
+ "//chrome/browser/apps/link_capturing",
"//chrome/browser/browsing_data:constants",
"//chrome/browser/ui/web_applications/diagnostics:app_service_browser_tests",
"//chrome/browser/web_applications",
@@ -144,8 +155,8 @@ source_set("app_service_browser_tests") {
"//components/app_constants",
"//components/embedder_support",
"//components/page_load_metrics/browser:test_support",
- "//components/services/app_service/public/cpp:intents",
- "//components/services/app_service/public/cpp:protocol_handling",
+ "//components/services/app_service",
+ "//components/webapps/browser:test_support",
]
if (is_chromeos_lacros) {
diff --git a/chromium/chrome/browser/ui/web_applications/diagnostics/BUILD.gn b/chromium/chrome/browser/ui/web_applications/diagnostics/BUILD.gn
index 34f39f2d967..c2c18966e91 100644
--- a/chromium/chrome/browser/ui/web_applications/diagnostics/BUILD.gn
+++ b/chromium/chrome/browser/ui/web_applications/diagnostics/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2022 The Chromium Authors. All rights reserved.
+# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -14,11 +14,12 @@ source_set("app_service_browser_tests") {
deps = [
"//chrome/app:command_ids",
+ "//chrome/browser/apps/app_service:app_registry_cache_waiter",
"//chrome/browser/web_applications",
"//chrome/browser/web_applications:web_applications_test_support",
"//chrome/test:test_support",
"//chrome/test:test_support_ui",
- "//components/services/app_service/public/cpp:intents",
+ "//components/services/app_service",
]
if (is_chromeos_lacros) {
diff --git a/chromium/chrome/browser/ui/webui/BUILD.gn b/chromium/chrome/browser/ui/webui/BUILD.gn
index ea52f7f8fb1..c4507915178 100644
--- a/chromium/chrome/browser/ui/webui/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/BUILD.gn
@@ -38,6 +38,10 @@ source_set("configs") {
}
# Includes Ash Chrome and Lacros Chrome
+ if (is_chromeos) {
+ deps += [ "//chromeos/constants" ]
+ }
+
if (is_chromeos_ash) {
sources += [
"ash/chrome_untrusted_web_ui_configs_chromeos.cc",
@@ -64,6 +68,7 @@ source_set("configs") {
"//ash/webui/shortcut_customization_ui",
"//ash/webui/system_extensions_internals_ui",
"//chrome/browser/ash",
+ "//components/cross_device/logging",
]
if (!is_official_build) {
diff --git a/chromium/chrome/browser/ui/webui/about_ui.cc b/chromium/chrome/browser/ui/webui/about_ui.cc
index f8009475fa4..4d852cd792f 100644
--- a/chromium/chrome/browser/ui/webui/about_ui.cc
+++ b/chromium/chrome/browser/ui/webui/about_ui.cc
@@ -492,7 +492,7 @@ void AppendHeader(std::string* output, const std::string& unescaped_title) {
// calling browser is Lacros.
bool isLacrosPrimaryOrCurrentBrowser() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
- return crosapi::browser_util::IsLacrosPrimaryBrowser();
+ return crosapi::browser_util::IsLacrosEnabled();
#else
return true;
#endif
@@ -756,17 +756,6 @@ std::string AboutUIHTMLSource::GetMimeType(const GURL& url) {
return "text/html";
}
-bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- if (source_name_ == chrome::kChromeUIOSCreditsHost ||
- source_name_ == chrome::kChromeUICrostiniCreditsHost ||
- source_name_ == chrome::kChromeUIBorealisCreditsHost) {
- return false;
- }
-#endif
- return content::URLDataSource::ShouldAddContentSecurityPolicy();
-}
-
std::string AboutUIHTMLSource::GetAccessControlAllowOriginForOrigin(
const std::string& origin) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chromium/chrome/browser/ui/webui/about_ui.h b/chromium/chrome/browser/ui/webui/about_ui.h
index 93741660eb1..a21baa300c0 100644
--- a/chromium/chrome/browser/ui/webui/about_ui.h
+++ b/chromium/chrome/browser/ui/webui/about_ui.h
@@ -33,7 +33,6 @@ class AboutUIHTMLSource : public content::URLDataSource {
const content::WebContents::Getter& wc_getter,
content::URLDataSource::GotDataCallback callback) override;
std::string GetMimeType(const GURL& url) override;
- bool ShouldAddContentSecurityPolicy() override;
std::string GetAccessControlAllowOriginForOrigin(
const std::string& origin) override;
diff --git a/chromium/chrome/browser/ui/webui/access_code_cast/OWNERS b/chromium/chrome/browser/ui/webui/access_code_cast/OWNERS
index 06fbaf35d65..7364c9b3c37 100644
--- a/chromium/chrome/browser/ui/webui/access_code_cast/OWNERS
+++ b/chromium/chrome/browser/ui/webui/access_code_cast/OWNERS
@@ -1,4 +1,3 @@
-gbj@google.com
bzielinski@google.com
per-file *.mojom=set noparent
diff --git a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.cc b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.cc
index 57818318d01..f0605246923 100644
--- a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.cc
@@ -182,12 +182,12 @@ views::Widget::InitParams AccessCodeCastDialog::CreateParams(
AccessCodeCastDialogMode dialog_mode) {
views::Widget::InitParams params;
params.remove_standard_frame = true;
- // Use the corner radius which matches style based on the appropriate mode.
- params.corner_radius =
- (dialog_mode == AccessCodeCastDialogMode::kBrowserStandard)
- ? views::LayoutProvider::Get()->GetCornerRadiusMetric(
- views::Emphasis::kMedium)
- : kSystemDialogCornerRadiusDp;
+ // If we are acting as a system dialog, use the appropriate corner radius.
+ // Otherwise, the widget will default to the correct value for browser
+ // dialogs.
+ if (dialog_mode == AccessCodeCastDialogMode::kSystem) {
+ params.corner_radius = kSystemDialogCornerRadiusDp;
+ }
params.type = views::Widget::InitParams::Type::TYPE_BUBBLE;
// Make sure the dialog border is rendered correctly
params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
diff --git a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.h b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.h
index 3b1ef731789..06fa3b3f8f0 100644
--- a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.h
+++ b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.h
@@ -119,7 +119,8 @@ class AccessCodeCastDialog : public ui::WebDialogDelegate,
// use of the media_route_starter_ pointer after c'tor.
std::unique_ptr<media_router::MediaRouteStarter> media_route_starter_;
- const raw_ptr<content::WebContents, DanglingUntriaged> web_contents_;
+ const raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged>
+ web_contents_;
const raw_ptr<Profile> context_;
base::Time dialog_creation_timestamp_;
bool closing_dialog_ = false;
diff --git a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc
index e8d4b18c640..438b3a6f605 100644
--- a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc
+++ b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc
@@ -183,6 +183,7 @@ void AccessCodeCastHandler::AddSink(
std::move(callback), AddSinkResultCode::UNKNOWN_ERROR);
add_sink_callback_ = std::move(base::BindOnce(&AddSinkMetricsCallback))
.Then(std::move(callback_with_default_invoker));
+ add_sink_request_time_ = base::Time::Now();
if (!media_route_starter_) {
std::move(add_sink_callback_).Run(AddSinkResultCode::UNKNOWN_ERROR);
@@ -366,6 +367,8 @@ void AccessCodeCastHandler::OnRouteResponse(MediaCastMode cast_mode,
return;
}
+ AccessCodeCastMetrics::RecordNewDeviceConnectDuration(base::Time::Now() -
+ add_sink_request_time_);
base::UmaHistogramSparse("MediaRouter.Source.CastingSource", cast_mode);
std::move(dialog_callback).Run(RouteRequestResultCode::OK);
}
diff --git a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h
index 60c7a49c866..5da6a5ee83e 100644
--- a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h
+++ b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h
@@ -7,6 +7,7 @@
#include "base/gtest_prod_util.h"
#include "base/scoped_observation.h"
+#include "base/time/time.h"
#include "chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h"
#include "chrome/browser/media/router/discovery/access_code/discovery_resources.pb.h"
#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
@@ -150,6 +151,9 @@ class AccessCodeCastHandler : public access_code_cast::mojom::PageHandler,
// This contains a value only when tracking a pending route request.
absl::optional<RouteRequest> current_route_request_;
+ // The time that the AddSink() function was last called. Used for metrics.
+ base::Time add_sink_request_time_;
+
base::WeakPtrFactory<AccessCodeCastHandler> weak_ptr_factory_{this};
};
diff --git a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
index 4f0f2c082f9..6b2200f94a1 100644
--- a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
@@ -7,6 +7,7 @@
#include "base/ranges/algorithm.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/test/test_simple_task_runner.h"
@@ -75,6 +76,8 @@ class MockPage : public access_code_cast::mojom::Page {
};
const char kEmail[] = "mock_email@gmail.com";
+constexpr char histogram[] =
+ "AccessCodeCast.Session.NewDeviceRouteCreationDuration";
} // namespace
@@ -320,7 +323,7 @@ class AccessCodeCastHandlerTest : public ChromeRenderViewHostTestHarness {
scoped_refptr<base::TestMockTimeTaskRunner> mock_time_task_runner_;
- raw_ptr<MockMediaRouter, DanglingUntriaged> router_;
+ raw_ptr<MockMediaRouter, AcrossTasksDanglingUntriaged> router_;
std::unique_ptr<LoggerImpl> logger_;
signin::IdentityTestEnvironment identity_test_env_;
@@ -345,7 +348,7 @@ class AccessCodeCastHandlerTest : public ChromeRenderViewHostTestHarness {
NiceMock<cast_channel::MockCastMessageHandler> message_handler_;
std::unique_ptr<StrictMock<MockPage>> page_;
std::unique_ptr<TestingProfileManager> profile_manager_;
- raw_ptr<Profile, DanglingUntriaged> profile_;
+ raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_;
std::unique_ptr<MockCastMediaSinkServiceImpl>
mock_cast_media_sink_service_impl_;
std::unique_ptr<MockWebContentsPresentationManager> presentation_manager_;
@@ -585,4 +588,18 @@ TEST_F(AccessCodeCastHandlerTest, ProfileSyncSuccess) {
mock_callback_success.Get());
}
+// Demonstrates that adding a sink and successfully casting to it will trigger a
+// histogram.
+TEST_F(AccessCodeCastHandlerTest, SuccessfulAddAndCastMetric) {
+ base::HistogramTester histogram_tester;
+ histogram_tester.ExpectTotalCount(histogram, 0);
+
+ set_expected_cast_result(mojom::RouteRequestResultCode::OK);
+ MockCastToSinkCallback mock_cast_sink_callback;
+ EXPECT_CALL(mock_cast_sink_callback, Run(RouteRequestResultCode::OK));
+ StartDesktopMirroring(MediaSource::ForUnchosenDesktop(),
+ mock_cast_sink_callback);
+ histogram_tester.ExpectTotalCount(histogram, 1);
+}
+
} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
index ca5e7abbf4d..ad7c0c36c58 100644
--- a/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
+++ b/chromium/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
@@ -20,6 +20,7 @@
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/webui/web_ui_util.h"
namespace media_router {
@@ -60,6 +61,8 @@ AccessCodeCastUI::AccessCodeCastUI(content::WebUI* web_ui)
source->AddBoolean("qrScannerEnabled", false);
source->AddString("learnMoreUrl", chrome::kAccessCodeCastLearnMoreURL);
+ webui::SetupChromeRefresh2023(source);
+
Profile* const profile = Profile::FromWebUI(web_ui);
source->AddInteger("rememberedDeviceDuration",
GetAccessCodeDeviceDurationPref(profile).InSeconds());
diff --git a/chromium/chrome/browser/ui/webui/app_home/DEPS b/chromium/chrome/browser/ui/webui/app_home/DEPS
new file mode 100644
index 00000000000..6d42e07d806
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_home/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+ ".*test\.cc": [
+ "+chrome/browser/ui/views/create_application_shortcut_view_test_support.h",
+ ]
+}
diff --git a/chromium/chrome/browser/ui/webui/app_home/app_home_page_handler.cc b/chromium/chrome/browser/ui/webui/app_home/app_home_page_handler.cc
index 175e2ce5a43..e1be7268968 100644
--- a/chromium/chrome/browser/ui/webui/app_home/app_home_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/app_home/app_home_page_handler.cc
@@ -31,8 +31,6 @@
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/extensions/extension_enable_flow.h"
#include "chrome/browser/ui/tab_dialogs.h"
-#include "chrome/browser/ui/web_applications/web_app_dialog_manager.h"
-#include "chrome/browser/ui/web_applications/web_app_ui_manager_impl.h"
#include "chrome/browser/ui/webui/app_home/app_home.mojom-shared.h"
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
#include "chrome/browser/web_applications/extension_status_utils.h"
@@ -45,6 +43,7 @@
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
+#include "chrome/browser/web_applications/web_app_ui_manager.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/extensions/extension_constants.h"
@@ -525,11 +524,9 @@ void AppHomePageHandler::UninstallWebApp(const std::string& web_app_id) {
Browser* browser = GetCurrentBrowser();
CHECK(browser);
- web_app::WebAppUiManagerImpl::Get(web_app_provider_)
- ->dialog_manager()
- .UninstallWebApp(web_app_id, webapps::WebappUninstallSource::kAppsPage,
- browser->window(),
- std::move(uninstall_success_callback));
+ web_app_provider_->ui_manager().PresentUserUninstallDialog(
+ web_app_id, webapps::WebappUninstallSource::kAppsPage, browser->window(),
+ std::move(uninstall_success_callback));
return;
}
diff --git a/chromium/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc
index de61d7d142e..60a50409f91 100644
--- a/chromium/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/ui/webui/app_home/app_home_page_handler.h"
+#include <utility>
#include <vector>
#include "base/strings/utf_string_conversions.h"
@@ -11,10 +12,14 @@
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/create_application_shortcut_view_test_support.h"
+#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
#include "chrome/browser/ui/webui/app_home/app_home.mojom.h"
#include "chrome/browser/ui/webui/app_home/mock_app_home_page.h"
#include "chrome/browser/web_applications/os_integration/web_app_shortcut.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/web_app_command_manager.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
@@ -141,8 +146,9 @@ class TestAppHomePageHandler : public AppHomePageHandler {
base::OnceClosure run_on_os_login_mode_changed_handle_;
};
-std::unique_ptr<WebAppInstallInfo> BuildWebAppInfo(std::string test_app_name) {
- auto app_info = std::make_unique<WebAppInstallInfo>();
+std::unique_ptr<web_app::WebAppInstallInfo> BuildWebAppInfo(
+ std::string test_app_name) {
+ auto app_info = std::make_unique<web_app::WebAppInstallInfo>();
app_info->start_url = GURL(kTestAppUrl);
app_info->scope = GURL(kTestAppUrl);
app_info->title = base::UTF8ToUTF16(base::StringPiece(test_app_name));
@@ -163,10 +169,18 @@ class AppHomePageHandlerTest : public InProcessBrowserTest {
~AppHomePageHandlerTest() override = default;
void SetUpOnMainThread() override {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ override_registration_ =
+ web_app::OsIntegrationTestOverrideImpl::OverrideForTesting();
web_app::test::WaitUntilWebAppProviderAndSubsystemsReady(
web_app::WebAppProvider::GetForTest(profile()));
}
+ void TearDownOnMainThread() override {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ override_registration_.reset();
+ }
+
protected:
std::unique_ptr<TestAppHomePageHandler> GetAppHomePageHandler() {
content::WebContents* contents =
@@ -251,14 +265,14 @@ class AppHomePageHandlerTest : public InProcessBrowserTest {
content::TestWebUI test_web_ui_;
testing::StrictMock<MockAppHomePage> page_;
+
+ std::unique_ptr<web_app::OsIntegrationTestOverrideImpl::BlockingRegistration>
+ override_registration_;
+
#if BUILDFLAG(IS_WIN)
- // This prevents SetRunOnOsLoginMode from leaving shortcuts in the Windows
- // startup directory that cause Chrome to get launched when Windows starts on
- // a bot. It needs to be in the class so that the override lasts until the
- // test object is destroyed, because tasks can keep running after the test
- // method finishes.
- // See https://crbug.com/1239809
- base::ScopedPathOverride override_user_startup_{base::DIR_USER_STARTUP};
+ // This is used as a fallback to prevent creating shortcuts in the startup
+ // dir if tasks are still running when `override_registration_` is reset.
+ base::ScopedPathOverride override_start_dir_{base::DIR_USER_STARTUP};
#endif // BUILDFLAG(IS_WIN)
};
@@ -426,14 +440,18 @@ IN_PROC_BROWSER_TEST_F(AppHomePageHandlerTest, CreateWebAppShortcut) {
page_handler->CreateAppShortcut(installed_app_id, loop.QuitClosure());
loop.Run();
#else
- views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
- "CreateChromeApplicationShortcutView");
+ CreateChromeApplicationShortcutViewWaiter waiter;
page_handler->CreateAppShortcut(installed_app_id, base::DoNothing());
FlushShortcutTasks();
- views::Widget* widget = waiter.WaitIfNeededAndGet();
- ASSERT_TRUE(widget != nullptr);
- views::test::AcceptDialog(widget);
+ std::move(waiter).WaitForAndAccept();
+ FlushShortcutTasks();
+ web_app::WebAppProvider::GetForTest(profile())
+ ->command_manager()
+ .AwaitAllCommandsCompleteForTesting();
#endif
+ EXPECT_CALL(page_, RemoveApp(MatchAppId(installed_app_id)))
+ .Times(testing::AtLeast(1));
+ UninstallTestWebApp(installed_app_id);
}
IN_PROC_BROWSER_TEST_F(AppHomePageHandlerTest, CreateExtensionAppShortcut) {
@@ -451,13 +469,16 @@ IN_PROC_BROWSER_TEST_F(AppHomePageHandlerTest, CreateExtensionAppShortcut) {
page_handler->CreateAppShortcut(extension->id(), loop.QuitClosure());
loop.Run();
#else
- views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
- "CreateChromeApplicationShortcutView");
+ CreateChromeApplicationShortcutViewWaiter waiter;
page_handler->CreateAppShortcut(extension->id(), base::DoNothing());
FlushShortcutTasks();
- views::Widget* widget = waiter.WaitIfNeededAndGet();
- ASSERT_TRUE(widget != nullptr);
- views::test::AcceptDialog(widget);
+ std::move(waiter).WaitForAndAccept();
+#endif
+ EXPECT_CALL(page_, RemoveApp(MatchAppId(extension->id())))
+ .Times(testing::AtLeast(1));
+ UninstallTestExtensionApp(extension.get());
+#if !BUILDFLAG(IS_MAC)
+ FlushShortcutTasks();
#endif
}
diff --git a/chromium/chrome/browser/ui/webui/app_launcher_page_ui.cc b/chromium/chrome/browser/ui/webui/app_launcher_page_ui.cc
deleted file mode 100644
index 4036ce14e55..00000000000
--- a/chromium/chrome/browser/ui/webui/app_launcher_page_ui.cc
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/app_launcher_page_ui.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/metrics/histogram_macros.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/ui/apps/app_info_dialog.h"
-#include "chrome/browser/ui/webui/metrics_handler.h"
-#include "chrome/browser/ui/webui/ntp/app_icon_webui_handler.h"
-#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
-#include "chrome/browser/ui/webui/ntp/app_resource_cache_factory.h"
-#include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
-#include "chrome/browser/ui/webui/theme_handler.h"
-#include "chrome/browser/ui/webui/webui_util.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/apps_resources.h"
-#include "chrome/grit/apps_resources_map.h"
-#include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/grit/theme_resources.h"
-#include "components/bookmarks/common/bookmark_pref_names.h"
-#include "components/google/core/common/google_util.h"
-#include "components/policy/core/common/policy_pref_names.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/prefs/pref_service.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/strings/grit/components_strings.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/common/extension_urls.h"
-#include "services/network/public/mojom/content_security_policy.mojom.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/base/webui/web_ui_util.h"
-#include "ui/gfx/animation/animation.h"
-
-#if BUILDFLAG(IS_MAC)
-#include "chrome/browser/platform_util.h"
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// AppLauncherPageUI
-
-AppLauncherPageUI::AppLauncherPageUI(content::WebUI* web_ui)
- : content::WebUIController(web_ui) {
- web_ui->OverrideTitle(l10n_util::GetStringUTF16(IDS_APP_LAUNCHER_TAB_TITLE));
-
- if (!GetProfile()->IsOffTheRecord()) {
- extensions::ExtensionService* service =
- extensions::ExtensionSystem::Get(GetProfile())->extension_service();
- web_app::WebAppProvider* web_app_provider =
- web_app::WebAppProvider::GetForWebApps(GetProfile());
- DCHECK(web_app_provider);
- DCHECK(service);
- // We should not be launched without an ExtensionService or WebAppProvider.
- web_ui->AddMessageHandler(
- std::make_unique<AppLauncherHandler>(service, web_app_provider));
- web_ui->AddMessageHandler(std::make_unique<CoreAppLauncherHandler>());
- web_ui->AddMessageHandler(std::make_unique<AppIconWebUIHandler>());
- web_ui->AddMessageHandler(std::make_unique<MetricsHandler>());
- }
-
- // The theme handler can require some CPU, so do it after hooking up the most
- // visited handler. This allows the DB query for the new tab thumbs to happen
- // earlier.
- web_ui->AddMessageHandler(std::make_unique<ThemeHandler>());
-
- content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
- GetProfile(), chrome::kChromeUIAppLauncherPageHost);
-
- source->AddResourcePaths(base::make_span(kAppsResources, kAppsResourcesSize));
- source->SetDefaultResource(IDR_APPS_NEW_TAB_HTML);
- source->UseStringsJs();
-
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
- {"title", IDS_NEW_TAB_TITLE},
- {"webStoreTitle", IDS_EXTENSION_WEB_STORE_TITLE},
- {"webStoreTitleShort", IDS_EXTENSION_WEB_STORE_TITLE_SHORT},
- {"attributionintro", IDS_NEW_TAB_ATTRIBUTION_INTRO},
- {"appuninstall", IDS_EXTENSIONS_UNINSTALL},
- {"appoptions", IDS_NEW_TAB_APP_OPTIONS},
- {"appdetails", IDS_NEW_TAB_APP_DETAILS},
- {"appinfodialog", IDS_APP_CONTEXT_MENU_SHOW_INFO},
- {"appcreateshortcut", IDS_NEW_TAB_APP_CREATE_SHORTCUT},
- {"appinstalllocally", IDS_NEW_TAB_APP_INSTALL_LOCALLY},
- {"appDefaultPageName", IDS_APP_DEFAULT_PAGE_NAME},
- {"applaunchtypepinned", IDS_APP_CONTEXT_MENU_OPEN_PINNED},
- {"applaunchtyperegular", IDS_APP_CONTEXT_MENU_OPEN_REGULAR},
- {"applaunchtypewindow", IDS_APP_CONTEXT_MENU_OPEN_WINDOW},
- {"applaunchtypefullscreen", IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN},
- {"syncpromotext", IDS_SYNC_START_SYNC_BUTTON_LABEL},
- {"syncLinkText", IDS_SYNC_ADVANCED_OPTIONS},
- {"learnMore", IDS_LEARN_MORE},
- {"appInstallHintText", IDS_NEW_TAB_APP_INSTALL_HINT_LABEL},
- {"learn_more", IDS_LEARN_MORE},
- {"tile_grid_screenreader_accessible_description",
- IDS_NEW_TAB_TILE_GRID_ACCESSIBLE_DESCRIPTION},
- {"page_switcher_change_title", IDS_NEW_TAB_PAGE_SWITCHER_CHANGE_TITLE},
- {"page_switcher_same_title", IDS_NEW_TAB_PAGE_SWITCHER_SAME_TITLE},
- {"runonoslogin", IDS_APP_CONTEXT_MENU_RUN_ON_OS_LOGIN},
- };
- source->AddLocalizedStrings(kLocalizedStrings);
-
- PrefService* prefs = GetProfile()->GetPrefs();
- source->AddString(
- "bookmarkbarattached",
- prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar) ? "true" : "false");
-
- const std::string& app_locale = g_browser_process->GetApplicationLocale();
- source->AddString("webStoreLink",
- google_util::AppendGoogleLocaleParam(
- extension_urls::GetWebstoreLaunchURL(), app_locale)
- .spec());
-
- bool is_swipe_tracking_from_scroll_events_enabled = false;
-#if BUILDFLAG(IS_MAC)
- // On the Mac, horizontal scrolling can be treated as a back or forward
- // gesture. Pass through a flag that indicates whether or not that feature is
- // enabled.
- is_swipe_tracking_from_scroll_events_enabled =
- platform_util::IsSwipeTrackingFromScrollEventsEnabled();
-#endif
- source->AddBoolean("isSwipeTrackingFromScrollEventsEnabled",
- is_swipe_tracking_from_scroll_events_enabled);
-
- source->AddBoolean(
- "showWebStoreIcon",
- !prefs->GetBoolean(policy::policy_prefs::kHideWebStoreIcon));
-
- pref_change_registrar_.Init(prefs);
- pref_change_registrar_.Add(
- policy::policy_prefs::kHideWebStoreIcon,
- base::BindRepeating(&AppLauncherPageUI::OnHideWebStoreIconChanged,
- base::Unretained(this)));
-
- source->AddBoolean("canShowAppInfoDialog", CanPlatformShowAppInfoDialog());
-
- AppLauncherHandler::RegisterLoadTimeData(GetProfile(), source);
-
- // Control fade and resize animations.
- source->AddBoolean("anim", gfx::Animation::ShouldRenderRichAnimation());
-
- source->AddBoolean("isUserSignedIn",
- IdentityManagerFactory::GetForProfile(GetProfile())
- ->HasPrimaryAccount(signin::ConsentLevel::kSync));
-
- source->OverrideContentSecurityPolicy(
- network::mojom::CSPDirectiveName::ScriptSrc,
- "script-src chrome://resources 'self' 'unsafe-eval' "
- "'unsafe-inline';");
- source->OverrideContentSecurityPolicy(
- network::mojom::CSPDirectiveName::StyleSrc,
- "style-src 'self' chrome://resources chrome://theme "
- "'unsafe-inline';");
- source->OverrideContentSecurityPolicy(
- network::mojom::CSPDirectiveName::ImgSrc,
- "img-src 'self' chrome://extension-icon chrome://app-icon chrome://theme "
- "chrome://resources data:;");
- source->OverrideContentSecurityPolicy(
- network::mojom::CSPDirectiveName::TrustedTypes,
- "trusted-types apps-page-js cr-ui-bubble-js-static "
- "parse-html-subset;");
-}
-
-AppLauncherPageUI::~AppLauncherPageUI() {
-}
-
-void AppLauncherPageUI::OnHideWebStoreIconChanged() {
- base::Value::Dict update;
- PrefService* prefs = GetProfile()->GetPrefs();
- update.Set("showWebStoreIcon",
- !prefs->GetBoolean(policy::policy_prefs::kHideWebStoreIcon));
- content::WebUIDataSource::Update(
- GetProfile(), chrome::kChromeUIAppLauncherPageHost, std::move(update));
-}
-
-// static
-base::RefCountedMemory* AppLauncherPageUI::GetFaviconResourceBytes(
- ui::ResourceScaleFactor scale_factor) {
- return ui::ResourceBundle::GetSharedInstance().
- LoadDataResourceBytesForScale(IDR_BOOKMARK_BAR_APPS_SHORTCUT,
- scale_factor);
-}
-
-Profile* AppLauncherPageUI::GetProfile() const {
- return Profile::FromWebUI(web_ui());
-}
diff --git a/chromium/chrome/browser/ui/webui/app_launcher_page_ui.h b/chromium/chrome/browser/ui/webui/app_launcher_page_ui.h
deleted file mode 100644
index 293a05415bb..00000000000
--- a/chromium/chrome/browser/ui/webui/app_launcher_page_ui.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_APP_LAUNCHER_PAGE_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_APP_LAUNCHER_PAGE_UI_H_
-
-#include "components/prefs/pref_change_registrar.h"
-#include "content/public/browser/web_ui_controller.h"
-#include "ui/base/resource/resource_scale_factor.h"
-
-class Profile;
-
-namespace base {
-class RefCountedMemory;
-}
-
-// The WebUIController used for the app launcher page UI.
-class AppLauncherPageUI : public content::WebUIController {
- public:
- explicit AppLauncherPageUI(content::WebUI* web_ui);
-
- AppLauncherPageUI(const AppLauncherPageUI&) = delete;
- AppLauncherPageUI& operator=(const AppLauncherPageUI&) = delete;
-
- ~AppLauncherPageUI() override;
-
- static base::RefCountedMemory* GetFaviconResourceBytes(
- ui::ResourceScaleFactor scale_factor);
-
- private:
- void OnHideWebStoreIconChanged();
-
- Profile* GetProfile() const;
- PrefChangeRegistrar pref_change_registrar_;
-};
-
-#endif // CHROME_BROWSER_UI_WEBUI_APP_LAUNCHER_PAGE_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index 455c1ced7ad..46651f9537b 100644
--- a/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -16,7 +16,9 @@
#include "base/debug/dump_without_crashing.h"
#include "base/i18n/message_formatter.h"
#include "base/logging.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
@@ -26,8 +28,15 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/locks/all_apps_lock.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
+#include "chrome/browser/web_applications/web_app_prefs_utils.h"
#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_registrar_observer.h"
+#include "chrome/browser/web_applications/web_app_registry_update.h"
+#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h"
#include "components/app_constants/constants.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
@@ -56,6 +65,7 @@
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/components/arc/session/connection_holder.h"
#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ash/apps/apk_web_app_service.h"
#include "chrome/browser/ash/crosapi/crosapi_ash.h"
#include "chrome/browser/ash/crosapi/crosapi_manager.h"
#include "chrome/browser/ash/crosapi/web_app_service_ash.h"
@@ -137,8 +147,9 @@ apps::IntentFilters GetSupportedLinkIntentFilters(Profile* profile,
[&app_id, &intent_filters](const apps::AppUpdate& update) {
if (update.Readiness() == apps::Readiness::kReady) {
for (auto& filter : update.IntentFilters()) {
- if (apps_util::IsSupportedLinkForApp(app_id, filter))
+ if (apps_util::IsSupportedLinkForApp(app_id, filter)) {
intent_filters.emplace_back(std::move(filter));
+ }
}
}
});
@@ -151,7 +162,8 @@ std::vector<std::string> GetSupportedLinks(Profile* profile,
std::set<std::string> supported_links;
auto intent_filters = GetSupportedLinkIntentFilters(profile, app_id);
for (auto& filter : intent_filters) {
- for (const auto& link : filter->GetSupportedLinksForAppManagement()) {
+ for (const auto& link :
+ apps_util::GetSupportedLinksForAppManagement(filter)) {
supported_links.insert(link);
}
}
@@ -160,6 +172,29 @@ std::vector<std::string> GetSupportedLinks(Profile* profile,
supported_links.end());
}
+#if !BUILDFLAG(IS_CHROMEOS)
+std::vector<std::string> GetSupportedLinksForPWAs(
+ const std::string& app_id,
+ web_app::WebAppProvider& provider) {
+ GURL app_scope = provider.registrar_unsafe().GetAppScope(app_id);
+ if (!web_app::IsValidScopeForLinkCapturing(app_scope)) {
+ return std::vector<std::string>();
+ }
+
+ std::string scope_str(app_scope.host());
+ if (app_scope.has_port()) {
+ scope_str += ":" + app_scope.port();
+ }
+ scope_str += app_scope.path();
+ if (scope_str.back() == '/') {
+ scope_str = scope_str + "*";
+ } else {
+ scope_str = scope_str + "/*";
+ }
+ return {scope_str};
+}
+#endif // !BUILDFLAG(IS_CHROMEOS)
+
absl::optional<std::string> MaybeFormatBytes(absl::optional<uint64_t> bytes) {
if (bytes.has_value()) {
// ui::FormatBytes requires a non-negative signed integer. In general, we
@@ -190,20 +225,17 @@ AppManagementPageHandler::AppManagementPageHandler(
: receiver_(this, std::move(receiver)),
page_(std::move(page)),
profile_(profile),
- delegate_(delegate),
#if BUILDFLAG(IS_CHROMEOS_ASH)
shelf_delegate_(this, profile),
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
- preferred_apps_list_handle_(
- apps::AppServiceProxyFactory::GetForProfile(profile)
- ->PreferredAppsList()) {
- app_registry_cache_observer_.Observe(
- &apps::AppServiceProxyFactory::GetForProfile(profile_)
- ->AppRegistryCache());
- preferred_apps_list_handle_observer_.Observe(&*preferred_apps_list_handle_);
+ delegate_(delegate) {
+ apps::AppServiceProxy* proxy =
+ apps::AppServiceProxyFactory::GetForProfile(profile_);
+ app_registry_cache_observer_.Observe(&proxy->AppRegistryCache());
+ preferred_apps_list_handle_observer_.Observe(&proxy->PreferredAppsList());
// On Chrome OS, file handler updates are already plumbed through
- // `OnAppUpdate()` since the change will also affect the intent filters.
+ // App Service since the change will also affect the intent filters.
// There's no need to update twice.
#if !BUILDFLAG(IS_CHROMEOS)
auto* provider = web_app::WebAppProvider::GetForWebApps(profile_);
@@ -220,13 +252,15 @@ void AppManagementPageHandler::OnPinnedChanged(const std::string& app_id,
apps::AppServiceProxyFactory::GetForProfile(profile_)
->AppRegistryCache()
.ForOneApp(app_id, [this, &app](const apps::AppUpdate& update) {
- if (update.Readiness() == apps::Readiness::kReady)
+ if (update.Readiness() == apps::Readiness::kReady) {
app = CreateUIAppPtr(update);
+ }
});
// If an app with this id is not already installed, do nothing.
- if (!app)
+ if (!app) {
return;
+ }
app->is_pinned = pinned ? OptionalBool::kTrue : OptionalBool::kFalse;
@@ -262,8 +296,9 @@ void AppManagementPageHandler::GetApp(const std::string& app_id,
apps::AppServiceProxyFactory::GetForProfile(profile_)
->AppRegistryCache()
.ForOneApp(app_id, [this, &app](const apps::AppUpdate& update) {
- if (update.Readiness() == apps::Readiness::kReady)
+ if (update.Readiness() == apps::Readiness::kReady) {
app = CreateUIAppPtr(update);
+ }
});
std::move(callback).Run(std::move(app));
@@ -359,29 +394,58 @@ void AppManagementPageHandler::OpenNativeSettings(const std::string& app_id) {
void AppManagementPageHandler::SetPreferredApp(const std::string& app_id,
bool is_preferred_app) {
- bool is_preferred_app_for_supported_links =
- preferred_apps_list_handle_->IsPreferredAppForSupportedLinks(app_id);
+#if BUILDFLAG(IS_CHROMEOS)
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile_);
+ bool is_preferred_app_for_supported_links =
+ proxy->PreferredAppsList().IsPreferredAppForSupportedLinks(app_id);
if (is_preferred_app && !is_preferred_app_for_supported_links) {
proxy->SetSupportedLinksPreference(app_id);
} else if (!is_preferred_app && is_preferred_app_for_supported_links) {
proxy->RemoveSupportedLinksPreference(app_id);
}
+#else
+ web_app::WebAppProvider* provider =
+ web_app::WebAppProvider::GetForWebApps(profile_);
+ provider->scheduler().ScheduleCallbackWithLock<web_app::AllAppsLock>(
+ "AppManagementPageHandler::MakeAppPreferredAndResetOthers",
+ std::make_unique<web_app::AllAppsLockDescription>(),
+ base::BindOnce(&AppManagementPageHandler::MakeAppPreferredAndResetOthers,
+ weak_ptr_factory_.GetWeakPtr(), app_id, is_preferred_app));
+#endif // BUILDFLAG(IS_CHROMEOS)
}
void AppManagementPageHandler::GetOverlappingPreferredApps(
const std::string& app_id,
GetOverlappingPreferredAppsCallback callback) {
+#if BUILDFLAG(IS_CHROMEOS)
auto intent_filters = GetSupportedLinkIntentFilters(profile_, app_id);
base::flat_set<std::string> app_ids =
- preferred_apps_list_handle_->FindPreferredAppsForFilters(intent_filters);
+ apps::AppServiceProxyFactory::GetForProfile(profile_)
+ ->PreferredAppsList()
+ .FindPreferredAppsForFilters(intent_filters);
app_ids.erase(app_id);
// Remove the use_browser app ID as it's mainly used inside the intent system
// and is not an app in app management. This prevents an overlap dialog from
// being shown when there are no "real" apps that overlap.
app_ids.erase(apps_util::kUseBrowserForLink);
std::move(callback).Run(std::move(app_ids).extract());
+#else
+ web_app::WebAppProvider* provider =
+ web_app::WebAppProvider::GetForWebApps(profile_);
+ provider->scheduler().ScheduleCallbackWithLock<web_app::AllAppsLock>(
+ "AppManagementPageHandler::GetOverlappingPreferredApps",
+ std::make_unique<web_app::AllAppsLockDescription>(),
+ base::BindOnce(
+ [](const web_app::AppId& app_id,
+ GetOverlappingPreferredAppsCallback callback,
+ web_app::AllAppsLock& all_apps_lock) {
+ std::move(callback).Run(
+ all_apps_lock.registrar().GetOverlappingAppsMatchingScope(
+ app_id));
+ },
+ app_id, std::move(callback)));
+#endif // BUILDFLAG(IS_CHROMEOS)
}
void AppManagementPageHandler::SetWindowMode(const std::string& app_id,
@@ -417,8 +481,7 @@ void AppManagementPageHandler::SetRunOnOsLoginMode(
void AppManagementPageHandler::SetFileHandlingEnabled(const std::string& app_id,
bool enabled) {
auto permission = std::make_unique<apps::Permission>(
- apps::PermissionType::kFileHandling,
- std::make_unique<apps::PermissionValue>(enabled),
+ apps::PermissionType::kFileHandling, enabled,
/*is_managed=*/false);
apps::AppServiceProxyFactory::GetForProfile(profile_)->SetPermission(
app_id, std::move(permission));
@@ -441,12 +504,14 @@ void AppManagementPageHandler::OnWebAppFileHandlerApprovalStateChanged(
apps::AppServiceProxyFactory::GetForProfile(profile_)
->AppRegistryCache()
.ForOneApp(app_id, [this, &app](const apps::AppUpdate& update) {
- if (update.Readiness() == apps::Readiness::kReady)
+ if (update.Readiness() == apps::Readiness::kReady) {
app = CreateUIAppPtr(update);
+ }
});
- if (!app)
+ if (!app) {
return;
+ }
page_->OnAppChanged(std::move(app));
}
@@ -455,6 +520,14 @@ void AppManagementPageHandler::OnAppRegistrarDestroyed() {
registrar_observation_.Reset();
}
+#if !BUILDFLAG(IS_CHROMEOS)
+void AppManagementPageHandler::OnWebAppUserLinkCapturingPreferencesChanged(
+ const web_app::AppId& app_id,
+ bool is_preferred) {
+ OnPreferredAppChanged(app_id, is_preferred);
+}
+#endif // !BUILDFLAG(IS_CHROMEOS)
+
app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
const apps::AppUpdate& update) {
auto app = app_management::mojom::App::New();
@@ -492,14 +565,33 @@ app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
app->resize_locked = update.ResizeLocked().value_or(false);
app->hide_resize_locked = !update.ResizeLocked().has_value();
#endif
+#if BUILDFLAG(IS_CHROMEOS)
+ app->is_preferred_app = apps::AppServiceProxyFactory::GetForProfile(profile_)
+ ->PreferredAppsList()
+ .IsPreferredAppForSupportedLinks(update.AppId());
+#else
+ web_app::WebAppProvider* provider =
+ web_app::WebAppProvider::GetForWebApps(profile_);
+ CHECK(provider);
app->is_preferred_app =
- preferred_apps_list_handle_->IsPreferredAppForSupportedLinks(
- update.AppId());
+ provider->registrar_unsafe().CapturesLinksInScope(update.AppId());
+#endif // BUILDFLAG(IS_CHROMEOS)
app->hide_more_settings = ShouldHideMoreSettings(app->id);
app->hide_pin_to_shelf =
!update.ShowInShelf().value_or(true) || ShouldHidePinToShelf(app->id);
app->window_mode = update.WindowMode();
+
+#if BUILDFLAG(IS_CHROMEOS)
app->supported_links = GetSupportedLinks(profile_, app->id);
+#else
+ // This allows us to bypass showing the supported links item on the PWA app
+ // settings page on Windows, Mac and Linux platforms.
+ if (base::FeatureList::IsEnabled(features::kDesktopPWAsLinkCapturing)) {
+ app->supported_links = GetSupportedLinksForPWAs(app->id, *provider);
+ } else {
+ app->supported_links = std::vector<std::string>();
+ }
+#endif // BUILDFLAG(IS_CHROMEOS)
auto run_on_os_login = update.RunOnOsLogin();
if (run_on_os_login.has_value()) {
app->run_on_os_login = std::make_unique<apps::RunOnOsLogin>(
@@ -524,11 +616,13 @@ app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
bool is_potential_file_handler_action = base::ranges::any_of(
filter->conditions.begin(), filter->conditions.end(),
[](const std::unique_ptr<apps::Condition>& condition) {
- if (condition->condition_type != apps::ConditionType::kAction)
+ if (condition->condition_type != apps::ConditionType::kAction) {
return false;
+ }
- if (condition->condition_values.size() != 1U)
+ if (condition->condition_values.size() != 1U) {
return false;
+ }
return condition->condition_values[0]->value ==
apps_util::kIntentActionPotentialFileHandler;
@@ -573,8 +667,9 @@ app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
}
absl::optional<GURL> learn_more_url;
- if (!CanShowDefaultAppAssociationsUi())
+ if (!CanShowDefaultAppAssociationsUi()) {
learn_more_url = GURL(kFileHandlingLearnMore);
+ }
// TODO(crbug/1252505): add file handling policy support.
app->file_handling_state = app_management::mojom::FileHandlingState::New(
fh_enabled, /*is_managed=*/false, file_handling_types,
@@ -583,7 +678,6 @@ app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
}
#if !BUILDFLAG(IS_CHROMEOS)
- auto* provider = web_app::WebAppProvider::GetForLocalAppsUnchecked(profile_);
app->hide_window_mode = provider->registrar_unsafe().IsIsolated(app->id);
#endif
@@ -595,19 +689,27 @@ app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
void AppManagementPageHandler::OpenStorePage(const std::string& app_id) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile_);
- proxy->AppRegistryCache().ForOneApp(app_id, [&proxy](const apps::AppUpdate&
- update) {
- if (update.InstallSource() == apps::InstallSource::kPlayStore) {
- GURL url("https://play.google.com/store/apps/details?id=" +
- update.PublisherId());
- proxy->LaunchAppWithUrl(arc::kPlayStoreAppId, ui::EF_NONE, url,
- apps::LaunchSource::kFromChromeInternal);
- } else if (update.InstallSource() == apps::InstallSource::kChromeWebStore) {
- GURL url("https://chrome.google.com/webstore/detail/" + update.AppId());
- proxy->LaunchAppWithUrl(extensions::kWebStoreAppId, ui::EF_NONE, url,
- apps::LaunchSource::kFromChromeInternal);
- }
- });
+ auto* apk_service = ash::ApkWebAppService::Get(profile_);
+ proxy->AppRegistryCache().ForOneApp(
+ app_id, [&proxy, &apk_service](const apps::AppUpdate& update) {
+ if (update.InstallSource() == apps::InstallSource::kPlayStore) {
+ std::string package_name = update.PublisherId();
+ if (apk_service->IsWebAppInstalledFromArc(update.AppId())) {
+ package_name =
+ apk_service->GetPackageNameForWebApp(update.AppId()).value();
+ }
+ GURL url("https://play.google.com/store/apps/details?id=" +
+ package_name);
+ proxy->LaunchAppWithUrl(arc::kPlayStoreAppId, ui::EF_NONE, url,
+ apps::LaunchSource::kFromChromeInternal);
+ } else if (update.InstallSource() ==
+ apps::InstallSource::kChromeWebStore) {
+ GURL url("https://chrome.google.com/webstore/detail/" +
+ update.AppId());
+ proxy->LaunchAppWithUrl(extensions::kWebStoreAppId, ui::EF_NONE, url,
+ apps::LaunchSource::kFromChromeInternal);
+ }
+ });
#endif
}
@@ -639,13 +741,15 @@ void AppManagementPageHandler::OnPreferredAppChanged(const std::string& app_id,
apps::AppServiceProxyFactory::GetForProfile(profile_)
->AppRegistryCache()
.ForOneApp(app_id, [this, &app](const apps::AppUpdate& update) {
- if (update.Readiness() == apps::Readiness::kReady)
+ if (update.Readiness() == apps::Readiness::kReady) {
app = CreateUIAppPtr(update);
+ }
});
// If an app with this id is not already installed, do nothing.
- if (!app)
+ if (!app) {
return;
+ }
app->is_preferred_app = is_preferred_app;
@@ -654,5 +758,54 @@ void AppManagementPageHandler::OnPreferredAppChanged(const std::string& app_id,
void AppManagementPageHandler::OnPreferredAppsListWillBeDestroyed(
apps::PreferredAppsListHandle* handle) {
- handle->RemoveObserver(this);
+ preferred_apps_list_handle_observer_.Reset();
+}
+
+#if !BUILDFLAG(IS_CHROMEOS)
+void AppManagementPageHandler::MakeAppPreferredAndResetOthers(
+ const web_app::AppId& app_id,
+ bool set_to_preferred,
+ web_app::AllAppsLock& lock) {
+ bool is_already_preferred = lock.registrar().CapturesLinksInScope(app_id);
+
+ // Only update in web_app DB if the user selected choice does not match the
+ // one in the DB currently.
+ bool requires_update = (set_to_preferred && !is_already_preferred) ||
+ (!set_to_preferred && is_already_preferred);
+
+ if (!requires_update) {
+ return;
+ }
+
+ // TODO(b/273830801): Automatically call observers when changes are committed
+ // to the web_app DB.
+ for (const web_app::AppId& id : lock.registrar().GetAppIds()) {
+ if (id == app_id) {
+ {
+ web_app::ScopedRegistryUpdate update = lock.sync_bridge().BeginUpdate();
+ web_app::WebApp* app_to_update = update->UpdateApp(app_id);
+ app_to_update->SetIsUserSelectedAppForSupportedLinks(set_to_preferred);
+ }
+ lock.registrar().NotifyWebAppUserLinkCapturingPreferencesChanged(
+ app_id, set_to_preferred);
+ } else {
+ // For all other app_ids, if one is already set as the preferred, reset
+ // all other apps in the registry if they were previously set to be a
+ // preferred app to capture similar type of links according to scope
+ // prefixes.
+ if (set_to_preferred && lock.registrar().CapturesLinksInScope(id) &&
+ lock.registrar().AppScopesMatchForUserLinkCapturing(app_id, id)) {
+ {
+ web_app::ScopedRegistryUpdate update =
+ lock.sync_bridge().BeginUpdate();
+ web_app::WebApp* app_to_update = update->UpdateApp(id);
+ app_to_update->SetIsUserSelectedAppForSupportedLinks(
+ /*is_user_selected_app_for_capturing_links=*/false);
+ }
+ lock.registrar().NotifyWebAppUserLinkCapturingPreferencesChanged(
+ id, /*is_preferred=*/false);
+ }
+ }
+ }
}
+#endif // !BUILDFLAG(IS_CHROMEOS)
diff --git a/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.h b/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.h
index 50dc39eecad..aec8f8118c4 100644
--- a/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.h
+++ b/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler.h
@@ -10,6 +10,7 @@
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "build/chromeos_buildflags.h"
+#include "chrome/browser/web_applications/locks/all_apps_lock.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_registrar_observer.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
@@ -93,6 +94,15 @@ class AppManagementPageHandler : public app_management::mojom::PageHandler,
const web_app::AppId& app_id) override;
void OnAppRegistrarDestroyed() override;
+ // The following observers are used for user link capturing on W/M/L platforms
+ // to observe user link capturing preferences being changed
+ // in the registrar, so as to propagate the changes to the app-settings/ page
+ // to change the UI dynamically.
+#if !BUILDFLAG(IS_CHROMEOS)
+ void OnWebAppUserLinkCapturingPreferencesChanged(const web_app::AppId& app_id,
+ bool is_preferred) override;
+#endif // !BUILDFLAG(IS_CHROMEOS)
+
private:
app_management::mojom::AppPtr CreateUIAppPtr(const apps::AppUpdate& update);
@@ -107,19 +117,23 @@ class AppManagementPageHandler : public app_management::mojom::PageHandler,
void OnPreferredAppsListWillBeDestroyed(
apps::PreferredAppsListHandle* handle) override;
+#if !BUILDFLAG(IS_CHROMEOS)
+ void MakeAppPreferredAndResetOthers(const web_app::AppId& app_id,
+ bool set_to_preferred,
+ web_app::AllAppsLock& lock);
+#endif // !BUILDFLAG(IS_CHROMEOS)
+
mojo::Receiver<app_management::mojom::PageHandler> receiver_;
mojo::Remote<app_management::mojom::Page> page_;
raw_ptr<Profile> profile_;
- const raw_ref<Delegate> delegate_;
-
#if BUILDFLAG(IS_CHROMEOS_ASH)
AppManagementShelfDelegate shelf_delegate_;
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
- const raw_ref<apps::PreferredAppsListHandle> preferred_apps_list_handle_;
+ const raw_ref<Delegate> delegate_;
base::ScopedObservation<apps::AppRegistryCache,
apps::AppRegistryCache::Observer>
@@ -132,6 +146,8 @@ class AppManagementPageHandler : public app_management::mojom::PageHandler,
base::ScopedObservation<web_app::WebAppRegistrar,
web_app::WebAppRegistrarObserver>
registrar_observation_{this};
+
+ base::WeakPtrFactory<AppManagementPageHandler> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_WEBUI_APP_MANAGEMENT_APP_MANAGEMENT_PAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler_unittest.cc b/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler_unittest.cc
new file mode 100644
index 00000000000..edf5f4cae00
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_management/app_management_page_handler_unittest.cc
@@ -0,0 +1,743 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/test/test_future.h"
+#include "chrome/browser/web_applications/mojom/user_display_mode.mojom-shared.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/webui/resources/cr_components/app_management/app_management.mojom.h"
+
+#if BUILDFLAG(IS_CHROMEOS)
+#include "ash/components/arc/test/fake_app_instance.h"
+#include "chrome/browser/apps/app_service/app_service_test.h"
+#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
+#include "chrome/browser/ash/app_list/arc/arc_app_test.h"
+#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ash/apps/apk_web_app_service.h"
+#include "components/arc/test/fake_intent_helper_instance.h"
+#else
+#include "base/test/scoped_feature_list.h"
+#include "chrome/common/chrome_features.h"
+#endif // BUILDFLAG(IS_CHROMEOS)
+
+using ::testing::Contains;
+using ::testing::ElementsAre;
+
+namespace apps {
+namespace {
+class TestDelegate : public AppManagementPageHandler::Delegate {
+ public:
+ TestDelegate() = default;
+ TestDelegate(const TestDelegate&) = delete;
+ TestDelegate& operator=(const TestDelegate&) = delete;
+
+ // AppManagementPageHandler::Delegate:
+
+ ~TestDelegate() override = default;
+
+ gfx::NativeWindow GetUninstallAnchorWindow() const override {
+ return gfx::NativeWindow();
+ }
+};
+} // namespace
+class AppManagementPageHandlerTestBase : public testing::Test {
+ public:
+ void SetUp() override {
+ testing::Test::SetUp();
+
+ profile_ = std::make_unique<TestingProfile>();
+ delegate_ = std::unique_ptr<TestDelegate>();
+
+ web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
+
+ mojo::PendingReceiver<app_management::mojom::Page> page;
+ mojo::Remote<app_management::mojom::PageHandler> handler;
+ handler_ = std::make_unique<AppManagementPageHandler>(
+ handler.BindNewPipeAndPassReceiver(),
+ page.InitWithNewPipeAndPassRemote(), profile_.get(), *delegate_.get());
+#if !BUILDFLAG(IS_CHROMEOS)
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kDesktopPWAsLinkCapturing);
+#endif // !BUILDFLAG(IS_CHROMEOS)
+ }
+
+ Profile* profile() { return profile_.get(); }
+ AppManagementPageHandler* handler() { return handler_.get(); }
+
+ protected:
+ void AwaitWebAppCommandsComplete() {
+ web_app::WebAppProvider* provider =
+ web_app::WebAppProvider::GetForTest(profile());
+ provider->command_manager().AwaitAllCommandsCompleteForTesting();
+ }
+
+ bool IsAppPreferred(const web_app::AppId& app_id) {
+ base::test::TestFuture<app_management::mojom::AppPtr> result;
+ handler()->GetApp(app_id, result.GetCallback());
+ return result.Get()->is_preferred_app;
+ }
+
+ std::vector<std::string> GetOverlappingPreferredApps(
+ const web_app::AppId& app_id) {
+ base::test::TestFuture<const std::vector<std::string>&> result;
+ handler()->GetOverlappingPreferredApps(app_id, result.GetCallback());
+ EXPECT_TRUE(result.Wait());
+ return result.Get();
+ }
+
+ private:
+ content::BrowserTaskEnvironment task_environment_;
+ data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
+ std::unique_ptr<TestingProfile> profile_;
+ std::unique_ptr<TestDelegate> delegate_;
+ std::unique_ptr<AppManagementPageHandler> handler_;
+#if !BUILDFLAG(IS_CHROMEOS)
+ base::test::ScopedFeatureList scoped_feature_list_;
+#endif // !BUILDFLAG(IS_CHROMEOS)
+};
+
+TEST_F(AppManagementPageHandlerTestBase, GetApp) {
+ // Create a web app entry with scope, which would be recognised
+ // as normal web app in the web app system.
+ auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info->title = u"app_name";
+ web_app_info->start_url = GURL("https://example.com/");
+
+ std::string app_id =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info));
+
+ base::test::TestFuture<app_management::mojom::AppPtr> result;
+ handler()->GetApp(app_id, result.GetCallback());
+
+ EXPECT_EQ(result.Get()->id, app_id);
+ EXPECT_EQ(result.Get()->title.value(), "app_name");
+ EXPECT_EQ(result.Get()->type, AppType::kWeb);
+}
+
+TEST_F(AppManagementPageHandlerTestBase, GetPreferredAppTest) {
+ auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info->title = u"app_name";
+ web_app_info->start_url = GURL("https://example.com/index.html");
+ web_app_info->scope = GURL("https://example.com/abc/");
+
+ std::string app_id =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info));
+
+ EXPECT_FALSE(IsAppPreferred(app_id));
+
+ handler()->SetPreferredApp(app_id, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ base::test::TestFuture<app_management::mojom::AppPtr> updated_result;
+ handler()->GetApp(app_id, updated_result.GetCallback());
+ EXPECT_TRUE(updated_result.Get()->is_preferred_app);
+
+ EXPECT_THAT(updated_result.Get()->supported_links,
+ testing::Contains("example.com/abc/*"));
+}
+
+#if !BUILDFLAG(IS_CHROMEOS)
+TEST_F(AppManagementPageHandlerTestBase, SupportedLinksWithPort) {
+ auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info->title = u"app_name";
+ web_app_info->start_url = GURL("https://example.com/index.html");
+ web_app_info->scope = GURL("https://example:8080/abc/");
+
+ std::string app_id =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info));
+ base::test::TestFuture<app_management::mojom::AppPtr> result;
+ handler()->GetApp(app_id, result.GetCallback());
+
+ EXPECT_THAT(result.Get()->supported_links,
+ testing::Contains("example:8080/abc/*"));
+}
+
+TEST_F(AppManagementPageHandlerTestBase, PreferredAppNonOverlappingScopePort) {
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"App 1";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example:8080/");
+
+ std::string app_id1 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"App 2";
+ web_app_info2->start_url = GURL("https://example.com/abc/index.html");
+ web_app_info2->scope = GURL("https://example:9090/");
+
+ std::string app_id2 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+ EXPECT_FALSE(IsAppPreferred(app_id1));
+ EXPECT_FALSE(IsAppPreferred(app_id2));
+
+ // app_id1 is set to preferred, app_id2 is not affected.
+ handler()->SetPreferredApp(app_id1, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+ EXPECT_TRUE(IsAppPreferred(app_id1));
+ EXPECT_FALSE(IsAppPreferred(app_id2));
+
+ // app_id2 is set as preferred, app_id1 is not affected.
+ handler()->SetPreferredApp(app_id2, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+ EXPECT_TRUE(IsAppPreferred(app_id1));
+ EXPECT_TRUE(IsAppPreferred(app_id2));
+}
+#endif // !BUILDFLAG(IS_CHROMEOS)
+
+TEST_F(AppManagementPageHandlerTestBase, PreferredAppOverlappingScopePort) {
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"App 1";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example:8080/");
+
+ std::string app_id1 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"App 2";
+ web_app_info2->start_url = GURL("https://example.com/abc/index.html");
+ web_app_info2->scope = GURL("https://example:8080/abc");
+
+ std::string app_id2 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+ EXPECT_FALSE(IsAppPreferred(app_id1));
+ EXPECT_FALSE(IsAppPreferred(app_id2));
+
+ // Setting app_id1 as preferred should set app_id2 as not preferred.
+ handler()->SetPreferredApp(app_id1, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+ EXPECT_TRUE(IsAppPreferred(app_id1));
+ EXPECT_FALSE(IsAppPreferred(app_id2));
+
+ handler()->SetPreferredApp(app_id2, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+// On Windows, Mac and Linux, nested scopes are not considered overlapping,
+// so 2 apps having nested scopes can be set as preferred at the same time,
+// while on CrOS, this cannot happen.
+// TODO(crbug.com/1476011): If CrOS decides to treat overlapping apps
+// as non-nested ones, then this will need to be modified.
+#if BUILDFLAG(IS_CHROMEOS)
+ EXPECT_FALSE(IsAppPreferred(app_id1));
+#else
+ EXPECT_TRUE(IsAppPreferred(app_id1));
+#endif // BUILDFLAG(IS_CHROMEOS)
+ EXPECT_TRUE(IsAppPreferred(app_id2));
+}
+
+TEST_F(AppManagementPageHandlerTestBase,
+ GetPreferredAppDifferentScopesNotReset) {
+ // Install app1 and mark it as preferred.
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"app_name";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string app_id1 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ handler()->SetPreferredApp(app_id1, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ // Install app2 with same scope as app1.
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"app_name2";
+ web_app_info2->start_url = GURL("https://example.com/index_abc.html");
+ web_app_info2->scope = GURL("https://example.com/");
+
+ std::string app_id2 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ // Install app3 with a completely different scope than app1 and app2.
+ auto web_app_info3 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info3->title = u"app_name3";
+ web_app_info3->start_url = GURL("https://abc.com/index.html");
+ web_app_info3->scope = GURL("https://abc.com/def/");
+
+ std::string app_id3 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info3));
+
+ // Set app2 and app3 as preferred
+ handler()->SetPreferredApp(app_id2, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+ handler()->SetPreferredApp(app_id3, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ // Verify the preferred app status of app_id1, app_id2 and app_id3. app_id1's
+ // preferred app status should have been reset to false, while app_id2 and
+ // app_id3 should still be true.
+ EXPECT_FALSE(IsAppPreferred(app_id1));
+ EXPECT_TRUE(IsAppPreferred(app_id2));
+ EXPECT_TRUE(IsAppPreferred(app_id3));
+}
+
+TEST_F(AppManagementPageHandlerTestBase, GetPreferredAppTestInvalidAppId) {
+ auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info->title = u"app_name";
+ web_app_info->start_url = GURL("https://example.com/index.html");
+ web_app_info->scope = GURL("https://example.com/");
+
+ std::string app_id =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info));
+
+ EXPECT_FALSE(IsAppPreferred(app_id));
+ handler()->SetPreferredApp("def", /*is_preferred_app=*/true);
+ EXPECT_FALSE(IsAppPreferred(app_id));
+}
+
+TEST_F(AppManagementPageHandlerTestBase,
+ GetPreferredAppTestInvalidSupportedLink) {
+ auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info->title = u"app_name";
+ web_app_info->start_url = GURL("https://example.com/index.html");
+ web_app_info->scope = GURL("abc://example.com/");
+
+ std::string app_id =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info));
+
+ EXPECT_FALSE(IsAppPreferred(app_id));
+
+ handler()->SetPreferredApp(app_id, /*is_preferred_app=*/true);
+
+ base::test::TestFuture<app_management::mojom::AppPtr> updated_result;
+ handler()->GetApp(app_id, updated_result.GetCallback());
+ EXPECT_FALSE(updated_result.Get()->is_preferred_app);
+ EXPECT_TRUE(updated_result.Get()->supported_links.empty());
+}
+
+TEST_F(AppManagementPageHandlerTestBase,
+ GetOverlappingPreferredAppsSingleAppOnly) {
+ // First install an app that has some scope set in it.
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"app_name";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string app_id1 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ // 2nd app has the same scope, but different app_id and opens in a new window.
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"app_name2";
+ web_app_info2->start_url = GURL("https://example.com/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+ web_app_info2->scope = GURL("https://example.com/");
+
+ std::string app_id2 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ // Set app_id1 as a preferred app.
+ handler()->SetPreferredApp(app_id1, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ std::vector<std::string> overlapping_apps =
+ GetOverlappingPreferredApps(app_id1);
+ EXPECT_TRUE(overlapping_apps.empty());
+}
+
+TEST_F(AppManagementPageHandlerTestBase,
+ GetOverlappingPreferredAppsNestedScope) {
+ // First install an app that has some scope set in it.
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"app_name";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string app_id1 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ // 2nd app has the same scope, but different app_id and opens in a new window.
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"app_name2";
+ web_app_info2->start_url = GURL("https://example.com/nested/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+ web_app_info2->scope = GURL("https://example.com/nested/");
+
+ std::string app_id2 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ // Set app_id1 as a preferred app.
+ handler()->SetPreferredApp(app_id1, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ std::vector<std::string> overlapping_apps =
+ GetOverlappingPreferredApps(app_id2);
+
+// TODO(crbug.com/1476011): Modify if nested scope behavior changes on CrOS.
+// On Windows, Mac and Linux, apps with nested scopes are not considered
+// overlapping, but on CrOS they are.
+#if BUILDFLAG(IS_CHROMEOS)
+ EXPECT_THAT(overlapping_apps, testing::ElementsAre(app_id1));
+#else
+ EXPECT_TRUE(overlapping_apps.empty());
+#endif // BUILDFLAG(IS_CHROMEOS)
+}
+
+TEST_F(AppManagementPageHandlerTestBase, GetOverlappingPreferredAppsTwice) {
+ // First install an app that has some scope set in it.
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"app_name";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string app_id1 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ // 2nd app has the same scope, but different app_id and opens in a new window.
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"app_name2";
+ web_app_info2->start_url = GURL("https://example.com/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+ web_app_info2->scope = GURL("https://example.com/");
+
+ std::string app_id2 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ // Set app_id1 as a preferred app.
+ handler()->SetPreferredApp(app_id1, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ std::vector<std::string> overlapping_apps =
+ GetOverlappingPreferredApps(app_id1);
+ EXPECT_TRUE(overlapping_apps.empty());
+
+ // Set app_id2 as a preferred app.
+ handler()->SetPreferredApp(app_id2, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ // app_id1 should be returned here.
+ overlapping_apps = GetOverlappingPreferredApps(app_id1);
+ EXPECT_THAT(overlapping_apps, testing::ElementsAre(app_id2));
+}
+
+TEST_F(AppManagementPageHandlerTestBase,
+ GetOverlappingPreferredAppsTwiceNonPreferred) {
+ // First install an app that has some scope set in it.
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"app_name";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string app_id1 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ // 2nd app has the same scope, but different app_id and opens in a new window.
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"app_name2";
+ web_app_info2->start_url = GURL("https://example.com/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+ web_app_info2->scope = GURL("https://example.com/");
+
+ std::string app_id2 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ // Set app_id1 as a preferred app.
+ handler()->SetPreferredApp(app_id1, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ std::vector<std::string> overlapping_apps =
+ GetOverlappingPreferredApps(app_id1);
+ EXPECT_TRUE(overlapping_apps.empty());
+
+ // Set app_id2 as a preferred app.
+ handler()->SetPreferredApp(app_id2, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ // Since app_id2 is already a preferred app, there should not be any other
+ // preferred apps.
+ overlapping_apps = GetOverlappingPreferredApps(app_id2);
+ EXPECT_TRUE(overlapping_apps.empty());
+}
+
+TEST_F(AppManagementPageHandlerTestBase,
+ GetOverlappingPreferredAppsShortcutApp) {
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"app_name";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string app_id1 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ // The WebAppRegistrar treats an app without a scope as a shortcut app.
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"app_name2";
+ web_app_info2->start_url = GURL("https://example.com/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+
+ std::string app_id2 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ // The result should be empty since app_id2 is a shortcut app.
+ std::vector<std::string> overlapping_apps =
+ GetOverlappingPreferredApps(app_id1);
+ EXPECT_TRUE(overlapping_apps.empty());
+}
+
+TEST_F(AppManagementPageHandlerTestBase, DifferentScopeNoOverlap) {
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"app_name";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("invalid");
+
+ std::string app_id1 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"app_name2";
+ web_app_info2->start_url = GURL("https://example_2.com/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+ web_app_info2->scope = GURL("https://example_2.com/");
+
+ std::string app_id2 =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ // The result should be empty since none of the apps have the same scope.
+ std::vector<std::string> overlapping_apps =
+ GetOverlappingPreferredApps(app_id1);
+ EXPECT_TRUE(overlapping_apps.empty());
+}
+
+// TODO(crbug.com/1476011): The overlapping nested scope based behavior is only
+// on ChromeOS, and will need to be modified if the behavior changes.
+#if BUILDFLAG(IS_CHROMEOS)
+TEST_F(AppManagementPageHandlerTestBase, UseCase_ADisabledBDisabled) {
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"A";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string appA =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"B";
+ web_app_info2->start_url = GURL("https://example.com/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+ web_app_info2->scope = GURL("https://example.com/abc/");
+
+ std::string appB =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ std::vector<std::string> overlapping_apps_a =
+ GetOverlappingPreferredApps(appA);
+ EXPECT_TRUE(overlapping_apps_a.empty());
+
+ std::vector<std::string> overlapping_apps_b =
+ GetOverlappingPreferredApps(appB);
+ EXPECT_TRUE(overlapping_apps_b.empty());
+}
+
+TEST_F(AppManagementPageHandlerTestBase, UseCase_ADisabledBEnabled) {
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"A";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string appA =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"B";
+ web_app_info2->start_url = GURL("https://example.com/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+ web_app_info2->scope = GURL("https://example.com/abc/");
+
+ std::string appB =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ handler()->SetPreferredApp(appB, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ // B is set as preferred app and its scope matches the prefix of A's scope.
+ std::vector<std::string> overlapping_apps_a =
+ GetOverlappingPreferredApps(appA);
+ EXPECT_THAT(overlapping_apps_a, testing::ElementsAre(appB));
+
+ std::vector<std::string> overlapping_apps_b =
+ GetOverlappingPreferredApps(appB);
+ EXPECT_TRUE(overlapping_apps_b.empty());
+}
+
+TEST_F(AppManagementPageHandlerTestBase, UseCase_AEnabledBDisabled) {
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"A";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string appA =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"B";
+ web_app_info2->start_url = GURL("https://example.com/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+ web_app_info2->scope = GURL("https://example.com/abc/");
+
+ std::string appB =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ handler()->SetPreferredApp(appA, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ std::vector<std::string> overlapping_apps_a =
+ GetOverlappingPreferredApps(appA);
+ EXPECT_TRUE(overlapping_apps_a.empty());
+
+ // A is set as preferred app and its scope matches the prefix of B's scope.
+ std::vector<std::string> overlapping_apps_b =
+ GetOverlappingPreferredApps(appB);
+ EXPECT_THAT(overlapping_apps_b, testing::ElementsAre(appA));
+}
+
+TEST_F(AppManagementPageHandlerTestBase, UseCase_AEnabledBEnabled) {
+ auto web_app_info1 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info1->title = u"A";
+ web_app_info1->start_url = GURL("https://example.com/index.html");
+ web_app_info1->scope = GURL("https://example.com/");
+
+ std::string appA =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info1));
+
+ auto web_app_info2 = std::make_unique<web_app::WebAppInstallInfo>();
+ web_app_info2->title = u"B";
+ web_app_info2->start_url = GURL("https://example.com/index_abc.html");
+ web_app_info2->user_display_mode =
+ web_app::mojom::UserDisplayMode::kStandalone;
+ web_app_info2->scope = GURL("https://example.com/abc/");
+
+ std::string appB =
+ web_app::test::InstallWebApp(profile(), std::move(web_app_info2));
+
+ handler()->SetPreferredApp(appA, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+ handler()->SetPreferredApp(appB, /*is_preferred_app=*/true);
+ AwaitWebAppCommandsComplete();
+
+ // Since both are enabled, B's scope prefix matches A's scope and is longer,
+ // so that is returned for A.
+ std::vector<std::string> overlapping_apps_a =
+ GetOverlappingPreferredApps(appA);
+ EXPECT_THAT(overlapping_apps_a, testing::ElementsAre(appB));
+
+ // While A and B are both enabled, and their scopes prefix match, B should not
+ // return A to prevent document links for being captured by A, who has a
+ // shorter scope.
+ std::vector<std::string> overlapping_apps_b =
+ GetOverlappingPreferredApps(appB);
+ EXPECT_TRUE(overlapping_apps_b.empty());
+}
+#endif // BUILDFLAG(IS_CHROMEOS)
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+class AppManagementPageHandlerArcTest
+ : public AppManagementPageHandlerTestBase {
+ public:
+ void SetUp() override {
+ AppManagementPageHandlerTestBase::SetUp();
+ // We want to set up the real ArcIntentHelper KeyedService with a fake
+ // ArcIntentHelperBridge, so that it's the same object that ArcApps
+ // uses to launch apps.
+ arc_test_.set_initialize_real_intent_helper_bridge(true);
+ arc_test_.SetUp(profile());
+ }
+
+ void TearDown() override {
+ arc_test_.StopArcInstance();
+ arc_test_.TearDown();
+ }
+
+ protected:
+ ArcAppTest* arc_test() { return &arc_test_; }
+
+ private:
+ ArcAppTest arc_test_;
+};
+
+TEST_F(AppManagementPageHandlerArcTest, OpenStorePageArcAppPlayStore) {
+ const auto& fake_apps = arc_test()->fake_apps();
+ std::string package_name = fake_apps[1]->package_name;
+ std::string app_id = ArcAppListPrefs::GetAppId(fake_apps[1]->package_name,
+ fake_apps[1]->activity);
+
+ std::vector<arc::mojom::AppInfoPtr> apps;
+ apps.push_back(arc::mojom::AppInfo::New("Play Store", arc::kPlayStorePackage,
+ arc::kPlayStoreActivity));
+ apps.push_back(fake_apps[1]->Clone());
+ arc_test()->app_instance()->SendRefreshAppList(apps);
+
+ handler()->OpenStorePage(app_id);
+
+ auto* intent_helper = arc_test()->intent_helper_instance();
+ const std::vector<arc::FakeIntentHelperInstance::HandledIntent>& intents =
+ intent_helper->handled_intents();
+ EXPECT_EQ(intents.size(), 1U);
+ EXPECT_EQ(intents[0].activity->package_name, arc::kPlayStorePackage);
+ EXPECT_EQ(intents[0].intent->data.value(),
+ "https://play.google.com/store/apps/details?id=" +
+ fake_apps[1]->package_name);
+}
+
+TEST_F(AppManagementPageHandlerArcTest, OpenStorePageWebAppPlayStore) {
+ std::vector<arc::mojom::ArcPackageInfoPtr> packages;
+ auto package = arc::mojom::ArcPackageInfo::New();
+ package->package_name = "package_name";
+ package->package_version = 1;
+ package->last_backup_android_id = 1;
+ package->last_backup_time = 1;
+ package->sync = true;
+ package->web_app_info = arc::mojom::WebAppInfo::New(
+ "Fake App Title", "https://www.google.com/index.html",
+ "https://www.google.com/", 0xFFAABBCC);
+ packages.push_back(std::move(package));
+
+ std::vector<arc::mojom::AppInfoPtr> apps;
+ apps.push_back(arc::mojom::AppInfo::New("Play Store", arc::kPlayStorePackage,
+ arc::kPlayStoreActivity));
+
+ arc_test()->app_instance()->SendRefreshAppList(apps);
+ ash::ApkWebAppService* service = ash::ApkWebAppService::Get(profile());
+
+ base::test::TestFuture<const std::string&, const web_app::AppId&>
+ installed_result;
+
+ service->SetWebAppInstalledCallbackForTesting(installed_result.GetCallback());
+ arc_test()->app_instance()->SendRefreshPackageList(std::move(packages));
+
+ web_app::AppId app_id = installed_result.Get<1>();
+ handler()->OpenStorePage(app_id);
+
+ auto* intent_helper = arc_test()->intent_helper_instance();
+ const std::vector<arc::FakeIntentHelperInstance::HandledIntent>& intents =
+ intent_helper->handled_intents();
+ EXPECT_EQ(intents.size(), 1U);
+ EXPECT_EQ(intents[0].activity->package_name, arc::kPlayStorePackage);
+ EXPECT_EQ(intents[0].intent->data.value(),
+ "https://play.google.com/store/apps/details?id=package_name");
+}
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+} // namespace apps
diff --git a/chromium/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc b/chromium/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
index 83c37e95de2..ef278430b52 100644
--- a/chromium/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
+++ b/chromium/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
@@ -73,8 +73,8 @@ bool AppManagementShelfDelegate::IsPolicyPinned(
}
// The app doesn't exist on the shelf - check launcher prefs instead.
std::vector<std::string> policy_pinned_apps =
- shelf_controller->shelf_prefs()->GetAppsPinnedByPolicy(
- shelf_controller_helper_.get());
+ ChromeShelfPrefs::GetAppsPinnedByPolicy(
+ shelf_controller_helper_->profile());
return base::Contains(policy_pinned_apps, app_id);
}
diff --git a/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.cc b/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.cc
index 2baf7ec77fb..0c1144b6ab1 100644
--- a/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.cc
+++ b/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.cc
@@ -5,6 +5,8 @@
#include "chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/url_constants.h"
@@ -44,18 +46,53 @@ WebAppSettingsNavigationThrottle::~WebAppSettingsNavigationThrottle() = default;
content::NavigationThrottle::ThrottleCheckResult
WebAppSettingsNavigationThrottle::WillStartRequest() {
- if (g_disable_throttle_for_testing_)
+ if (g_disable_throttle_for_testing_) {
return content::NavigationThrottle::PROCEED;
+ }
+
+ const web_app::AppId app_id =
+ web_app::GetAppIdFromAppSettingsUrl(navigation_handle()->GetURL());
+ if (app_id.empty()) {
+ return content::NavigationThrottle::BLOCK_REQUEST;
+ }
content::WebContents* web_contents = navigation_handle()->GetWebContents();
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
- if (!web_app::HasAppSettingsPage(profile, navigation_handle()->GetURL())) {
+ auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
+ if (!provider) {
+ return content::NavigationThrottle::BLOCK_REQUEST;
+ }
+
+ if (!provider->on_registry_ready().is_signaled()) {
+ provider->on_registry_ready().Post(
+ FROM_HERE,
+ base::BindOnce(&WebAppSettingsNavigationThrottle::ContinueCheckForApp,
+ weak_factory_.GetWeakPtr(), app_id));
+ return content::NavigationThrottle::DEFER;
+ }
+
+ if (!provider->registrar_unsafe().IsLocallyInstalled(app_id)) {
return content::NavigationThrottle::BLOCK_REQUEST;
}
+
return content::NavigationThrottle::PROCEED;
}
const char* WebAppSettingsNavigationThrottle::GetNameForLogging() {
return "WebAppSettingsNavigationThrottle";
}
+
+void WebAppSettingsNavigationThrottle::ContinueCheckForApp(
+ const web_app::AppId& app_id) {
+ content::WebContents* web_contents = navigation_handle()->GetWebContents();
+ Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
+ CHECK(provider);
+ if (provider->registrar_unsafe().IsLocallyInstalled(app_id)) {
+ Resume();
+ } else {
+ CancelDeferredNavigation(content::NavigationThrottle::BLOCK_REQUEST);
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h b/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h
index efc09cdd6c1..090145a69aa 100644
--- a/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h
+++ b/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h
@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_UI_WEBUI_APP_SETTINGS_WEB_APP_SETTINGS_NAVIGATION_THROTTLE_H_
#define CHROME_BROWSER_UI_WEBUI_APP_SETTINGS_WEB_APP_SETTINGS_NAVIGATION_THROTTLE_H_
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/web_applications/web_app_id.h"
#include "content/public/browser/navigation_throttle.h"
namespace content {
@@ -29,6 +31,11 @@ class WebAppSettingsNavigationThrottle : public content::NavigationThrottle {
// content::NavigationThrottle:
ThrottleCheckResult WillStartRequest() override;
const char* GetNameForLogging() override;
+
+ private:
+ void ContinueCheckForApp(const web_app::AppId& app_id);
+
+ base::WeakPtrFactory<WebAppSettingsNavigationThrottle> weak_factory_{this};
};
#endif // CHROME_BROWSER_UI_WEBUI_APP_SETTINGS_WEB_APP_SETTINGS_NAVIGATION_THROTTLE_H_
diff --git a/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc b/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc
index b795d7d5dc1..e5149872cd1 100644
--- a/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc
+++ b/chromium/chrome/browser/ui/webui/app_settings/web_app_settings_ui.cc
@@ -27,6 +27,7 @@ namespace {
void AddAppManagementStrings(content::WebUIDataSource* html_source) {
static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ {"cancel", IDS_CANCEL},
{"close", IDS_CLOSE},
{"title", IDS_WEB_APP_SETTINGS_TITLE},
{"appManagementAppInstalledByPolicyLabel",
@@ -48,6 +49,40 @@ void AddAppManagementStrings(content::WebUIDataSource* html_source) {
IDS_APP_MANAGEMENT_FILE_HANDLING_OVERFLOW_DIALOG_TITLE},
{"fileHandlingSetDefaults",
IDS_APP_MANAGEMENT_FILE_HANDLING_SET_DEFAULTS_LINK},
+ {"appManagementIntentSettingsDialogTitle",
+ IDS_APP_MANAGEMENT_INTENT_SETTINGS_DIALOG_TITLE},
+ {"appManagementIntentSettingsTitle",
+ IDS_APP_MANAGEMENT_INTENT_SETTINGS_TITLE},
+ {"appManagementIntentOverlapDialogTitle",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_DIALOG_TITLE},
+ {"appManagementIntentOverlapChangeButton",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_CHANGE_BUTTON},
+ {"appManagementIntentSharingOpenBrowserLabel",
+ IDS_APP_MANAGEMENT_INTENT_SHARING_BROWSER_OPEN},
+ {"appManagementIntentSharingOpenAppLabel",
+ IDS_APP_MANAGEMENT_INTENT_SHARING_APP_OPEN},
+ {"appManagementIntentOverlapWarningText1App",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_WARNING_TEXT_1_APP},
+ {"appManagementIntentOverlapWarningText2Apps",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_WARNING_TEXT_2_APPS},
+ {"appManagementIntentOverlapWarningText3Apps",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_WARNING_TEXT_3_APPS},
+ {"appManagementIntentOverlapWarningText4Apps",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_WARNING_TEXT_4_APPS},
+ {"appManagementIntentOverlapWarningText5OrMoreApps",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_WARNING_TEXT_5_OR_MORE_APPS},
+ {"appManagementIntentOverlapDialogText1App",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_DIALOG_TEXT_1_APP},
+ {"appManagementIntentOverlapDialogText2Apps",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_DIALOG_TEXT_2_APPS},
+ {"appManagementIntentOverlapDialogText3Apps",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_DIALOG_TEXT_3_APPS},
+ {"appManagementIntentOverlapDialogText4Apps",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_DIALOG_TEXT_4_APPS},
+ {"appManagementIntentOverlapDialogText5OrMoreApps",
+ IDS_APP_MANAGEMENT_INTENT_OVERLAP_DIALOG_TEXT_5_OR_MORE_APPS},
+ {"appManagementIntentSharingTabExplanation",
+ IDS_APP_MANAGEMENT_INTENT_SHARING_TAB_EXPLANATION},
};
html_source->AddLocalizedStrings(kLocalizedStrings);
}
@@ -65,7 +100,7 @@ class WebAppSettingsWindowDelegate : public AppManagementPageHandler::Delegate {
}
private:
- raw_ptr<Profile, DanglingUntriaged> profile_;
+ raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_;
};
} // namespace
diff --git a/chromium/chrome/browser/ui/webui/ash/DEPS b/chromium/chrome/browser/ui/webui/ash/DEPS
index ef595e52f41..7880df5d2d2 100644
--- a/chromium/chrome/browser/ui/webui/ash/DEPS
+++ b/chromium/chrome/browser/ui/webui/ash/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+ash/system/diagnostics",
+ "+ash/test",
# Ash depends on views, so allow code in ui/webui/ash to depend on ui/views.
"+chrome/browser/ui/views",
diff --git a/chromium/chrome/browser/ui/webui/ash/add_supervision/BUILD.gn b/chromium/chrome/browser/ui/webui/ash/add_supervision/BUILD.gn
index 7cfa680b143..0c8f3d3fc4c 100644
--- a/chromium/chrome/browser/ui/webui/ash/add_supervision/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/ash/add_supervision/BUILD.gn
@@ -8,5 +8,7 @@ import("//mojo/public/tools/bindings/mojom.gni")
assert(is_chromeos_ash)
mojom("mojo_bindings") {
+ webui_module_path = "/"
sources = [ "add_supervision.mojom" ]
+ use_typescript_sources = true
}
diff --git a/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_metrics_recorder.h b/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_metrics_recorder.h
index 308c1f082f8..bd46ba8c9d2 100644
--- a/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_metrics_recorder.h
+++ b/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_metrics_recorder.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_UI_WEBUI_ASH_ADD_SUPERVISION_ADD_SUPERVISION_METRICS_RECORDER_H_
#define CHROME_BROWSER_UI_WEBUI_ASH_ADD_SUPERVISION_ADD_SUPERVISION_METRICS_RECORDER_H_
+#include "base/memory/raw_ptr_exclusion.h"
#include "base/time/time.h"
namespace base {
@@ -56,7 +57,9 @@ class AddSupervisionMetricsRecorder {
void RecordUserTime(const char* metric_name) const;
// Points to the base::DefaultTickClock by default.
- const base::TickClock* clock_;
+ // This field is not a raw_ptr<> because it was filtered by the rewriter
+ // for: #global-scope
+ RAW_PTR_EXCLUSION const base::TickClock* clock_;
// Records when the user initiates the Add Supervision process.
base::TimeTicks start_time_;
diff --git a/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui.cc b/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui.cc
index 3fd2175e64b..0ba67454ab2 100644
--- a/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui.cc
@@ -20,6 +20,8 @@
#include "chrome/browser/ui/webui/ash/add_supervision/confirm_signout_dialog.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/add_supervision_resources.h"
+#include "chrome/grit/add_supervision_resources_map.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/supervision_resources.h"
@@ -204,14 +206,14 @@ void AddSupervisionUI::SetUpResources() {
source->EnableReplaceI18nInJS();
// Forward data to the WebUI.
- source->AddResourcePath("add_supervision_api_server.js",
- IDR_ADD_SUPERVISION_API_SERVER_JS);
- source->AddResourcePath("add_supervision_ui.js", IDR_ADD_SUPERVISION_UI_JS);
- source->AddResourcePath("add_supervision_app.js", IDR_ADD_SUPERVISION_APP_JS);
+ source->AddResourcePaths(
+ base::make_span(kAddSupervisionResources, kAddSupervisionResourcesSize));
source->AddResourcePaths(
base::make_span(kSupervisionResources, kSupervisionResourcesSize));
source->AddLocalizedString("pageTitle", IDS_ADD_SUPERVISION_PAGE_TITLE);
+ source->AddLocalizedString("webviewLoadingMessage",
+ IDS_ADD_SUPERVISION_WEBVIEW_LOADING_MESSAGE);
source->AddLocalizedString("supervisedUserErrorDescription",
IDS_SUPERVISED_USER_ERROR_DESCRIPTION);
source->AddLocalizedString("supervisedUserErrorTitle",
@@ -221,14 +223,8 @@ void AddSupervisionUI::SetUpResources() {
source->AddLocalizedString("supervisedUserOfflineTitle",
IDS_SUPERVISED_USER_OFFLINE_TITLE);
- // Full paths (relative to src) are important for Mojom generated files.
- source->AddResourcePath(
- "chrome/browser/ui/webui/ash/add_supervision/"
- "add_supervision.mojom-lite.js",
- IDR_ADD_SUPERVISION_MOJOM_LITE_JS);
-
source->UseStringsJs();
- source->SetDefaultResource(IDR_ADD_SUPERVISION_HTML);
+ source->SetDefaultResource(IDR_ADD_SUPERVISION_ADD_SUPERVISION_HTML);
source->AddString("webviewUrl", supervision_url_.spec());
source->AddString("eventOriginFilter",
supervision_url_.DeprecatedGetOriginAsURL().spec());
diff --git a/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui_browsertest.cc
index 3ea76a993eb..d46e9e2299b 100644
--- a/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui_browsertest.cc
@@ -179,21 +179,22 @@ IN_PROC_BROWSER_TEST_F(AddSupervisionBrowserTest, ShowConfirmSignoutDialog) {
EXPECT_TRUE(content::WaitForLoadStop(contents()));
// Request that the dialog close before supervision has been enabled.
- ASSERT_TRUE(
- content::ExecJs(contents(), std::string(kGetAddSupervisionUIElementJS) +
- std::string(".server.requestClose()")));
+ ASSERT_TRUE(content::ExecJs(
+ contents(), std::string(kGetAddSupervisionUIElementJS) +
+ std::string(".getApiServerForTest().requestClose()")));
// Confirm that the signout dialog isn't showing
ASSERT_FALSE(ConfirmSignoutDialog::IsShowing());
// Simulate supervision being enabled.
ASSERT_TRUE(content::ExecJs(
- contents(), std::string(kGetAddSupervisionUIElementJS) +
- std::string(".server.notifySupervisionEnabled()")));
+ contents(),
+ std::string(kGetAddSupervisionUIElementJS) +
+ std::string(".getApiServerForTest().notifySupervisionEnabled()")));
// Request that the dialog is closed again.
- ASSERT_TRUE(
- content::ExecJs(contents(), std::string(kGetAddSupervisionUIElementJS) +
- std::string(".server.requestClose()")));
+ ASSERT_TRUE(content::ExecJs(
+ contents(), std::string(kGetAddSupervisionUIElementJS) +
+ std::string(".getApiServerForTest().requestClose()")));
// Confirm that the dialog is showing.
ASSERT_TRUE(ConfirmSignoutDialog::IsShowing());
@@ -218,8 +219,9 @@ IN_PROC_BROWSER_TEST_F(AddSupervisionBrowserTest, UMATest) {
// Simulate supervision being enabled.
ASSERT_TRUE(content::ExecJs(
- contents(), std::string(kGetAddSupervisionUIElementJS) +
- std::string(".server.notifySupervisionEnabled()")));
+ contents(),
+ std::string(kGetAddSupervisionUIElementJS) +
+ std::string(".getApiServerForTest().notifySupervisionEnabled()")));
// Should see 1 Add Supervision process completed.
histogram_tester.ExpectUniqueSample(
diff --git a/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.cc b/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.cc
index 614257d753f..502985a41d2 100644
--- a/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.cc
@@ -212,10 +212,8 @@ base::trace_event::TraceConfig GetTracingConfig() {
} // namespace
-// static
base::FilePath ArcGraphicsTracingHandler::GetModelPathFromTitle(
- Profile* profile,
- const std::string& title) {
+ std::string_view title) {
constexpr size_t kMaxNameSize = 32;
char normalized_name[kMaxNameSize];
size_t index = 0;
@@ -231,10 +229,9 @@ base::FilePath ArcGraphicsTracingHandler::GetModelPathFromTitle(
normalized_name[index++] = c;
}
normalized_name[index] = 0;
- return file_manager::util::GetDownloadsFolderForProfile(profile).AppendASCII(
+ return GetDownloadsFolder().AppendASCII(
base::StringPrintf("overview_tracing_%s_%" PRId64 ".json",
- normalized_name,
- (base::Time::Now() - base::Time()).InSeconds()));
+ normalized_name, Now().since_origin().InSeconds()));
}
ArcGraphicsTracingHandler::ArcGraphicsTracingHandler()
@@ -288,11 +285,9 @@ void ArcGraphicsTracingHandler::OnWindowActivated(ActivationReason reason,
arc_active_window_->AddPreTargetHandler(this);
// Limit tracing by newly activated window.
- tracing_time_min_ = TRACE_TIME_TICKS_NOW();
-}
-
-base::TimeDelta ArcGraphicsTracingHandler::GetMaxInterval() const {
- return max_tracing_time_;
+ if (tracing_active_) {
+ tracing_time_min_ = SystemTicksNow();
+ }
}
void ArcGraphicsTracingHandler::OnWindowPropertyChanged(aura::Window* window,
@@ -363,7 +358,36 @@ void ArcGraphicsTracingHandler::DiscardActiveArcWindow() {
arc_active_window_ = nullptr;
}
-void ArcGraphicsTracingHandler::Activate() {
+base::Time ArcGraphicsTracingHandler::Now() {
+ return base::Time::Now();
+}
+
+void ArcGraphicsTracingHandler::StartTracingOnController(
+ const base::trace_event::TraceConfig& trace_config,
+ content::TracingController::StartTracingDoneCallback after_start) {
+ content::TracingController::GetInstance()->StartTracing(
+ trace_config, std::move(after_start));
+}
+
+void ArcGraphicsTracingHandler::StopTracingOnController(
+ content::TracingController::CompletionCallback after_stop) {
+ auto* const controller = content::TracingController::GetInstance();
+
+ if (!controller->IsTracing()) {
+ LOG(WARNING) << "TracingController has already stopped tracing";
+ return;
+ }
+
+ controller->StopTracing(
+ content::TracingController::CreateStringEndpoint(std::move(after_stop)));
+}
+
+base::FilePath ArcGraphicsTracingHandler::GetDownloadsFolder() {
+ return file_manager::util::GetDownloadsFolderForProfile(
+ Profile::FromWebUI(web_ui()));
+}
+
+void ArcGraphicsTracingHandler::ActivateWebUIWindow() {
aura::Window* const window =
web_ui()->GetWebContents()->GetTopLevelNativeWindow();
if (!window) {
@@ -381,14 +405,14 @@ void ArcGraphicsTracingHandler::StartTracing() {
if (jank_detector_)
jank_detector_->Reset();
system_stat_collector_ = std::make_unique<arc::ArcSystemStatCollector>();
- system_stat_collector_->Start(GetMaxInterval());
+ system_stat_collector_->Start(max_tracing_time_);
// Timestamp and app information would be updated when |OnTracingStarted| is
// called.
- timestamp_ = base::Time::Now();
+ timestamp_ = Now();
UpdateActiveArcWindowInfo();
- content::TracingController::GetInstance()->StartTracing(
+ StartTracingOnController(
GetTracingConfig(),
base::BindOnce(&ArcGraphicsTracingHandler::OnTracingStarted,
weak_ptr_factory_.GetWeakPtr()));
@@ -400,25 +424,19 @@ void ArcGraphicsTracingHandler::StopTracing() {
tracing_active_ = false;
stop_tracing_timer_.Stop();
- tracing_time_max_ = TRACE_TIME_TICKS_NOW();
+ tracing_time_max_ = SystemTicksNow();
if (system_stat_collector_)
system_stat_collector_->Stop();
- content::TracingController* const controller =
- content::TracingController::GetInstance();
-
- if (!controller->IsTracing())
- return;
-
- controller->StopTracing(content::TracingController::CreateStringEndpoint(
+ StopTracingOnController(
base::BindOnce(&ArcGraphicsTracingHandler::OnTracingStopped,
- weak_ptr_factory_.GetWeakPtr())));
+ weak_ptr_factory_.GetWeakPtr()));
}
void ArcGraphicsTracingHandler::StopTracingAndActivate() {
StopTracing();
- Activate();
+ ActivateWebUIWindow();
}
void ArcGraphicsTracingHandler::SetStatus(const std::string& status) {
@@ -427,16 +445,20 @@ void ArcGraphicsTracingHandler::SetStatus(const std::string& status) {
base::Value(status.empty() ? "Idle" : status));
}
+base::TimeTicks ArcGraphicsTracingHandler::SystemTicksNow() {
+ return TRACE_TIME_TICKS_NOW();
+}
+
void ArcGraphicsTracingHandler::OnTracingStarted() {
// This is an asynchronous call and it may arrive after tracing is actually
// stopped.
if (!tracing_active_)
return;
- timestamp_ = base::Time::Now();
+ timestamp_ = Now();
UpdateActiveArcWindowInfo();
- tracing_time_min_ = TRACE_TIME_TICKS_NOW();
+ tracing_time_min_ = SystemTicksNow();
stop_tracing_timer_.Start(
FROM_HERE, system_stat_collector_->max_interval(),
base::BindOnce(&ArcGraphicsTracingHandler::StopTracingAndActivate,
@@ -448,9 +470,7 @@ void ArcGraphicsTracingHandler::OnTracingStopped(
std::string string_data;
string_data.swap(*trace_data);
- Profile* const profile = Profile::FromWebUI(web_ui());
- const base::FilePath model_path =
- GetModelPathFromTitle(profile, active_task_title_);
+ const base::FilePath model_path = GetModelPathFromTitle(active_task_title_);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
@@ -475,14 +495,18 @@ void ArcGraphicsTracingHandler::OnGraphicsModelReady(
void ArcGraphicsTracingHandler::HandleSetMaxTime(
const base::Value::List& args) {
- DCHECK_EQ(1U, args.size());
+ if (args.size() != 1) {
+ LOG(ERROR) << "Expect 1 numeric arg";
+ return;
+ }
- if (!args[0].is_int()) {
- LOG(ERROR) << "Invalid input";
+ auto new_time = args[0].GetIfDouble();
+ if (!new_time.has_value() || *new_time < 1.0) {
+ LOG(ERROR) << "Interval too small or not a number: " << args[0];
return;
}
- max_tracing_time_ = base::Seconds(args[0].GetInt());
- DCHECK_GE(max_tracing_time_, base::Seconds(1));
+
+ max_tracing_time_ = base::Seconds(*new_time);
}
void ArcGraphicsTracingHandler::HandleLoadFromText(
diff --git a/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.h b/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.h
index 0f0b46e9664..e47dd940a75 100644
--- a/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.h
@@ -16,13 +16,12 @@
#include "base/timer/timer.h"
#include "base/values.h"
#include "components/exo/surface_observer.h"
+#include "content/public/browser/tracing_controller.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "ui/aura/window_observer.h"
#include "ui/events/event_handler.h"
#include "ui/wm/public/activation_change_observer.h"
-class Profile;
-
namespace arc {
class ArcGraphicsJankDetector;
class ArcSystemStatCollector;
@@ -45,8 +44,7 @@ class ArcGraphicsTracingHandler : public content::WebUIMessageHandler,
public ui::EventHandler,
public exo::SurfaceObserver {
public:
- static base::FilePath GetModelPathFromTitle(Profile* profile,
- const std::string& title);
+ base::FilePath GetModelPathFromTitle(std::string_view title);
ArcGraphicsTracingHandler();
@@ -77,8 +75,32 @@ class ArcGraphicsTracingHandler : public content::WebUIMessageHandler,
void OnSurfaceDestroying(exo::Surface* surface) override;
void OnCommit(exo::Surface* surface) override;
+ // Visible for testing.
+ base::TimeDelta max_tracing_time() const { return max_tracing_time_; }
+
private:
- void Activate();
+ virtual void StartTracingOnController(
+ const base::trace_event::TraceConfig& trace_config,
+ content::TracingController::StartTracingDoneCallback after_start);
+ virtual void StopTracingOnController(
+ content::TracingController::CompletionCallback after_stop);
+
+ // For testing. This lets tests avoid casting from BrowserContext to Profile.
+ virtual base::FilePath GetDownloadsFolder();
+
+ // There is a ScopedTimeClockOverrides for tests that makes this seem
+ // redundant, but it is rather awkward to have a single test base which
+ // utilizes either system time or mock time, as this must be specified in
+ // the constructor, and the childmost test class constructor must be
+ // parameterless.
+ virtual base::Time Now();
+
+ // Exposed for testing. This implementation uses TRACE_TIME_TICKS_NOW.
+ // Returns the timestamp using clock_gettime(CLOCK_MONOTONIC), which is
+ // needed for comparison with trace timestamps.
+ virtual base::TimeTicks SystemTicksNow();
+
+ virtual void ActivateWebUIWindow();
void StartTracing();
void StopTracing();
void StopTracingAndActivate();
@@ -102,9 +124,6 @@ class ArcGraphicsTracingHandler : public content::WebUIMessageHandler,
// Stops tracking ARC window for janks.
void DiscardActiveArcWindow();
- // Returns max sampling interval to display.
- base::TimeDelta GetMaxInterval() const;
-
// Indicates that tracing was initiated by this handler.
bool tracing_active_ = false;
diff --git a/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler_unittest.cc b/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler_unittest.cc
index 4c336045adc..6a51c68bf09 100644
--- a/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler_unittest.cc
@@ -3,51 +3,296 @@
// found in the LICENSE file.
#include "chrome/browser/ui/webui/ash/arc_graphics_tracing/arc_graphics_tracing_handler.h"
+
+#include "ash/components/arc/arc_prefs.h"
+#include "ash/components/arc/test/arc_task_window_builder.h"
+#include "ash/constants/ash_switches.h"
#include "base/files/file_path.h"
-#include "base/time/time_override.h"
-#include "chrome/browser/ash/file_manager/path_util.h"
+#include "base/test/test_file_util.h"
+#include "base/time/time.h"
+#include "chrome/browser/ash/app_list/arc/arc_app_test.h"
+#include "chrome/test/base/chrome_ash_test_base.h"
+#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
+#include "components/exo/shell_surface_util.h"
+#include "components/exo/wm_helper.h"
+#include "components/prefs/testing_pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_message_handler.h"
#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_web_ui.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
namespace {
-class ArcGraphicsTracingHandlerTest : public testing::Test {
+class TestHandler : public ArcGraphicsTracingHandler {
+ public:
+ using content::WebUIMessageHandler::set_web_ui;
+
+ void StartTracingOnControllerRespond() {
+ DCHECK(after_start_);
+ std::move(after_start_).Run();
+ }
+
+ void StopTracingOnControllerRespond(std::unique_ptr<std::string> trace_data) {
+ DCHECK(after_stop_);
+ std::move(after_stop_).Run(std::move(trace_data));
+ }
+
+ void set_downloads_folder(const base::FilePath& downloads_folder) {
+ downloads_folder_ = downloads_folder;
+ }
+
+ void set_now(base::Time now) { now_ = now; }
+ base::Time Now() override { return now_; }
+ base::TimeTicks SystemTicksNow() override {
+ return now_ - trace_time_base_ + base::TimeTicks();
+ }
+
+ void set_trace_time_base(base::Time trace_time_base) {
+ trace_time_base_ = trace_time_base;
+ }
+
+ private:
+ void StartTracingOnController(
+ const base::trace_event::TraceConfig& trace_config,
+ content::TracingController::StartTracingDoneCallback after_start)
+ override {
+ after_start_ = std::move(after_start);
+ }
+
+ void StopTracingOnController(
+ content::TracingController::CompletionCallback after_stop) override {
+ after_stop_ = std::move(after_stop);
+ }
+
+ void ActivateWebUIWindow() override {
+ // TODO(matvore): See if we can make the default implementation for this
+ // method run in tests.
+ }
+
+ base::FilePath GetDownloadsFolder() override { return downloads_folder_; }
+
+ content::TracingController::StartTracingDoneCallback after_start_;
+ content::TracingController::CompletionCallback after_stop_;
+
+ base::Time trace_time_base_;
+ base::FilePath downloads_folder_;
+ base::Time now_;
+};
+
+class ArcGraphicsTracingHandlerTest : public ChromeAshTestBase {
public:
- ArcGraphicsTracingHandlerTest() = default;
+ ArcGraphicsTracingHandlerTest()
+ : ChromeAshTestBase(std::unique_ptr<base::test::TaskEnvironment>(
+ std::make_unique<content::BrowserTaskEnvironment>(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME))) {}
+
~ArcGraphicsTracingHandlerTest() override = default;
ArcGraphicsTracingHandlerTest(const ArcGraphicsTracingHandlerTest&) = delete;
ArcGraphicsTracingHandlerTest& operator=(
const ArcGraphicsTracingHandlerTest&) = delete;
+ void SetUp() override {
+ ChromeAshTestBase::SetUp();
+
+ profile_ = std::make_unique<TestingProfile>();
+ arc_app_test_.SetUp(profile_.get());
+
+ // WMHelper constructor sets a global instance which the Handler constructor
+ // requires.
+ wm_helper_ = std::make_unique<exo::WMHelper>();
+ download_path_ = base::GetTempDirForTesting();
+ web_ui_ = std::make_unique<content::TestWebUI>();
+ handler_ = std::make_unique<TestHandler>();
+ handler_->set_downloads_folder(download_path_);
+ handler_->set_web_ui(web_ui_.get());
+
+ local_pref_service_ = std::make_unique<TestingPrefServiceSimple>();
+ TestingBrowserProcess::GetGlobal()->SetLocalState(
+ local_pref_service_.get());
+ arc::prefs::RegisterLocalStatePrefs(local_pref_service_->registry());
+
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ ash::switches::kEnableArcVm);
+ }
+
+ void TearDown() override {
+ TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
+ local_pref_service_.reset();
+
+ handler_.reset();
+ web_ui_.reset();
+ wm_helper_.reset();
+
+ arc_app_test_.TearDown();
+
+ profile_.reset();
+
+ ChromeAshTestBase::TearDown();
+ }
+
protected:
- // NOTE: The initialization order of these members matters.
- content::BrowserTaskEnvironment task_environment_;
- TestingProfile* profile() { return &profile_; }
+ void FastForwardClockAndTaskQueue(base::TimeDelta delta) {
+ handler_->set_now(handler_->Now() + delta);
+ task_environment()->FastForwardBy(delta);
+ }
- private:
- TestingProfile profile_;
+ void SendStartStopKey() {
+ ui::KeyEvent ev{ui::ET_KEY_RELEASED, ui::VKEY_G,
+ ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN};
+ handler_->OnKeyEvent(&ev);
+ }
+
+ // The time relative to which trace tick timestamps are calculated. This is
+ // typically when the system was booted.
+ base::Time trace_time_base_;
+ std::unique_ptr<TestingProfile> profile_;
+ ArcAppTest arc_app_test_;
+ std::unique_ptr<exo::WMHelper> wm_helper_;
+ base::FilePath download_path_;
+ std::unique_ptr<content::TestWebUI> web_ui_;
+ std::unique_ptr<TestHandler> handler_;
+
+ std::unique_ptr<TestingPrefServiceSimple> local_pref_service_;
};
TEST_F(ArcGraphicsTracingHandlerTest, ModelName) {
- base::subtle::ScopedTimeClockOverrides time_override(
- []() { return base::Time::UnixEpoch() + base::Seconds(1); }, nullptr,
- nullptr);
+ base::FilePath download_path = base::FilePath::FromASCII("/mnt/downloads");
+ handler_->set_downloads_folder(download_path);
- const base::FilePath download_path =
- file_manager::util::GetDownloadsFolderForProfile(profile());
+ handler_->set_now(base::Time::UnixEpoch() + base::Seconds(1));
EXPECT_EQ(download_path.AppendASCII(
"overview_tracing_test_title_1_11644473601.json"),
- ArcGraphicsTracingHandler::GetModelPathFromTitle(profile(),
- "Test Title #:1"));
+ handler_->GetModelPathFromTitle("Test Title #:1"));
EXPECT_EQ(
download_path.AppendASCII(
"overview_tracing_0123456789012345678901234567890_11644473601.json"),
- ArcGraphicsTracingHandler::GetModelPathFromTitle(
- profile(), "0123456789012345678901234567890123456789"));
+ handler_->GetModelPathFromTitle(
+ "0123456789012345678901234567890123456789"));
+
+ handler_->set_now(base::Time::UnixEpoch() + base::Days(50));
+ EXPECT_EQ(
+ download_path.AppendASCII("overview_tracing_xyztitle_11648793600.json"),
+ handler_->GetModelPathFromTitle("xyztitle"));
+
+ download_path = base::FilePath::FromASCII("/var/DownloadFolder");
+ handler_->set_downloads_folder(download_path);
+ EXPECT_EQ(
+ download_path.AppendASCII("overview_tracing_secret_app_11648793600.json"),
+ handler_->GetModelPathFromTitle("Secret App"));
+}
+
+TEST_F(ArcGraphicsTracingHandlerTest, FilterSystemTraceByTimestamp) {
+ handler_->set_now(base::Time::FromJavaTime(1'500'088'880'000));
+ handler_->set_trace_time_base(base::Time::FromJavaTime(1'500'000'000'000));
+
+ auto arc_widget = arc::ArcTaskWindowBuilder().BuildOwnsNativeWidget();
+ arc_widget->Show();
+ SendStartStopKey();
+ handler_->StartTracingOnControllerRespond();
+
+ // Fast forward past the max tracing interval.
+ FastForwardClockAndTaskQueue(handler_->max_tracing_time() +
+ base::Milliseconds(500));
+
+ // Pass results from trace controller to handler. First and last events should
+ // not be in the model.
+ handler_->StopTracingOnControllerRespond(std::make_unique<std::string>(
+ R"(
+{
+ "traceEvents": [],
+ "systemTraceEvents":
+" <idle>-0 [000] d..0 88879.800000: sched_wakeup: comm=foo pid=99 prio=115 target_cpu=000
+ <idle>-0 [000] d..0 88882.000001: cpu_idle: state=0 cpu_id=0
+ <idle>-0 [000] dn.0 88883.000002: cpu_idle: state=4294967295 cpu_id=0
+ <idle>-0 [000] dnh3 88884.000003: sched_wakeup: comm=foo pid=25821 prio=115 target_cpu=000
+ <idle>-0 [000] d..3 88884.500004: sched_switch: prev_comm=bar prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=baz next_pid=25891 next_prio=115
+ <idle>-0 [000] d..3 88885.500004: sched_switch: prev_comm=baz prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=foo next_pid=33921 next_prio=115
+"
+})"));
+
+ {
+ const auto& set_status = web_ui_->call_data().back();
+ ASSERT_EQ("cr.ArcOverviewTracing.setStatus", set_status->function_name());
+ ASSERT_EQ("Building model...", set_status->arg1()->GetString());
+ }
+ web_ui_->ClearTrackedCalls();
+
+ task_environment()->RunUntilIdle();
+
+ {
+ const auto& set_model = web_ui_->call_data().back();
+ ASSERT_EQ("cr.ArcOverviewTracing.setModel", set_model->function_name());
+ const auto& dict = set_model->arg1()->GetDict();
+ const auto* events_by_cpu = dict.FindListByDottedPath("system.cpu");
+ ASSERT_TRUE(events_by_cpu);
+ // Only one CPU in log.
+ ASSERT_EQ(1UL, events_by_cpu->size());
+
+ const auto& cpu_events = (*events_by_cpu)[0].GetList();
+ ASSERT_EQ(4UL, cpu_events.size()) << cpu_events;
+
+ EXPECT_EQ(25821.0, cpu_events[2].GetList()[2].GetDouble());
+ EXPECT_EQ(25891.0, cpu_events[3].GetList()[2].GetDouble());
+ }
+}
+
+TEST_F(ArcGraphicsTracingHandlerTest, SwitchWindowDuringModelBuild) {
+ handler_->set_now(base::Time::FromJavaTime(1'600'044'440'000));
+ handler_->set_trace_time_base(base::Time::FromJavaTime(1'600'000'000'000));
+
+ exo::Surface s;
+ auto arc_widget = arc::ArcTaskWindowBuilder()
+ .SetTaskId(22)
+ .SetPackageName("org.funstuff.client")
+ .SetShellRootSurface(&s)
+ .BuildOwnsNativeWidget();
+
+ auto other_arc_widget = arc::ArcTaskWindowBuilder()
+ .SetTaskId(88)
+ .SetPackageName("net.differentapp")
+ .SetShellRootSurface(&s)
+ .BuildOwnsNativeWidget();
+
+ arc_widget->Show();
+ other_arc_widget->ShowInactive();
+ SendStartStopKey();
+ handler_->StartTracingOnControllerRespond();
+
+ // Fast forward past the max tracing interval. This will stop the trace at the
+ // end of the fast-forward, which is 400ms after the timeout.
+ FastForwardClockAndTaskQueue(handler_->max_tracing_time() +
+ base::Milliseconds(400));
+
+ // While model is being built, switch to the ARC window to change
+ // min_tracing_time_. This sets the min trace time to 300ms after the end of
+ // the trace.
+ FastForwardClockAndTaskQueue(base::Milliseconds(300));
+ other_arc_widget->Activate();
+
+ // Pass results from trace controller to handler.
+ handler_->StopTracingOnControllerRespond(std::make_unique<std::string>(
+ "{\"traceEvents\":[],\"systemTraceEvents\":\""
+ // clang-format off
+ " <idle>-0 [003] d..0 44442.000001: cpu_idle: state=0 cpu_id=3\n"
+ // clang-format on
+ "\"}"));
+
+ task_environment()->RunUntilIdle();
+
+ {
+ const auto& dict = web_ui_->call_data().back()->arg1()->GetDict();
+ const auto* events_by_cpu = dict.FindListByDottedPath("system.cpu");
+ ASSERT_TRUE(events_by_cpu);
+ ASSERT_EQ(4UL, events_by_cpu->size());
+
+ const auto& cpu_events = (*events_by_cpu)[3].GetList();
+ ASSERT_EQ(1UL, cpu_events.size()) << cpu_events;
+ }
}
} // namespace
diff --git a/chromium/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc b/chromium/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc
index 760fedefa93..c25a81548d3 100644
--- a/chromium/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc
@@ -10,6 +10,7 @@
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/shelf_config.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/functional/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
@@ -22,6 +23,7 @@
#include "chrome/browser/ui/views/chrome_web_dialog_view.h"
#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
+#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/assistant_optin_resources.h"
#include "chrome/grit/assistant_optin_resources_map.h"
@@ -73,6 +75,7 @@ AssistantOptInUI::AssistantOptInUI(content::WebUI* web_ui)
// Set up the chrome://assistant-optin source.
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
Profile::FromWebUI(web_ui), chrome::kChromeUIAssistantOptInHost);
+ ash::EnableTrustedTypesCSP(source);
auto assistant_handler = std::make_unique<AssistantOptInFlowScreenHandler>();
assistant_handler_ptr_ = assistant_handler.get();
@@ -92,7 +95,6 @@ AssistantOptInUI::AssistantOptInUI(content::WebUI* web_ui)
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::WorkerSrc,
"worker-src blob: chrome://resources 'self';");
- source->DisableTrustedTypesCSP();
// Do not zoom for Assistant opt-in web contents.
content::HostZoomMap* zoom_map =
diff --git a/chromium/chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.cc b/chromium/chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.cc
index 9d2911fb7a8..c4278a3e9f6 100644
--- a/chromium/chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.cc
@@ -7,6 +7,7 @@
#include <memory>
#include "ash/public/cpp/bluetooth_config_service.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/check.h"
#include "base/json/json_writer.h"
#include "base/memory/ptr_util.h"
@@ -141,7 +142,10 @@ BluetoothPairingDialogUI::BluetoothPairingDialogUI(content::WebUI* web_ui)
base::make_span(kBluetoothPairingDialogResources,
kBluetoothPairingDialogResourcesSize),
IDR_BLUETOOTH_PAIRING_DIALOG_BLUETOOTH_PAIRING_DIALOG_CONTAINER_HTML);
- source->DisableTrustedTypesCSP();
+ // Enabling trusted types via trusted_types_util must be done after
+ // webui::SetupWebUIDataSource to override the trusted type CSP with correct
+ // policies for JS WebUIs.
+ ash::EnableTrustedTypesCSP(source);
device::RecordUiSurfaceDisplayed(
device::BluetoothUiSurface::kStandalonePairingDialog);
diff --git a/chromium/chrome/browser/ui/webui/ash/borealis_installer/BUILD.gn b/chromium/chrome/browser/ui/webui/ash/borealis_installer/BUILD.gn
new file mode 100644
index 00000000000..be193971b8f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/borealis_installer/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(is_chromeos_ash)
+
+mojom("mojo_bindings") {
+ sources = [ "borealis_installer.mojom" ]
+ webui_module_path = "/"
+ public_deps = [ "//mojo/public/mojom/base" ]
+ use_typescript_sources = true
+}
diff --git a/chromium/chrome/browser/ui/webui/ash/borealis_installer/OWNERS b/chromium/chrome/browser/ui/webui/ash/borealis_installer/OWNERS
new file mode 100644
index 00000000000..0973eac4323
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/borealis_installer/OWNERS
@@ -0,0 +1,5 @@
+lqu@google.com
+file://chrome/browser/ash/borealis/OWNERS
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS \ No newline at end of file
diff --git a/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer.mojom b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer.mojom
new file mode 100644
index 00000000000..5cb16a3364c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer.mojom
@@ -0,0 +1,40 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ash.borealis_installer.mojom;
+
+// Lives in the browser process. A renderer uses this to create a page handler
+// for controlling Borealis installation.
+interface PageHandlerFactory {
+ // Creates a page handler to enable communication with the browser process.
+ CreatePageHandler(pending_remote<Page> page,
+ pending_receiver<PageHandler> handler);
+};
+
+// Lives in the renderer process. The browser uses this to send installation.
+interface PageHandler {
+ // Start installation.
+ Install();
+ // Shut down the VM.
+ ShutDown();
+ // Launch Borealis.
+ Launch();
+ // This is called when the web page is "closed", and the dialog (or whatever)
+ // hosting it should also be closed. This can happen as a result of
+ // Page::RequestClose() being called, or it can happen spontaneously (e.g.
+ // user clicking cancel on the page or installation finished).
+ OnPageClosed();
+};
+
+// Lives in the renderer process. The browser uses this to sends installation
+// updates to the web page in the render.
+interface Page {
+ // Called to update the install percentage.
+ OnProgressUpdate(double progress_fraction, string label);
+ // Called when installation finishes.
+ OnInstallFinished();
+ // Restarts the installation if an error occurs. Triggered from an error
+ // dialog.
+ RestartInstallation();
+};
diff --git a/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.cc b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.cc
new file mode 100644
index 00000000000..3a276e8840d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.cc
@@ -0,0 +1,142 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "chrome/browser/ash/borealis/borealis_app_launcher.h"
+#include "chrome/browser/ash/borealis/borealis_context_manager.h"
+#include "chrome/browser/ash/borealis/borealis_features.h"
+#include "chrome/browser/ash/borealis/borealis_installer.h"
+#include "chrome/browser/ash/borealis/borealis_metrics.h"
+#include "chrome/browser/ash/borealis/borealis_service.h"
+#include "chrome/browser/ash/borealis/borealis_util.h"
+#include "chrome/browser/ui/views/borealis/borealis_installer_error_dialog.h"
+#include "chrome/browser/ui/views/borealis/borealis_splash_screen_view.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/time_format.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+namespace {
+// Returns the text to be used for a predicted completion time, which is
+// something like "starting up" or "3 mins remaining" depending on how
+// accurately we can predict.
+std::string GetInstallationPredictionText(const base::Time& start_time,
+ double completion_proportion) {
+ base::TimeDelta duration = base::Time::Now() - start_time;
+ // We have no confidence in the prediction for the first second or for
+ // too-small proportions.
+ if (completion_proportion < 0.001 || duration < base::Seconds(1)) {
+ return l10n_util::GetStringUTF8(IDS_BOREALIS_INSTALLER_ONGOING_INITIAL);
+ }
+ // Linear-interpolation to predict remaining time.
+ base::TimeDelta remaining = (duration / completion_proportion) - duration;
+ return base::UTF16ToUTF8(
+ ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
+ ui::TimeFormat::LENGTH_SHORT, remaining));
+}
+
+} // namespace
+namespace ash {
+
+BorealisInstallerPageHandler::BorealisInstallerPageHandler(
+ mojo::PendingReceiver<ash::borealis_installer::mojom::PageHandler>
+ pending_page_handler,
+ mojo::PendingRemote<ash::borealis_installer::mojom::Page> pending_page,
+ base::OnceClosure on_page_closed,
+ content::WebUI* web_ui)
+ : receiver_{this, std::move(pending_page_handler)},
+ page_{std::move(pending_page)},
+ on_page_closed_{std::move(on_page_closed)},
+ native_window_(web_ui->GetWebContents()->GetTopLevelNativeWindow()),
+ profile_(Profile::FromWebUI(web_ui)),
+ observation_(this) {}
+
+BorealisInstallerPageHandler::~BorealisInstallerPageHandler() = default;
+
+void BorealisInstallerPageHandler::Install() {
+ fraction_complete_ = 0;
+ install_start_time_ = base::Time::Now();
+ borealis::BorealisInstaller& installer =
+ borealis::BorealisService::GetForProfile(profile_)->Installer();
+ if (observation_.IsObserving()) {
+ observation_.Reset();
+ }
+ observation_.Observe(&installer);
+ installer.Start();
+}
+
+void BorealisInstallerPageHandler::ShutDown() {
+ borealis::BorealisService::GetForProfile(profile_)
+ ->ContextManager()
+ .ShutDownBorealis(
+ base::BindOnce([](borealis::BorealisShutdownResult result) {
+ if (result == borealis::BorealisShutdownResult::kSuccess) {
+ return;
+ }
+ LOG(ERROR) << "Failed to shutdown borealis after install: code="
+ << static_cast<int>(result);
+ }));
+}
+
+void BorealisInstallerPageHandler::Launch() {
+ // Installation starts the borealis VM, so the splash screen would normally
+ // not show, therefore we need to show it manually here. Since this is
+ // post-install we know borealis wasn't running previously.
+ borealis::ShowBorealisSplashScreenView(profile_);
+ // Launch button has been clicked.
+ borealis::BorealisService::GetForProfile(profile_)->AppLauncher().Launch(
+ borealis::kClientAppId,
+ base::BindOnce([](borealis::BorealisAppLauncher::LaunchResult result) {
+ if (result == borealis::BorealisAppLauncher::LaunchResult::kSuccess) {
+ return;
+ }
+ LOG(ERROR) << "Failed to launch borealis after install: code="
+ << static_cast<int>(result);
+ }));
+}
+
+void BorealisInstallerPageHandler::OnPageClosed() {
+ if (on_page_closed_) {
+ std::move(on_page_closed_).Run();
+ }
+}
+
+void BorealisInstallerPageHandler::OnProgressUpdated(double fraction_complete) {
+ page_->OnProgressUpdate(
+ fraction_complete,
+ GetInstallationPredictionText(install_start_time_, fraction_complete));
+}
+
+void BorealisInstallerPageHandler::OnInstallationEnded(
+ borealis::BorealisInstallResult result,
+ const std::string& error_description) {
+ if (result == borealis::BorealisInstallResult::kSuccess) {
+ page_->OnInstallFinished();
+ } else if (result != borealis::BorealisInstallResult::kCancelled) {
+ views::borealis::ShowInstallerErrorDialog(
+ native_window_, result,
+ base::BindOnce(&BorealisInstallerPageHandler::OnErrorDialogDismissed,
+ weak_factory_.GetWeakPtr()));
+ LOG(ERROR) << "Borealis Installation Error: " << error_description;
+ }
+}
+
+void BorealisInstallerPageHandler::OnErrorDialogDismissed(
+ views::borealis::ErrorDialogChoice choice) {
+ switch (choice) {
+ case views::borealis::ErrorDialogChoice::kRetry:
+ page_->RestartInstallation();
+ return;
+ case views::borealis::ErrorDialogChoice::kExit:
+ OnPageClosed();
+ return;
+ }
+}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.h b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.h
new file mode 100644
index 00000000000..27d6a0810a3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.h
@@ -0,0 +1,64 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_BOREALIS_INSTALLER_BOREALIS_INSTALLER_PAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_BOREALIS_INSTALLER_BOREALIS_INSTALLER_PAGE_HANDLER_H_
+
+#include "base/scoped_observation.h"
+#include "chrome/browser/ash/borealis/borealis_installer.h"
+#include "chrome/browser/ash/borealis/borealis_metrics.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/views/borealis/borealis_installer_error_dialog.h"
+#include "chrome/browser/ui/webui/ash/borealis_installer/borealis_installer.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace ash {
+
+class BorealisInstallerPageHandler
+ : public ash::borealis_installer::mojom::PageHandler,
+ public borealis::BorealisInstaller::Observer {
+ public:
+ BorealisInstallerPageHandler(
+ mojo::PendingReceiver<ash::borealis_installer::mojom::PageHandler>
+ pending_page_handler,
+ mojo::PendingRemote<ash::borealis_installer::mojom::Page> pending_page,
+ base::OnceClosure on_page_closed,
+ content::WebUI* web_ui);
+ ~BorealisInstallerPageHandler() override;
+
+ void Install() override;
+ void ShutDown() override;
+ void Launch() override;
+ void OnPageClosed() override;
+
+ // borealis::BorealisInstaller::Observer implementation.
+ void OnStateUpdated(
+ borealis::BorealisInstaller::InstallingState new_state) override {}
+ void OnProgressUpdated(double fraction_complete) override;
+ void OnInstallationEnded(borealis::BorealisInstallResult result,
+ const std::string& error_description) override;
+ void OnCancelInitiated() override {}
+
+ private:
+ void OnErrorDialogDismissed(views::borealis::ErrorDialogChoice choice);
+
+ mojo::Receiver<ash::borealis_installer::mojom::PageHandler> receiver_;
+ mojo::Remote<ash::borealis_installer::mojom::Page> page_;
+ base::OnceClosure on_page_closed_;
+ gfx::NativeWindow native_window_;
+ raw_ptr<Profile, ExperimentalAsh> profile_;
+ base::Time install_start_time_;
+ base::ScopedObservation<borealis::BorealisInstaller,
+ borealis::BorealisInstaller::Observer>
+ observation_;
+ double fraction_complete_;
+ base::WeakPtrFactory<BorealisInstallerPageHandler> weak_factory_{this};
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_BOREALIS_INSTALLER_BOREALIS_INSTALLER_PAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.cc b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.cc
new file mode 100644
index 00000000000..a3764b78ef1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.cc
@@ -0,0 +1,99 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.h"
+
+#include "ash/constants/ash_features.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/borealis_installer_resources.h"
+#include "chrome/grit/borealis_installer_resources_map.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+namespace ash {
+
+bool BorealisInstallerUIConfig::IsWebUIEnabled(
+ content::BrowserContext* browser_context) {
+ return base::FeatureList::IsEnabled(features::kBorealisWebUIInstaller);
+}
+
+BorealisInstallerUI::BorealisInstallerUI(content::WebUI* web_ui)
+ : ui::MojoWebDialogUI{web_ui}, web_ui_(web_ui) {
+ // Set up the chrome://borealis-installer source.
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::CreateAndAdd(
+ web_ui->GetWebContents()->GetBrowserContext(),
+ chrome::kChromeUIBorealisInstallerHost);
+ static constexpr webui::LocalizedString kStrings[] = {
+ {"cancel", IDS_CANCEL},
+ {"install", IDS_INSTALL},
+ {"confirmationTitle", IDS_BOREALIS_INSTALLER_CONFIRMATION_TITLE},
+ {"confirmationMessage", IDS_BOREALIS_INSTALLER_CONFIRMATION_MESSAGE},
+ {"ongoingTitle", IDS_BOREALIS_INSTALLER_ONGOING_TITLE},
+ {"ongingMessage", IDS_BOREALIS_INSTALLER_ONGOING_MESSAGE},
+ {"percent", IDS_BOREALIS_INSTALLER_ONGOING_PERCENTAGE},
+ {"finishedTitle", IDS_BOREALIS_INSTALLER_FINISHED_TITLE},
+ {"finishedMessage", IDS_BOREALIS_INSTALLER_FINISHED_MESSAGE},
+ {"launch", IDS_BOREALIS_INSTALLER_LAUNCH_BUTTON},
+ };
+ html_source->AddLocalizedStrings(kStrings);
+
+ webui::SetupWebUIDataSource(html_source,
+ base::make_span(kBorealisInstallerResources,
+ kBorealisInstallerResourcesSize),
+ IDR_BOREALIS_INSTALLER_BOREALIS_INSTALLER_HTML);
+}
+
+BorealisInstallerUI::~BorealisInstallerUI() = default;
+
+void BorealisInstallerUI::BindPageHandlerFactory(
+ mojo::PendingReceiver<ash::borealis_installer::mojom::PageHandlerFactory>
+ pending_receiver) {
+ if (page_factory_receiver_.is_bound()) {
+ page_factory_receiver_.reset();
+ }
+
+ page_factory_receiver_.Bind(std::move(pending_receiver));
+}
+
+void BorealisInstallerUI::BindInterface(
+ mojo::PendingReceiver<borealis_installer::mojom::PageHandlerFactory>
+ pending_receiver) {
+ if (page_factory_receiver_.is_bound()) {
+ page_factory_receiver_.reset();
+ }
+
+ page_factory_receiver_.Bind(std::move(pending_receiver));
+}
+
+void BorealisInstallerUI::CreatePageHandler(
+ mojo::PendingRemote<ash::borealis_installer::mojom::Page> pending_page,
+ mojo::PendingReceiver<ash::borealis_installer::mojom::PageHandler>
+ pending_page_handler) {
+ DCHECK(pending_page.is_valid());
+
+ page_handler_ = std::make_unique<BorealisInstallerPageHandler>(
+ std::move(pending_page_handler), std::move(pending_page),
+ base::BindOnce(&BorealisInstallerUI::OnPageClosed,
+ base::Unretained(this)),
+ web_ui_);
+}
+
+void BorealisInstallerUI::OnPageClosed() {
+ page_closed_ = true;
+ // CloseDialog() is a no-op if we are not in a dialog (e.g. user
+ // access the page using the URL directly, which is not supported).
+ ui::MojoWebDialogUI::CloseDialog(base::Value::List());
+}
+
+WEB_UI_CONTROLLER_TYPE_IMPL(BorealisInstallerUI);
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.h b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.h
new file mode 100644
index 00000000000..72c8c307f7a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.h
@@ -0,0 +1,70 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_BOREALIS_INSTALLER_BOREALIS_INSTALLER_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_BOREALIS_INSTALLER_BOREALIS_INSTALLER_UI_H_
+
+#include "chrome/browser/ui/webui/ash/borealis_installer/borealis_installer.mojom.h"
+#include "chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_page_handler.h"
+#include "chrome/common/webui_url_constants.h"
+#include "content/public/browser/webui_config.h"
+#include "content/public/common/url_constants.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+namespace ash {
+
+class BorealisInstallerUI;
+
+// WebUIConfig for chrome://borealis-installer
+class BorealisInstallerUIConfig
+ : public content::DefaultWebUIConfig<BorealisInstallerUI> {
+ public:
+ BorealisInstallerUIConfig()
+ : DefaultWebUIConfig(content::kChromeUIScheme,
+ chrome::kChromeUIBorealisInstallerHost) {}
+
+ bool IsWebUIEnabled(content::BrowserContext* browser_context) override;
+};
+
+// The WebUI for chrome://borealis-installer
+class BorealisInstallerUI
+ : public ui::MojoWebDialogUI,
+ public ash::borealis_installer::mojom::PageHandlerFactory {
+ public:
+ explicit BorealisInstallerUI(content::WebUI* web_ui);
+ ~BorealisInstallerUI() override;
+
+ // Instantiates implementor of the mojom::PageHandlerFactory
+ // mojo interface passing the pending receiver that will be internally bound.
+ void BindInterface(
+ mojo::PendingReceiver<borealis_installer::mojom::PageHandlerFactory>
+ pending_receiver);
+
+ private:
+ void BindPageHandlerFactory(
+ mojo::PendingReceiver<ash::borealis_installer::mojom::PageHandlerFactory>
+ pending_receiver);
+
+ // ash::borealis_installer::mojom::PageHandlerFactory:
+ void CreatePageHandler(
+ mojo::PendingRemote<ash::borealis_installer::mojom::Page> pending_page,
+ mojo::PendingReceiver<ash::borealis_installer::mojom::PageHandler>
+ pending_page_handler) override;
+ void OnPageClosed();
+
+ std::unique_ptr<BorealisInstallerPageHandler> page_handler_;
+ mojo::Receiver<ash::borealis_installer::mojom::PageHandlerFactory>
+ page_factory_receiver_{this};
+ bool page_closed_;
+ raw_ptr<content::WebUI, ExperimentalAsh> web_ui_;
+
+ WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_BOREALIS_INSTALLER_BOREALIS_INSTALLER_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc
index d0da0bd1a7a..852e12a999f 100644
--- a/chromium/chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.cc
@@ -60,8 +60,11 @@ constexpr webui::LocalizedString kLocalizedStringsWithoutPlaceholders[] = {
{"eSimProfileDetectDuringActiveCellularConnectionMessage",
IDS_CELLULAR_SETUP_ESIM_PROFILE_DETECT_DURING_ACTIVE_CELLULAR_CONNECTION_MESSAGE},
{"scanQRCode", IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE},
- {"scanQRCodeEnterActivationCode",
- IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_ENTER_ACTIVATION_CODE},
+ {"scanQRCodeNoProfilesFound",
+ IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_NO_PROFILES_FOUND},
+ {"enterActivationCode", IDS_CELLULAR_SETUP_ESIM_PAGE_ENTER_ACTIVATION_CODE},
+ {"enterActivationCodeNoProfilesFound",
+ IDS_CELLULAR_SETUP_ESIM_PAGE_ENTER_ACTIVATION_CODE_NO_PROFILES_FOUND},
{"switchCamera", IDS_CELLULAR_SETUP_ESIM_PAGE_SWITCH_CAMERA},
{"qrCodeA11YCameraOn", IDS_CELLULAR_SETUP_ESIM_PAGE_A11Y_QR_CODE_CAMERA_ON},
{"qrCodeA11YCameraScanSuccess",
@@ -88,7 +91,15 @@ constexpr webui::LocalizedString kLocalizedStringsWithoutPlaceholders[] = {
{"confirmationCodeLoading",
IDS_CELLULAR_SETUP_ESIM_PAGE_CONFIRMATION_CODE_LOADING},
{"verifyingActivationCode",
- IDS_CELLULAR_SETUP_ESIM_PAGE_VERIFYING_ACTIVATION_CODE}};
+ IDS_CELLULAR_SETUP_ESIM_PAGE_VERIFYING_ACTIVATION_CODE},
+ {"profileDiscoveryConsentTitle",
+ IDS_CELLULAR_SETUP_ESIM_PAGE_PROFILE_DISCOVERY_CONSENT_TITLE},
+ {"profileDiscoveryConsentMessageWithLink",
+ IDS_CELLULAR_SETUP_ESIM_PAGE_PROFILE_DISCOVERY_CONSENT_MESSAGE_WITH_LINK},
+ {"profileDiscoveryConsentScan",
+ IDS_CELLULAR_SETUP_ESIM_PAGE_PROFILE_DISCOVERY_CONSENT_SCAN},
+ {"profileDiscoveryConsentCancel",
+ IDS_CELLULAR_SETUP_ESIM_PAGE_PROFILE_DISCOVERY_CONSENT_CANCEL}};
struct NamedBoolean {
const char* name;
@@ -103,7 +114,9 @@ struct NamedResourceId {
const std::vector<const NamedBoolean>& GetBooleanValues() {
static const base::NoDestructor<std::vector<const NamedBoolean>> named_bools(
{{"useSecondEuicc",
- base::FeatureList::IsEnabled(features::kCellularUseSecondEuicc)}});
+ base::FeatureList::IsEnabled(features::kCellularUseSecondEuicc)},
+ {"isSmdsSupportEnabled",
+ ash::features::IsSmdsSupportEnabled()}});
return *named_bools;
}
diff --git a/chromium/chrome/browser/ui/webui/ash/certificate_manager_dialog_ui.cc b/chromium/chrome/browser/ui/webui/ash/certificate_manager_dialog_ui.cc
index cc175a8d2c5..6e1a2b8c775 100644
--- a/chromium/chrome/browser/ui/webui/ash/certificate_manager_dialog_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/certificate_manager_dialog_ui.cc
@@ -52,7 +52,7 @@ CertificateManagerDialogUI::CertificateManagerDialogUI(content::WebUI* web_ui)
source->AddBoolean(
"isGuest",
user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
- user_manager::UserManager::Get()->IsLoggedInAsPublicAccount());
+ user_manager::UserManager::Get()->IsLoggedInAsManagedGuestSession());
source->AddBoolean(
"isKiosk", user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp());
diff --git a/chromium/chrome/browser/ui/webui/ash/chrome_untrusted_web_ui_configs_chromeos.cc b/chromium/chrome/browser/ui/webui/ash/chrome_untrusted_web_ui_configs_chromeos.cc
index bb0e8ca65fa..34c9b3f0ac6 100644
--- a/chromium/chrome/browser/ui/webui/ash/chrome_untrusted_web_ui_configs_chromeos.cc
+++ b/chromium/chrome/browser/ui/webui/ash/chrome_untrusted_web_ui_configs_chromeos.cc
@@ -17,13 +17,16 @@
#include "ash/webui/help_app_ui/help_app_kids_magazine_untrusted_ui.h"
#include "ash/webui/os_feedback_ui/os_feedback_untrusted_ui.h"
#include "chrome/browser/ash/login/demo_mode/demo_session.h"
-#include "chrome/browser/ash/web_applications/camera_app/camera_app_untrusted_ui_config.h"
-#include "chrome/browser/ash/web_applications/crosh_ui.h"
-#include "chrome/browser/ash/web_applications/help_app/help_app_untrusted_ui_config.h"
-#include "chrome/browser/ash/web_applications/media_app/media_app_guest_ui_config.h"
-#include "chrome/browser/ash/web_applications/projector_app/untrusted_projector_annotator_ui_config.h"
-#include "chrome/browser/ash/web_applications/projector_app/untrusted_projector_ui_config.h"
-#include "chrome/browser/ash/web_applications/terminal_ui.h"
+#include "chrome/browser/ash/system_web_apps/apps/camera_app/camera_app_untrusted_ui_config.h"
+#include "chrome/browser/ash/system_web_apps/apps/chrome_demo_mode_app_delegate.h"
+#include "chrome/browser/ash/system_web_apps/apps/crosh_ui.h"
+#include "chrome/browser/ash/system_web_apps/apps/help_app/help_app_untrusted_ui_config.h"
+#include "chrome/browser/ash/system_web_apps/apps/media_app/media_app_guest_ui_config.h"
+#include "chrome/browser/ash/system_web_apps/apps/projector_app/untrusted_projector_annotator_ui_config.h"
+#include "chrome/browser/ash/system_web_apps/apps/projector_app/untrusted_projector_ui_config.h"
+#include "chrome/browser/ash/system_web_apps/apps/terminal_ui.h"
+#include "chrome/browser/ui/webui/ash/mako/mako_ui.h"
+#include "chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.h"
#if !defined(OFFICIAL_BUILD)
#include "ash/webui/sample_system_web_app_ui/sample_system_web_app_untrusted_ui.h"
@@ -36,7 +39,8 @@ std::unique_ptr<content::WebUIConfig> MakeDemoModeAppUntrustedUIConfig() {
[](content::WebUI* web_ui,
const GURL& url) -> std::unique_ptr<content::WebUIController> {
return std::make_unique<DemoModeAppUntrustedUI>(
- web_ui, DemoSession::Get()->GetDemoAppComponentPath());
+ web_ui, DemoSession::Get()->GetDemoAppComponentPath(),
+ std::make_unique<ChromeDemoModeAppDelegate>(web_ui));
});
return std::make_unique<DemoModeAppUntrustedUIConfig>(create_controller_func);
}
@@ -66,6 +70,8 @@ void RegisterAshChromeUntrustedWebUIConfigs() {
std::make_unique<feedback::OsFeedbackUntrustedUIConfig>());
map.AddUntrustedWebUIConfig(std::make_unique<FaceMLAppUntrustedUIConfig>());
map.AddUntrustedWebUIConfig(MakeDemoModeAppUntrustedUIConfig());
+ map.AddUntrustedWebUIConfig(std::make_unique<MakoUntrustedUIConfig>());
+ map.AddUntrustedWebUIConfig(std::make_unique<ScalableIphDebugUIConfig>());
#if !defined(OFFICIAL_BUILD)
map.AddUntrustedWebUIConfig(
std::make_unique<SampleSystemWebAppUntrustedUIConfig>());
diff --git a/chromium/chrome/browser/ui/webui/ash/chrome_web_ui_configs_chromeos.cc b/chromium/chrome/browser/ui/webui/ash/chrome_web_ui_configs_chromeos.cc
index cff0f4046f3..0aa7daa3125 100644
--- a/chromium/chrome/browser/ui/webui/ash/chrome_web_ui_configs_chromeos.cc
+++ b/chromium/chrome/browser/ui/webui/ash/chrome_web_ui_configs_chromeos.cc
@@ -45,13 +45,13 @@
#include "chrome/browser/ash/printing/print_management/printing_manager_factory.h"
#include "chrome/browser/ash/scanning/chrome_scanning_app_delegate.h"
#include "chrome/browser/ash/shimless_rma/chrome_shimless_rma_delegate.h"
-#include "chrome/browser/ash/web_applications/camera_app/chrome_camera_app_ui_delegate.h"
-#include "chrome/browser/ash/web_applications/chrome_file_manager_ui_delegate.h"
-#include "chrome/browser/ash/web_applications/face_ml/chrome_face_ml_user_provider.h"
-#include "chrome/browser/ash/web_applications/files_internals_ui_delegate.h"
-#include "chrome/browser/ash/web_applications/help_app/help_app_ui_delegate.h"
-#include "chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.h"
-#include "chrome/browser/ash/web_applications/personalization_app/personalization_app_utils.h"
+#include "chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.h"
+#include "chrome/browser/ash/system_web_apps/apps/chrome_file_manager_ui_delegate.h"
+#include "chrome/browser/ash/system_web_apps/apps/face_ml/chrome_face_ml_user_provider.h"
+#include "chrome/browser/ash/system_web_apps/apps/files_internals_ui_delegate.h"
+#include "chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.h"
+#include "chrome/browser/ash/system_web_apps/apps/media_app/chrome_media_app_ui_delegate.h"
+#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_utils.h"
#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h"
#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
@@ -63,6 +63,7 @@
#include "chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.h"
#include "chrome/browser/ui/webui/ash/audio/audio_ui.h"
#include "chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.h"
+#include "chrome/browser/ui/webui/ash/borealis_installer/borealis_installer_ui.h"
#include "chrome/browser/ui/webui/ash/certificate_manager_dialog_ui.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.h"
#include "chrome/browser/ui/webui/ash/crostini_installer/crostini_installer_ui.h"
@@ -90,11 +91,13 @@
#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h"
#include "chrome/browser/ui/webui/ash/power_ui.h"
#include "chrome/browser/ui/webui/ash/remote_maintenance_curtain_ui.h"
+#include "chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.h"
#include "chrome/browser/ui/webui/ash/set_time_ui.h"
#include "chrome/browser/ui/webui/ash/slow_trace_ui.h"
#include "chrome/browser/ui/webui/ash/slow_ui.h"
#include "chrome/browser/ui/webui/ash/smb_shares/smb_credentials_dialog.h"
#include "chrome/browser/ui/webui/ash/smb_shares/smb_share_dialog.h"
+#include "chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.h"
#include "chrome/browser/ui/webui/ash/sys_internals/sys_internals_ui.h"
#include "chrome/browser/ui/webui/ash/vc_tray_tester/vc_tray_tester_ui.h"
#include "chrome/browser/ui/webui/ash/vm/vm_ui.h"
@@ -225,6 +228,7 @@ void RegisterAshChromeWebUIConfigs() {
map.AddWebUIConfig(std::make_unique<AssistantOptInUIConfig>());
map.AddWebUIConfig(std::make_unique<AudioUIConfig>());
map.AddWebUIConfig(std::make_unique<BluetoothPairingDialogUIConfig>());
+ map.AddWebUIConfig(std::make_unique<BorealisInstallerUIConfig>());
map.AddWebUIConfig(std::make_unique<CertificateManagerDialogUIConfig>());
map.AddWebUIConfig(std::make_unique<cloud_upload::CloudUploadUIConfig>());
map.AddWebUIConfig(std::make_unique<ColorInternalsUIConfig>());
@@ -236,6 +240,7 @@ void RegisterAshChromeWebUIConfigs() {
map.AddWebUIConfig(MakeDiagnosticsUIConfig());
map.AddWebUIConfig(std::make_unique<DriveInternalsUIConfig>());
map.AddWebUIConfig(MakeEcheAppUIConfig());
+ map.AddWebUIConfig(std::make_unique<SensorInfoUIConfig>());
map.AddWebUIConfig(std::make_unique<EmojiUIConfig>());
map.AddWebUIConfig(
MakeComponentConfigWithDelegate<FaceMLAppUIConfig, FaceMLAppUI,
@@ -316,6 +321,7 @@ void RegisterAshChromeWebUIConfigs() {
map.AddWebUIConfig(std::make_unique<VmUIConfig>());
#if !defined(OFFICIAL_BUILD)
map.AddWebUIConfig(std::make_unique<SampleSystemWebAppUIConfig>());
+ map.AddWebUIConfig(std::make_unique<StatusAreaInternalsUIConfig>());
#if !defined(USE_REAL_DBUS_CLIENTS)
map.AddWebUIConfig(std::make_unique<DeviceEmulatorUIConfig>());
#endif // !defined(USE_REAL_DBUS_CLIENTS)
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom
index 4c87b871d62..2c64207ebe1 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom
@@ -7,6 +7,8 @@ module ash.cloud_upload.mojom;
// The selected action when the user closes the dialog.
enum UserAction {
kCancel,
+ kCancelGoogleDrive,
+ kCancelOneDrive,
kSetUpOneDrive,
kUploadToGoogleDrive,
kUploadToOneDrive,
@@ -40,7 +42,7 @@ enum MetricsRecordedSetupPage {
kOneDriveSetupWelcome = 3,
kOneDriveSetupPWAInstall = 4,
kOneDriveSetupODFSMount = 5,
- kOneDriveSetupUpload = 6,
+ kOneDriveSetupComplete = 6,
};
// Operation used to upload the source file.
@@ -72,9 +74,9 @@ struct DialogArgs {
// the `kFileHandlerDialog` dialog.
array<DialogTask> local_tasks;
- // True if the setup flow is running for the first time. False if running
- // fixup flow.
- bool first_time_setup;
+ // True if the setup flow should end with setting Microsoft 365 as default
+ // handler.
+ bool set_office_as_default_handler;
// Indicates what operation is used to upload the source file (copy or move).
OperationType operation_type;
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc
index 695e2b504fc..a31e3985c80 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc
@@ -4,11 +4,14 @@
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
+#include "ash/public/cpp/new_window_delegate.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "base/containers/enum_set.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
+#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
@@ -29,7 +32,6 @@
#include "chrome/browser/ash/file_system_provider/mount_path_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/notifications/notification_display_service.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
@@ -41,14 +43,15 @@
#include "chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h"
#include "chrome/browser/web_applications/web_app_id_constants.h"
+#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
-#include "chromeos/constants/chromeos_features.h"
-#include "components/services/app_service/public/cpp/types_util.h"
#include "components/user_manager/user_manager.h"
#include "extensions/browser/api/file_handlers/mime_util.h"
#include "extensions/browser/entry_info.h"
#include "extensions/common/constants.h"
+#include "net/base/url_util.h"
#include "storage/browser/file_system/file_system_url.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
@@ -60,13 +63,13 @@
namespace ash::cloud_upload {
namespace {
+namespace fm_tasks = file_manager::file_tasks;
+
using ash::file_system_provider::ProvidedFileSystemInfo;
using ash::file_system_provider::ProviderId;
using ash::file_system_provider::Service;
-using file_manager::file_tasks::kDriveTaskResultMetricName;
-using file_manager::file_tasks::OfficeTaskResult;
-const char kAndroidOneDriveAuthority[] =
+constexpr char kAndroidOneDriveAuthority[] =
"com.microsoft.skydrive.content.StorageAccessProvider";
constexpr char kNotificationId[] = "cloud_upload_open_failure";
@@ -124,45 +127,75 @@ enum class Microsoft365Availability {
kMaxValue = kODFS,
};
-std::vector<ProvidedFileSystemInfo> GetODFSFileSystems(Profile* profile) {
- Service* service = Service::Get(profile);
- ProviderId provider_id = ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile));
- return service->GetProvidedFileSystemInfoList(provider_id);
+// Opens the file specified by |url| in a new tab. |url| must be a
+// docs.google.com URL for an office file.
+fm_tasks::OfficeDriveOpenErrors OpenDriveUrl(const GURL& url) {
+ if (!url.is_valid()) {
+ LOG(ERROR) << "Invalid URL";
+ return fm_tasks::OfficeDriveOpenErrors::kInvalidAlternateUrl;
+ }
+ if (url.host() == "drive.google.com") {
+ LOG(ERROR) << "URL was from drive.google.com";
+ return fm_tasks::OfficeDriveOpenErrors::kDriveAlternateUrl;
+ }
+ if (url.host() != "docs.google.com") {
+ LOG(ERROR) << "URL was not from docs.google.com";
+ return fm_tasks::OfficeDriveOpenErrors::kUnexpectedAlternateUrl;
+ }
+
+ ash::NewWindowDelegate::GetPrimary()->OpenUrl(
+ net::AppendOrReplaceQueryParameter(url, "cros_files", "true"),
+ ash::NewWindowDelegate::OpenUrlFrom::kUserInteraction,
+ ash::NewWindowDelegate::Disposition::kNewForegroundTab);
+ return fm_tasks::OfficeDriveOpenErrors::kSuccess;
+}
+
+// Logs UMA when the Drive task ends with an attempt to open a file.
+void LogGoogleDriveOpenResultUMA(OfficeTaskResult success_task_result,
+ fm_tasks::OfficeDriveOpenErrors open_result) {
+ UMA_HISTOGRAM_ENUMERATION(fm_tasks::kDriveErrorMetricName, open_result);
+ UMA_HISTOGRAM_ENUMERATION(
+ kGoogleDriveTaskResultMetricName,
+ open_result == fm_tasks::OfficeDriveOpenErrors::kSuccess
+ ? success_task_result
+ : OfficeTaskResult::kFailedToOpen);
}
// Open a hosted MS Office file e.g. .docx, from a url hosted in
// DriveFS. Check the file was successfully uploaded to DriveFS.
-void OpenUploadedDriveUrl(const GURL& url) {
- if (url.is_empty()) {
- UMA_HISTOGRAM_ENUMERATION(kDriveTaskResultMetricName,
- OfficeTaskResult::FAILED);
- return;
- }
- UMA_HISTOGRAM_ENUMERATION(kDriveTaskResultMetricName,
- OfficeTaskResult::MOVED);
- file_manager::util::OpenNewTabForHostedOfficeFile(url);
+void OpenUploadedDriveUrl(const GURL& url, const OfficeTaskResult task_result) {
+ // TODO(b/296950967): This function logs both open result and task result (but
+ // only if open fails) metrics internally, pull them up to a higher level so
+ // all the metrics are logged in one place.
+ fm_tasks::OfficeDriveOpenErrors open_result = OpenDriveUrl(url);
+ LogGoogleDriveOpenResultUMA(task_result, open_result);
}
// Open an already hosted MS Office file e.g. .docx, from a url hosted in
// DriveFS. Check there was no error retrieving the file's metadata.
-void OpenAlreadyHostedDriveUrl(drive::FileError error,
- drivefs::mojom::FileMetadataPtr metadata) {
- if (error != drive::FILE_ERROR_OK) {
- UMA_HISTOGRAM_ENUMERATION(
- file_manager::file_tasks::kDriveErrorMetricName,
- file_manager::file_tasks::OfficeDriveErrors::NO_METADATA);
+void OnGoogleDriveGetMetadata(drive::FileError error,
+ drivefs::mojom::FileMetadataPtr metadata) {
+ fm_tasks::OfficeDriveOpenErrors open_result =
+ fm_tasks::OfficeDriveOpenErrors::kSuccess;
+ if (error == drive::FILE_ERROR_OK) {
+ GURL hosted_url(metadata->alternate_url);
+ open_result = OpenDriveUrl(hosted_url);
+ } else {
LOG(ERROR) << "Drive metadata error: " << error;
- return;
+ open_result = fm_tasks::OfficeDriveOpenErrors::kNoMetadata;
}
+ LogGoogleDriveOpenResultUMA(OfficeTaskResult::kOpened, open_result);
+}
- GURL hosted_url(metadata->alternate_url);
- bool opened = file_manager::util::OpenNewTabForHostedOfficeFile(hosted_url);
-
- if (opened) {
- UMA_HISTOGRAM_ENUMERATION(kDriveTaskResultMetricName,
- OfficeTaskResult::OPENED);
- }
+// Logs UMA when the OneDrive task ends with an attempt to open a file.
+void LogOneDriveOpenResultUMA(OfficeTaskResult success_task_result,
+ fm_tasks::OfficeOneDriveOpenErrors open_result) {
+ UMA_HISTOGRAM_ENUMERATION(fm_tasks::kOneDriveErrorMetricName, open_result);
+ UMA_HISTOGRAM_ENUMERATION(
+ kOneDriveTaskResultMetricName,
+ open_result == fm_tasks::OfficeOneDriveOpenErrors::kSuccess
+ ? success_task_result
+ : OfficeTaskResult::kFailedToOpen);
}
// Handle system error notification "Sign in" click.
@@ -172,7 +205,7 @@ void HandleSignInClick(Profile* profile, absl::optional<int> button_index) {
if (button_index) {
// TODO(b/282619291) decide what callback should be.
// Request an ODFS mount which will trigger reauthentication.
- CloudUploadDialog::RequestODFSMount(profile, base::DoNothing());
+ RequestODFSMount(profile, base::DoNothing());
}
NotificationDisplayService* notification_service =
NotificationDisplayServiceFactory::GetForProfile(profile);
@@ -180,16 +213,33 @@ void HandleSignInClick(Profile* profile, absl::optional<int> button_index) {
kNotificationId);
}
-// Show system authentication error notification to prompt the user to
-// reauthenticate to ODFS via a "Sign in" button and to communicate why their
-// file can't be opened.
-void ShowUnableToOpenNotification(Profile* profile) {
- // TODO(b/254586358): i18n these strings.
+// TODO(b/288038136): Use a notification manager to handle error notifications.
+// Show system error notification to communicate that their file can't be
+// opened. If the user needs to reauthenticate to OneDrive, prompt the user to
+// reauthenticate to ODFS via a "Sign in" button.
+void ShowUnableToOpenNotification(Profile* profile,
+ bool reauthentication_required = false) {
+ std::string message = GetGenericErrorMessage();
+ std::vector<message_center::ButtonInfo> notification_buttons;
+
+ // Special case of |FILE_ERROR_ACCESS_DENIED| where the user needs to
+ // reauthenticate to OneDrive.
+ if (reauthentication_required) {
+ message = GetReauthenticationRequiredMessage();
+ // Add "Sign in" button.
+ notification_buttons.emplace_back(
+ l10n_util::GetStringUTF16(IDS_OFFICE_NOTIFICATION_SIGN_IN_BUTTON));
+ }
+
auto notification = ash::CreateSystemNotificationPtr(
/*type=*/message_center::NOTIFICATION_TYPE_SIMPLE,
- /*id=*/kNotificationId, /*title=*/u"Can't open file",
- /*message=*/
- u"Sign in to your Microsoft account and then try again",
+ /*id=*/kNotificationId,
+ // TODO(b/242685536) Use "files" for multi-files when support for
+ // multi-files is added.
+ /*title=*/
+ l10n_util::GetPluralStringFUTF16(IDS_OFFICE_UPLOAD_ERROR_CANT_OPEN_FILE,
+ 1),
+ /*message=*/base::UTF8ToUTF16(message),
/*display_source=*/
l10n_util::GetStringUTF16(IDS_ASH_MESSAGE_CENTER_SYSTEM_APP_NAME_FILES),
/*origin_url=*/GURL(),
@@ -202,11 +252,7 @@ void ShowUnableToOpenNotification(Profile* profile) {
/*warning_level=*/
message_center::SystemNotificationWarningLevel::WARNING);
- // Add "Sign in" button.
- std::vector<message_center::ButtonInfo> notification_buttons = {
- message_center::ButtonInfo(u"Sign in")};
notification->set_buttons(notification_buttons);
-
notification->set_never_timeout(true);
NotificationDisplayService* notification_service =
NotificationDisplayServiceFactory::GetForProfile(profile);
@@ -215,30 +261,66 @@ void ShowUnableToOpenNotification(Profile* profile) {
/*metadata=*/nullptr);
}
+// Check if reauthentication to OneDrive is required from the ODFS metadata
+// and show the reuathentication is required notification if true. Otherwise
+// show the generic access error notification.
+void OnGetReauthenticationRequired(
+ Profile* profile,
+ base::expected<ODFSMetadata, base::File::Error> metadata_or_error) {
+ if (!metadata_or_error.has_value()) {
+ LOG(ERROR) << "Failed to get reauthentication required state: "
+ << metadata_or_error.error();
+ return;
+ }
+ ShowUnableToOpenNotification(profile,
+ metadata_or_error->reauthentication_required);
+}
+
+// Show the correct error notification for base::File::FILE_ERROR_ACCESS_DENIED.
+// Request ODFS metadata and show the correct notification in the
+// |OnGetReauthenticationRequired| callback.
+void ShowAccessDeniedNotification(Profile* profile) {
+ file_system_provider::ProvidedFileSystemInterface* file_system =
+ GetODFS(profile);
+ if (!file_system) {
+ ShowUnableToOpenNotification(profile, /*reauthentication_required=*/false);
+ return;
+ }
+ GetODFSMetadata(file_system,
+ base::BindOnce(&OnGetReauthenticationRequired, profile));
+}
+
// Open file with |file_path| from ODFS |file_system|. Open in the OneDrive PWA
// without link capturing.
void OpenFileFromODFS(
Profile* profile,
file_system_provider::ProvidedFileSystemInterface* file_system,
- const base::FilePath& file_path) {
+ const base::FilePath& file_path,
+ base::OnceCallback<void(fm_tasks::OfficeOneDriveOpenErrors)> callback) {
file_system->GetActions(
{file_path},
base::BindOnce(
[](base::WeakPtr<Profile> profile_weak_ptr,
+ base::OnceCallback<void(fm_tasks::OfficeOneDriveOpenErrors)>
+ callback,
const file_system_provider::Actions& actions,
base::File::Error result) {
Profile* profile = profile_weak_ptr.get();
if (!profile) {
+ std::move(callback).Run(
+ fm_tasks::OfficeOneDriveOpenErrors::kNoProfile);
return;
}
- // TODO(b/288022200 b/275911611): Distinguish between
- // reauthentication required and generic error.
if (result == base::File::Error::FILE_ERROR_ACCESS_DENIED) {
- ShowUnableToOpenNotification(profile);
+ ShowAccessDeniedNotification(profile);
+ std::move(callback).Run(fm_tasks::OfficeOneDriveOpenErrors::
+ kGetActionsReauthRequired);
return;
}
if (result != base::File::Error::FILE_OK) {
- // TODO(b/275911611): Add generic "failed to open" notification.
+ ShowUnableToOpenNotification(profile);
+ std::move(callback).Run(
+ fm_tasks::OfficeOneDriveOpenErrors::kGetActionsGenericError);
return;
}
for (const file_system_provider::Action& action : actions) {
@@ -247,6 +329,8 @@ void OpenFileFromODFS(
// attribute to be opened using an installed web app.
GURL url(action.title);
if (!url.is_valid()) {
+ std::move(callback).Run(fm_tasks::OfficeOneDriveOpenErrors::
+ kGetActionsInvalidUrl);
return;
}
@@ -256,25 +340,35 @@ void OpenFileFromODFS(
/*event_flags=*/ui::EF_NONE, url,
apps::LaunchSource::kFromFileManager,
/*window_info=*/nullptr);
+ std::move(callback).Run(
+ fm_tasks::OfficeOneDriveOpenErrors::kSuccess);
return;
}
}
},
- profile->GetWeakPtr()));
+ profile->GetWeakPtr(), std::move(callback)));
}
// Open office file using the ODFS |url|.
-void OpenODFSUrl(Profile* profile, const storage::FileSystemURL& url) {
+void OpenODFSUrl(
+ Profile* profile,
+ const storage::FileSystemURL& url,
+ base::OnceCallback<void(fm_tasks::OfficeOneDriveOpenErrors)> callback) {
if (!url.is_valid()) {
LOG(ERROR) << "Invalid uploaded file URL";
+ std::move(callback).Run(
+ fm_tasks::OfficeOneDriveOpenErrors::kNoFileSystemURL);
return;
}
ash::file_system_provider::util::FileSystemURLParser parser(url);
if (!parser.Parse()) {
LOG(ERROR) << "Path not in FSP";
+ std::move(callback).Run(
+ fm_tasks::OfficeOneDriveOpenErrors::kInvalidFileSystemURL);
return;
}
- OpenFileFromODFS(profile, parser.file_system(), parser.file_path());
+ OpenFileFromODFS(profile, parser.file_system(), parser.file_path(),
+ std::move(callback));
}
// Open office files from ODFS that were originally selected from Android
@@ -291,8 +385,9 @@ void OpenAndroidOneDriveUrls(
LOG(ERROR) << "Android OneDrive Url cannot be converted to ODFS";
return;
}
- OpenFileFromODFS(profile, fs_and_path->file_system,
- fs_and_path->file_path_within_odfs);
+ OpenFileFromODFS(
+ profile, fs_and_path->file_system, fs_and_path->file_path_within_odfs,
+ base::BindOnce(&LogOneDriveOpenResultUMA, OfficeTaskResult::kOpened));
}
}
@@ -303,40 +398,29 @@ bool PathIsOnDriveFS(Profile* profile, const base::FilePath& file_path) {
return integration_service->GetRelativeDrivePath(file_path, &relative_path);
}
+bool HasFileWithExtensionFromSet(
+ const std::vector<storage::FileSystemURL>& file_urls,
+ const std::set<std::string>& extensions) {
+ return base::ranges::any_of(file_urls, [&extensions](const auto& file_url) {
+ return base::ranges::any_of(extensions, [&file_url](const auto& extension) {
+ return file_url.path().MatchesExtension(extension);
+ });
+ });
+}
+
bool HasWordFile(const std::vector<storage::FileSystemURL>& file_urls) {
- for (auto& url : file_urls) {
- for (const std::string& extension :
- file_manager::file_tasks::WordGroupExtensions()) {
- if (url.path().MatchesExtension(extension)) {
- return true;
- }
- }
- }
- return false;
+ return HasFileWithExtensionFromSet(file_urls,
+ fm_tasks::WordGroupExtensions());
}
bool HasExcelFile(const std::vector<storage::FileSystemURL>& file_urls) {
- for (auto& url : file_urls) {
- for (const std::string& extension :
- file_manager::file_tasks::ExcelGroupExtensions()) {
- if (url.path().MatchesExtension(extension)) {
- return true;
- }
- }
- }
- return false;
+ return HasFileWithExtensionFromSet(file_urls,
+ fm_tasks::ExcelGroupExtensions());
}
bool HasPowerPointFile(const std::vector<storage::FileSystemURL>& file_urls) {
- for (auto& url : file_urls) {
- for (const std::string& extension :
- file_manager::file_tasks::PowerPointGroupExtensions()) {
- if (url.path().MatchesExtension(extension)) {
- return true;
- }
- }
- }
- return false;
+ return HasFileWithExtensionFromSet(file_urls,
+ fm_tasks::PowerPointGroupExtensions());
}
// This indicates we ran Office setup and set a preference, or the user had a
@@ -344,22 +428,29 @@ bool HasPowerPointFile(const std::vector<storage::FileSystemURL>& file_urls) {
bool HaveExplicitFileHandlers(
Profile* profile,
const std::vector<storage::FileSystemURL>& file_urls) {
- return std::all_of(
- file_urls.begin(), file_urls.end(),
- [profile](const storage::FileSystemURL& url) {
- return file_manager::file_tasks::HasExplicitDefaultFileHandler(
- profile, url.path().FinalExtension());
- });
+ return base::ranges::all_of(file_urls, [profile](const auto& url) {
+ return fm_tasks::HasExplicitDefaultFileHandler(profile,
+ url.path().FinalExtension());
+ });
+}
+
+// This indicates we ran Office setup and set a preference, or the user had a
+// pre-existing preference for these file types.
+bool HaveExplicitFileHandlers(Profile* profile,
+ const std::set<std::string>& extensions) {
+ return base::ranges::all_of(extensions, [profile](const auto& extension) {
+ return fm_tasks::HasExplicitDefaultFileHandler(profile, extension);
+ });
}
void RecordMicrosoft365Availability(const char* metric, Profile* profile) {
base::EnumSet<Microsoft365Availability, Microsoft365Availability::kMinValue,
Microsoft365Availability::kMaxValue>
ms365_state;
- if (CloudUploadDialog::IsOfficeWebAppInstalled(profile)) {
+ if (IsOfficeWebAppInstalled(profile)) {
ms365_state.Put(Microsoft365Availability::kPWA);
}
- if (CloudUploadDialog::IsODFSMounted(profile)) {
+ if (IsODFSMounted(profile)) {
ms365_state.Put(Microsoft365Availability::kODFS);
}
base::UmaHistogramExactLinear(
@@ -460,7 +551,7 @@ void CloudOpenTask::OpenOrMoveFiles() {
transfer_required_ = OfficeFilesTransferRequired::kNotRequired;
UMA_HISTOGRAM_ENUMERATION(kOneDriveTransferRequiredMetric,
OfficeFilesTransferRequired::kNotRequired);
- OpenODFSUrls();
+ OpenODFSUrls(OfficeTaskResult::kOpened);
} else if (cloud_provider_ == CloudProvider::kOneDrive &&
UrlIsOnAndroidOneDrive(profile_, file_urls_.front())) {
// The files are on OneDrive already, selected from Android OneDrive.
@@ -470,10 +561,10 @@ void CloudOpenTask::OpenOrMoveFiles() {
OpenAndroidOneDriveUrlsIfAccountMatchedODFS();
} else {
// The files need to be moved.
- auto operation = GetOperationTypeForUpload(profile_, file_urls_.front()) ==
- file_manager::io_task::OperationType::kCopy
- ? OfficeFilesTransferRequired::kCopy
- : OfficeFilesTransferRequired::kMove;
+ auto operation =
+ GetUploadType(profile_, file_urls_.front()) == UploadType::kCopy
+ ? OfficeFilesTransferRequired::kCopy
+ : OfficeFilesTransferRequired::kMove;
transfer_required_ = operation;
switch (cloud_provider_) {
case CloudProvider::kGoogleDrive:
@@ -495,16 +586,17 @@ void CloudOpenTask::OpenAlreadyHostedDriveUrls() {
if (integration_service->GetRelativeDrivePath(file_url.path(),
&relative_path)) {
integration_service->GetDriveFsInterface()->GetMetadata(
- relative_path, base::BindOnce(&OpenAlreadyHostedDriveUrl));
+ relative_path, base::BindOnce(&OnGoogleDriveGetMetadata));
} else {
LOG(ERROR) << "Unexpected error obtaining the relative path ";
}
}
}
-void CloudOpenTask::OpenODFSUrls() {
+void CloudOpenTask::OpenODFSUrls(const OfficeTaskResult task_result_uma) {
for (const auto& file_url : file_urls_) {
- OpenODFSUrl(profile_, file_url);
+ OpenODFSUrl(profile_, file_url,
+ base::BindOnce(&LogOneDriveOpenResultUMA, task_result_uma));
}
}
@@ -518,47 +610,43 @@ bool CloudOpenTask::ShouldShowConfirmationDialog() {
switch (source_type) {
case SourceType::READ_ONLY:
force_show_confirmation_dialog =
- !file_manager::file_tasks::
- GetOfficeMoveConfirmationShownForLocalToDrive(profile_) &&
- !file_manager::file_tasks::
- GetOfficeMoveConfirmationShownForCloudToDrive(profile_);
+ !fm_tasks::GetOfficeMoveConfirmationShownForLocalToDrive(
+ profile_) &&
+ !fm_tasks::GetOfficeMoveConfirmationShownForCloudToDrive(profile_);
break;
case SourceType::LOCAL:
force_show_confirmation_dialog =
- !file_manager::file_tasks::
- GetOfficeMoveConfirmationShownForLocalToDrive(profile_);
+ !fm_tasks::GetOfficeMoveConfirmationShownForLocalToDrive(profile_);
break;
case SourceType::CLOUD:
force_show_confirmation_dialog =
- !file_manager::file_tasks::
- GetOfficeMoveConfirmationShownForCloudToDrive(profile_);
+ !fm_tasks::GetOfficeMoveConfirmationShownForCloudToDrive(profile_);
break;
}
return force_show_confirmation_dialog ||
- !file_manager::file_tasks::GetAlwaysMoveOfficeFilesToDrive(profile_);
+ !fm_tasks::GetAlwaysMoveOfficeFilesToDrive(profile_);
} else if (cloud_provider_ == CloudProvider::kOneDrive) {
switch (source_type) {
case SourceType::READ_ONLY:
force_show_confirmation_dialog =
- !file_manager::file_tasks::
- GetOfficeMoveConfirmationShownForLocalToOneDrive(profile_) &&
- !file_manager::file_tasks::
- GetOfficeMoveConfirmationShownForCloudToOneDrive(profile_);
+ !fm_tasks::GetOfficeMoveConfirmationShownForLocalToOneDrive(
+ profile_) &&
+ !fm_tasks::GetOfficeMoveConfirmationShownForCloudToOneDrive(
+ profile_);
break;
case SourceType::LOCAL:
force_show_confirmation_dialog =
- !file_manager::file_tasks::
- GetOfficeMoveConfirmationShownForLocalToOneDrive(profile_);
+ !fm_tasks::GetOfficeMoveConfirmationShownForLocalToOneDrive(
+ profile_);
break;
case SourceType::CLOUD:
force_show_confirmation_dialog =
- !file_manager::file_tasks::
- GetOfficeMoveConfirmationShownForCloudToOneDrive(profile_);
+ !fm_tasks::GetOfficeMoveConfirmationShownForCloudToOneDrive(
+ profile_);
break;
}
return force_show_confirmation_dialog ||
- !file_manager::file_tasks::GetAlwaysMoveOfficeFilesToOneDrive(
- profile_);
+ !fm_tasks::GetAlwaysMoveOfficeFilesToOneDrive(profile_);
}
NOTREACHED();
return true;
@@ -577,24 +665,9 @@ void CloudOpenTask::ConfirmMoveOrStartUpload() {
}
}
-bool IsEligibleAndEnabledUploadOfficeToCloud(Profile* profile) {
- if (!chromeos::features::IsUploadOfficeToCloudEnabled()) {
- return false;
- }
- if (!profile) {
- return false;
- }
- // Managed users, e.g. enterprise account, child account, are not eligible.
- if (profile->GetProfilePolicyConnector()->IsManaged()) {
- return false;
- }
- return true;
-}
-
bool ShouldFixUpOffice(Profile* profile, const CloudProvider cloud_provider) {
return cloud_provider == CloudProvider::kOneDrive &&
- !(CloudUploadDialog::IsODFSMounted(profile) &&
- CloudUploadDialog::IsOfficeWebAppInstalled(profile));
+ !(IsODFSMounted(profile) && IsOfficeWebAppInstalled(profile));
}
bool UrlIsOnODFS(Profile* profile, const FileSystemURL& url) {
@@ -605,7 +678,7 @@ bool UrlIsOnODFS(Profile* profile, const FileSystemURL& url) {
file_system_provider::ProviderId provider_id =
file_system_provider::ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile));
+ extension_misc::kODFSExtensionId);
if (parser.file_system()->GetFileSystemInfo().provider_id() != provider_id) {
return false;
}
@@ -668,11 +741,9 @@ void CloudOpenTask::OpenAndroidOneDriveUrlsIfAccountMatchedODFS() {
LOG(ERROR) << "Android OneDrive Url cannot be converted to ODFS";
return;
}
- // TODO(b/288022200): Query '/' instead to get user email.
- fs_and_path->file_system->GetActions(
- {fs_and_path->file_path_within_odfs},
- base::BindOnce(&CloudOpenTask::CheckEmailAndOpenURLs, this,
- android_onedrive_email.value()));
+ GetODFSMetadata(fs_and_path->file_system,
+ base::BindOnce(&CloudOpenTask::CheckEmailAndOpenURLs, this,
+ android_onedrive_email.value()));
}
absl::optional<ODFSFileSystemAndPath> AndroidOneDriveUrlToODFS(
@@ -684,14 +755,12 @@ absl::optional<ODFSFileSystemAndPath> AndroidOneDriveUrlToODFS(
}
// Get the ODFS mount path.
- std::vector<ProvidedFileSystemInfo> odfs_file_system_infos =
- GetODFSFileSystems(profile);
- if (odfs_file_system_infos.size() != 1u) {
- LOG(ERROR) << "One and only one filesystem should be mounted for the ODFS "
- "extension";
+ absl::optional<ProvidedFileSystemInfo> odfs_file_system_info =
+ GetODFSInfo(profile);
+ if (!odfs_file_system_info.has_value()) {
return absl::nullopt;
}
- base::FilePath odfs_path = odfs_file_system_infos[0].mount_path();
+ base::FilePath odfs_path = odfs_file_system_info->mount_path();
// Find the relative path from Android OneDrive Url.
std::string authority;
@@ -730,24 +799,22 @@ absl::optional<ODFSFileSystemAndPath> AndroidOneDriveUrlToODFS(
void CloudOpenTask::CheckEmailAndOpenURLs(
const std::string& android_onedrive_email,
- const file_system_provider::Actions& actions,
- base::File::Error result) {
- if (result != base::File::Error::FILE_OK) {
- LOG(ERROR) << "Failed to get actions: " << result;
+ base::expected<ODFSMetadata, base::File::Error> metadata_or_error) {
+ if (!metadata_or_error.has_value()) {
+ LOG(ERROR) << "Failed to get user email: " << metadata_or_error.error();
+ return;
+ }
+ if (metadata_or_error->user_email.empty()) {
+ LOG(ERROR) << "User email is empty";
return;
}
// Query whether the account logged into Android OneDrive is the
// same as ODFS.
- for (const file_system_provider::Action& action : actions) {
- if (action.id == kUserEmailActionId) {
- if (android_onedrive_email == action.title) {
- OpenAndroidOneDriveUrls(profile_, file_urls_);
- } else {
- LOG(ERROR) << "Email accounts associated with ODFS and "
- "Android OneDrive don't match.";
- }
- return;
- }
+ if (android_onedrive_email == metadata_or_error->user_email) {
+ OpenAndroidOneDriveUrls(profile_, file_urls_);
+ } else {
+ LOG(ERROR) << "Email accounts associated with ODFS and "
+ "Android OneDrive don't match.";
}
}
@@ -772,43 +839,65 @@ void CloudOpenTask::StartUpload() {
}
}
-void CloudOpenTask::FinishedDriveUpload(const GURL& url, int64_t size) {
+void CloudOpenTask::FinishedDriveUpload(absl::optional<GURL> url,
+ int64_t size) {
DCHECK_GT(pending_uploads_, 0UL);
- OpenUploadedDriveUrl(url);
- if (size > 0) {
+ if (url.has_value()) {
upload_total_size_ += size;
+ fm_tasks::SetOfficeFileMovedToGoogleDrive(profile_, base::Time::Now());
+ // Open the URL.
+ const OfficeTaskResult task_result_uma =
+ transfer_required_ == OfficeFilesTransferRequired::kCopy
+ ? OfficeTaskResult::kCopied
+ : OfficeTaskResult::kMoved;
+ OpenUploadedDriveUrl(url.value(), task_result_uma);
+ } else {
+ has_upload_errors_ = true;
+ UMA_HISTOGRAM_ENUMERATION(kGoogleDriveTaskResultMetricName,
+ OfficeTaskResult::kFailedToUpload);
}
if (--pending_uploads_) {
return;
}
- RecordUploadLatencyUMA();
- file_manager::file_tasks::SetOfficeFileMovedToGoogleDrive(profile_,
- base::Time::Now());
+ if (!has_upload_errors_) {
+ RecordUploadLatencyUMA();
+ }
}
void CloudOpenTask::FinishedOneDriveUpload(
base::WeakPtr<Profile> profile_weak_ptr,
- const storage::FileSystemURL& url,
+ absl::optional<storage::FileSystemURL> url,
int64_t size) {
DCHECK_GT(pending_uploads_, 0UL);
- Profile* profile = profile_weak_ptr.get();
- if (!profile) {
- return;
- }
- if (size > 0) {
+ if (url.has_value()) {
upload_total_size_ += size;
+ Profile* profile = profile_weak_ptr.get();
+ if (!profile) {
+ // TODO(b/296950967): metric to log here?
+ return;
+ }
+ fm_tasks::SetOfficeFileMovedToOneDrive(profile, base::Time::Now());
+ const OfficeTaskResult task_result_uma =
+ transfer_required_ == OfficeFilesTransferRequired::kCopy
+ ? OfficeTaskResult::kCopied
+ : OfficeTaskResult::kMoved;
+ OpenODFSUrl(profile, url.value(),
+ base::BindOnce(&LogOneDriveOpenResultUMA, task_result_uma));
+ } else {
+ has_upload_errors_ = true;
+ UMA_HISTOGRAM_ENUMERATION(kOneDriveTaskResultMetricName,
+ OfficeTaskResult::kFailedToUpload);
}
- OpenODFSUrl(profile, url);
if (--pending_uploads_) {
return;
}
- RecordUploadLatencyUMA();
- file_manager::file_tasks::SetOfficeFileMovedToOneDrive(profile,
- base::Time::Now());
+ if (!has_upload_errors_) {
+ RecordUploadLatencyUMA();
+ }
}
void CloudOpenTask::RecordUploadLatencyUMA() {
- const int64_t kMegabyte = 1000 * 1000;
+ constexpr int64_t kMegabyte = 1000 * 1000;
std::string uma_size;
if (upload_total_size_ > 1000 * kMegabyte) {
uma_size = "1000MB-and-above";
@@ -848,10 +937,9 @@ bool CloudOpenTask::InitAndShowDialog(mojom::DialogPage dialog_page) {
// Display local file handlers (tasks) only for the file handler dialog.
if (dialog_page == mojom::DialogPage::kFileHandlerDialog) {
// Callback to show the dialog after the tasks have been found.
- file_manager::file_tasks::FindTasksCallback
- find_all_types_of_tasks_callback =
- base::BindOnce(IgnoreResult(&CloudOpenTask::ShowDialog), this,
- std::move(args), dialog_page);
+ fm_tasks::FindTasksCallback find_all_types_of_tasks_callback =
+ base::BindOnce(IgnoreResult(&CloudOpenTask::ShowDialog), this,
+ std::move(args), dialog_page);
// Find the file tasks that can open the `file_urls_` and then run
// `ShowDialog`.
FindTasksForDialog(std::move(find_all_types_of_tasks_callback));
@@ -868,25 +956,16 @@ mojom::DialogArgsPtr CloudOpenTask::CreateDialogArgs(
args->file_names.push_back(file_url.path().BaseName().value());
}
args->dialog_page = dialog_page;
- args->first_time_setup = !HaveExplicitFileHandlers(profile_, file_urls_);
- const file_manager::io_task::OperationType operation_type =
- GetOperationTypeForUpload(profile_, file_urls_[0]);
- switch (operation_type) {
- case file_manager::io_task::OperationType::kMove:
+ args->set_office_as_default_handler =
+ !HaveExplicitFileHandlers(profile_, file_urls_);
+ const UploadType upload_type = GetUploadType(profile_, file_urls_[0]);
+ switch (upload_type) {
+ case UploadType::kMove:
args->operation_type = mojom::OperationType::kMove;
break;
- case file_manager::io_task::OperationType::kCopy:
+ case UploadType::kCopy:
args->operation_type = mojom::OperationType::kCopy;
break;
- case file_manager::io_task::OperationType::kDelete:
- case file_manager::io_task::OperationType::kEmptyTrash:
- case file_manager::io_task::OperationType::kExtract:
- case file_manager::io_task::OperationType::kRestore:
- case file_manager::io_task::OperationType::kRestoreToDestination:
- case file_manager::io_task::OperationType::kTrash:
- case file_manager::io_task::OperationType::kZip:
- NOTREACHED() << "Unexpected upload operation type";
- break;
}
return args;
}
@@ -899,16 +978,15 @@ mojom::DialogArgsPtr CloudOpenTask::CreateDialogArgs(
void CloudOpenTask::ShowDialog(
mojom::DialogArgsPtr args,
const mojom::DialogPage dialog_page,
- std::unique_ptr<::file_manager::file_tasks::ResultingTasks>
- resulting_tasks) {
- SetTaskArgs(args, std::move(resulting_tasks));
+ std::unique_ptr<fm_tasks::ResultingTasks> resulting_tasks) {
+ if (resulting_tasks) {
+ SetTaskArgs(args, std::move(resulting_tasks));
+ }
bool office_move_confirmation_shown =
cloud_provider_ == CloudProvider::kGoogleDrive
- ? file_manager::file_tasks::GetOfficeMoveConfirmationShownForDrive(
- profile_)
- : file_manager::file_tasks::GetOfficeMoveConfirmationShownForOneDrive(
- profile_);
+ ? fm_tasks::GetOfficeMoveConfirmationShownForDrive(profile_)
+ : fm_tasks::GetOfficeMoveConfirmationShownForOneDrive(profile_);
// This CloudUploadDialog pointer is managed by an instance of
// `views::WebDialogView` and deleted in
// `SystemWebDialogDelegate::OnDialogClosed`.
@@ -933,30 +1011,26 @@ void CloudOpenTask::ShowDialog(
// Stores constructed tasks into `args->tasks` and `local_tasks_`.
void CloudOpenTask::SetTaskArgs(
mojom::DialogArgsPtr& args,
- std::unique_ptr<::file_manager::file_tasks::ResultingTasks>
- resulting_tasks) {
- if (resulting_tasks) {
- int nextPosition = 0;
- for (const file_manager::file_tasks::FullTaskDescriptor& task :
- resulting_tasks->tasks) {
- // Ignore Google Docs and MS Office tasks as they are already
- // set up to show in the dialog.
- if (IsWebDriveOfficeTask(task.task_descriptor) ||
- file_manager::file_tasks::IsOpenInOfficeTask(task.task_descriptor)) {
- continue;
- }
- mojom::DialogTaskPtr dialog_task = mojom::DialogTask::New();
- // The (unique and positive) `position` of the task in the `tasks` vector.
- // If the user responds with the `position`, the task will be launched via
- // `LaunchLocalFileTask()`.
- dialog_task->position = nextPosition++;
- dialog_task->title = task.task_title;
- dialog_task->icon_url = task.icon_url.spec();
- dialog_task->app_id = task.task_descriptor.app_id;
-
- args->local_tasks.push_back(std::move(dialog_task));
- local_tasks_.push_back(std::move(task.task_descriptor));
+ std::unique_ptr<fm_tasks::ResultingTasks> resulting_tasks) {
+ int nextPosition = 0;
+ for (fm_tasks::FullTaskDescriptor& task : resulting_tasks->tasks) {
+ // Ignore Google Docs and MS Office tasks as they are already
+ // set up to show in the dialog.
+ if (fm_tasks::IsWebDriveOfficeTask(task.task_descriptor) ||
+ fm_tasks::IsOpenInOfficeTask(task.task_descriptor)) {
+ continue;
}
+ mojom::DialogTaskPtr dialog_task = mojom::DialogTask::New();
+ // The (unique and positive) `position` of the task in the `tasks` vector.
+ // If the user responds with the `position`, the task will be launched via
+ // `LaunchLocalFileTask()`.
+ dialog_task->position = nextPosition++;
+ dialog_task->title = task.task_title;
+ dialog_task->icon_url = task.icon_url.spec();
+ dialog_task->app_id = task.task_descriptor.app_id;
+
+ args->local_tasks.push_back(std::move(dialog_task));
+ local_tasks_.push_back(std::move(task.task_descriptor));
}
}
@@ -982,18 +1056,6 @@ void CloudOpenTask::OnBrowserAdded(Browser* browser) {
// task in `local_tasks_` to launch. We never use the return value but it's
// necessary to make sure that we delete CloudOpenTask when we're done.
void CloudOpenTask::OnDialogComplete(const std::string& user_response) {
- using file_manager::file_tasks::SetExcelFileHandlerToFilesSWA;
- using file_manager::file_tasks::SetOfficeMoveConfirmationShownForCloudToDrive;
- using file_manager::file_tasks::
- SetOfficeMoveConfirmationShownForCloudToOneDrive;
- using file_manager::file_tasks::SetOfficeMoveConfirmationShownForDrive;
- using file_manager::file_tasks::SetOfficeMoveConfirmationShownForLocalToDrive;
- using file_manager::file_tasks::
- SetOfficeMoveConfirmationShownForLocalToOneDrive;
- using file_manager::file_tasks::SetOfficeMoveConfirmationShownForOneDrive;
- using file_manager::file_tasks::SetPowerPointFileHandlerToFilesSWA;
- using file_manager::file_tasks::SetWordFileHandlerToFilesSWA;
-
// TODO(petermarshall): Don't need separate actions for drive/onedrive now
// (and for StartUpload?).
if (user_response == kUserActionConfirmOrUploadToGoogleDrive) {
@@ -1006,21 +1068,20 @@ void CloudOpenTask::OnDialogComplete(const std::string& user_response) {
if (HasWordFile(file_urls_)) {
UMA_HISTOGRAM_ENUMERATION(kFileHandlerSelectionMetricName,
OfficeSetupFileHandler::kGoogleDocs);
- SetWordFileHandlerToFilesSWA(
- profile_, file_manager::file_tasks::kActionIdWebDriveOfficeWord);
+ fm_tasks::SetWordFileHandlerToFilesSWA(
+ profile_, fm_tasks::kActionIdWebDriveOfficeWord);
}
if (HasExcelFile(file_urls_)) {
UMA_HISTOGRAM_ENUMERATION(kFileHandlerSelectionMetricName,
OfficeSetupFileHandler::kGoogleSheets);
- SetExcelFileHandlerToFilesSWA(
- profile_, file_manager::file_tasks::kActionIdWebDriveOfficeExcel);
+ fm_tasks::SetExcelFileHandlerToFilesSWA(
+ profile_, fm_tasks::kActionIdWebDriveOfficeExcel);
}
if (HasPowerPointFile(file_urls_)) {
UMA_HISTOGRAM_ENUMERATION(kFileHandlerSelectionMetricName,
OfficeSetupFileHandler::kGoogleSlides);
- SetPowerPointFileHandlerToFilesSWA(
- profile_,
- file_manager::file_tasks::kActionIdWebDriveOfficePowerPoint);
+ fm_tasks::SetPowerPointFileHandlerToFilesSWA(
+ profile_, fm_tasks::kActionIdWebDriveOfficePowerPoint);
}
OpenOrMoveFiles();
} else if (user_response == kUserActionConfirmOrUploadToOneDrive) {
@@ -1029,14 +1090,14 @@ void CloudOpenTask::OnDialogComplete(const std::string& user_response) {
OpenOrMoveFiles();
} else if (user_response == kUserActionUploadToGoogleDrive) {
cloud_provider_ = CloudProvider::kGoogleDrive;
- SetOfficeMoveConfirmationShownForDrive(profile_, true);
+ fm_tasks::SetOfficeMoveConfirmationShownForDrive(profile_, true);
SourceType source_type = GetSourceType(profile_, file_urls_[0]);
switch (source_type) {
case SourceType::LOCAL:
- SetOfficeMoveConfirmationShownForLocalToDrive(profile_, true);
+ fm_tasks::SetOfficeMoveConfirmationShownForLocalToDrive(profile_, true);
break;
case SourceType::CLOUD:
- SetOfficeMoveConfirmationShownForCloudToDrive(profile_, true);
+ fm_tasks::SetOfficeMoveConfirmationShownForCloudToDrive(profile_, true);
break;
case SourceType::READ_ONLY:
// TODO (jboulic): Clarify UX.
@@ -1044,14 +1105,16 @@ void CloudOpenTask::OnDialogComplete(const std::string& user_response) {
}
StartUpload();
} else if (user_response == kUserActionUploadToOneDrive) {
- SetOfficeMoveConfirmationShownForOneDrive(profile_, true);
+ fm_tasks::SetOfficeMoveConfirmationShownForOneDrive(profile_, true);
SourceType source_type = GetSourceType(profile_, file_urls_[0]);
switch (source_type) {
case SourceType::LOCAL:
- SetOfficeMoveConfirmationShownForLocalToOneDrive(profile_, true);
+ fm_tasks::SetOfficeMoveConfirmationShownForLocalToOneDrive(profile_,
+ true);
break;
case SourceType::CLOUD:
- SetOfficeMoveConfirmationShownForCloudToOneDrive(profile_, true);
+ fm_tasks::SetOfficeMoveConfirmationShownForCloudToOneDrive(profile_,
+ true);
break;
case SourceType::READ_ONLY:
// TODO (jboulic): Clarify UX.
@@ -1064,8 +1127,13 @@ void CloudOpenTask::OnDialogComplete(const std::string& user_response) {
cloud_provider_ = CloudProvider::kOneDrive;
InitAndShowDialog(mojom::DialogPage::kOneDriveSetup);
} else if (user_response == kUserActionCancel) {
- UMA_HISTOGRAM_ENUMERATION(kDriveTaskResultMetricName,
- OfficeTaskResult::CANCELLED);
+ // Do nothing.
+ } else if (user_response == kUserActionCancelGoogleDrive) {
+ UMA_HISTOGRAM_ENUMERATION(kGoogleDriveTaskResultMetricName,
+ OfficeTaskResult::kCancelled);
+ } else if (user_response == kUserActionCancelOneDrive) {
+ UMA_HISTOGRAM_ENUMERATION(kOneDriveTaskResultMetricName,
+ OfficeTaskResult::kCancelled);
} else {
LaunchLocalFileTask(user_response);
}
@@ -1089,12 +1157,12 @@ void CloudOpenTask::LaunchLocalFileTask(
return;
}
// Launch the task.
- file_manager::file_tasks::TaskDescriptor& task = local_tasks_[task_position];
+ fm_tasks::TaskDescriptor& task = local_tasks_[task_position];
UMA_HISTOGRAM_ENUMERATION(kFileHandlerSelectionMetricName,
extension_misc::IsQuickOfficeExtension(task.app_id)
? OfficeSetupFileHandler::kQuickOffice
: OfficeSetupFileHandler::kOtherLocalHandler);
- file_manager::file_tasks::ExecuteFileTask(
+ fm_tasks::ExecuteFileTask(
profile_, task, file_urls_, nullptr,
base::BindOnce(&CloudOpenTask::LocalTaskExecuted, this, task));
}
@@ -1102,7 +1170,7 @@ void CloudOpenTask::LaunchLocalFileTask(
// We never use the return value but it's necessary to make sure that we delete
// CloudOpenTask when we're done.
void CloudOpenTask::LocalTaskExecuted(
- const file_manager::file_tasks::TaskDescriptor& task,
+ const fm_tasks::TaskDescriptor& task,
extensions::api::file_manager_private::TaskResult result,
std::string error_message) {
if (!error_message.empty()) {
@@ -1113,21 +1181,20 @@ void CloudOpenTask::LocalTaskExecuted(
}
if (HasWordFile(file_urls_)) {
- SetWordFileHandler(profile_, task);
+ fm_tasks::SetWordFileHandler(profile_, task);
}
if (HasExcelFile(file_urls_)) {
- SetExcelFileHandler(profile_, task);
+ fm_tasks::SetExcelFileHandler(profile_, task);
}
if (HasPowerPointFile(file_urls_)) {
- SetPowerPointFileHandler(profile_, task);
+ fm_tasks::SetPowerPointFileHandler(profile_, task);
}
}
// Find the file tasks that can open the `file_urls` and pass them to the
// `find_all_types_of_tasks_callback`.
void CloudOpenTask::FindTasksForDialog(
- file_manager::file_tasks::FindTasksCallback
- find_all_types_of_tasks_callback) {
+ fm_tasks::FindTasksCallback find_all_types_of_tasks_callback) {
using extensions::app_file_handler_util::MimeTypeCollector;
// Get the file info for finding the tasks.
std::vector<base::FilePath> local_paths;
@@ -1141,7 +1208,8 @@ void CloudOpenTask::FindTasksForDialog(
// get the entries.
std::unique_ptr<MimeTypeCollector> mime_collector =
std::make_unique<MimeTypeCollector>(profile_);
- mime_collector.get()->CollectForLocalPaths(
+ auto* mime_collector_ptr = mime_collector.get();
+ mime_collector_ptr->CollectForLocalPaths(
local_paths,
base::BindOnce(&CloudOpenTask::ConstructEntriesAndFindTasks, this,
local_paths, gurls, std::move(mime_collector),
@@ -1153,8 +1221,7 @@ void CloudOpenTask::ConstructEntriesAndFindTasks(
const std::vector<GURL>& gurls,
std::unique_ptr<extensions::app_file_handler_util::MimeTypeCollector>
mime_collector,
- file_manager::file_tasks::FindTasksCallback
- find_all_types_of_tasks_callback,
+ fm_tasks::FindTasksCallback find_all_types_of_tasks_callback,
std::unique_ptr<std::vector<std::string>> mime_types) {
std::vector<extensions::EntryInfo> entries;
DCHECK_EQ(file_paths.size(), mime_types->size());
@@ -1163,43 +1230,15 @@ void CloudOpenTask::ConstructEntriesAndFindTasks(
}
const std::vector<std::string> dlp_source_urls(entries.size(), "");
- file_manager::file_tasks::FindAllTypesOfTasks(
- profile_, entries, gurls, dlp_source_urls,
- std::move(find_all_types_of_tasks_callback));
+ fm_tasks::FindAllTypesOfTasks(profile_, entries, gurls, dlp_source_urls,
+ std::move(find_all_types_of_tasks_callback));
}
void CloudOpenTask::SetTasksForTest(
- const std::vector<file_manager::file_tasks::TaskDescriptor>& tasks) {
+ const std::vector<fm_tasks::TaskDescriptor>& tasks) {
local_tasks_ = tasks;
}
-void CloudUploadDialog::RequestODFSMount(
- Profile* profile,
- file_system_provider::RequestMountCallback callback) {
- Service* service = Service::Get(profile);
- ProviderId provider_id = ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile));
- service->RequestMount(provider_id, std::move(callback));
-}
-
-bool CloudUploadDialog::IsODFSMounted(Profile* profile) {
- // Assume any file system mounted by ODFS is the correct one.
- return !GetODFSFileSystems(profile).empty();
-}
-
-bool CloudUploadDialog::IsOfficeWebAppInstalled(Profile* profile) {
- if (!apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) {
- return false;
- }
- auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
- bool installed = false;
- proxy->AppRegistryCache().ForOneApp(
- web_app::kMicrosoft365AppId, [&installed](const apps::AppUpdate& update) {
- installed = apps_util::IsInstalled(update.Readiness());
- });
- return installed;
-}
-
void CloudUploadDialog::OnDialogShown(content::WebUI* webui) {
DCHECK(dialog_args_);
SystemWebDialogDelegate::OnDialogShown(webui);
@@ -1247,20 +1286,20 @@ bool CloudUploadDialog::ShouldShowCloseButton() const {
}
namespace {
-const int kDialogWidthForOneDriveSetup = 512;
-const int kDialogHeightForOneDriveSetup = 556;
+constexpr int kDialogWidthForOneDriveSetup = 512;
+constexpr int kDialogHeightForOneDriveSetup = 556;
-const int kDialogWidthForFileHandlerDialog = 512;
-const int kDialogHeightForFileHandlerDialog = 375;
-const int kDialogHeightForFileHandlerDialogNoLocalApp = 311;
+constexpr int kDialogWidthForFileHandlerDialog = 512;
+constexpr int kDialogHeightForFileHandlerDialog = 379;
+constexpr int kDialogHeightForFileHandlerDialogNoLocalApp = 315;
-const int kDialogWidthForMoveConfirmation = 512;
-const int kDialogHeightForMoveConfirmationWithCheckbox = 500;
+constexpr int kDialogWidthForMoveConfirmation = 512;
+constexpr int kDialogHeightForMoveConfirmationWithCheckbox = 524;
-const int kDialogHeightForMoveConfirmationWithoutCheckbox = 448;
+constexpr int kDialogHeightForMoveConfirmationWithoutCheckbox = 472;
-const int kDialogWidthForConnectToOneDrive = 512;
-const int kDialogHeightForConnectToOneDrive = 556;
+constexpr int kDialogWidthForConnectToOneDrive = 512;
+constexpr int kDialogHeightForConnectToOneDrive = 556;
} // namespace
void CloudUploadDialog::GetDialogSize(gfx::Size* size) const {
@@ -1317,4 +1356,27 @@ bool ShowConnectOneDriveDialog(gfx::NativeWindow modal_parent) {
return true;
}
+void LaunchMicrosoft365Setup(Profile* profile, gfx::NativeWindow modal_parent) {
+ mojom::DialogArgsPtr args = mojom::DialogArgs::New();
+ args->dialog_page = mojom::DialogPage::kOneDriveSetup;
+
+ // If `set_office_as_default_handler` is false, it indicates that we already
+ // ran the Office setup and set file handler preferences for all handled
+ // Office file types, or that the user has pre-existing preferences for these
+ // file types.
+ args->set_office_as_default_handler =
+ !HaveExplicitFileHandlers(profile, fm_tasks::WordGroupExtensions()) ||
+ !HaveExplicitFileHandlers(profile, fm_tasks::ExcelGroupExtensions()) ||
+ !HaveExplicitFileHandlers(profile, fm_tasks::PowerPointGroupExtensions());
+
+ // This CloudUploadDialog pointer is managed by an instance of
+ // `views::WebDialogView` and deleted in
+ // `SystemWebDialogDelegate::OnDialogClosed`.
+ CloudUploadDialog* dialog = new CloudUploadDialog(
+ std::move(args), base::DoNothing(), mojom::DialogPage::kOneDriveSetup,
+ /*office_move_confirmation_shown=*/false);
+
+ dialog->ShowSystemDialog(modal_parent);
+}
+
} // namespace ash::cloud_upload
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h
index c3ccc288085..be20ce326b0 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h
@@ -9,6 +9,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
#include "base/timer/elapsed_timer.h"
#include "chrome/browser/ash/file_manager/file_tasks.h"
#include "chrome/browser/ash/file_system_provider/mount_path_util.h"
@@ -17,8 +18,10 @@
#include "chrome/browser/platform_util.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
#include "chrome/browser/ui/webui/ash/system_web_dialog_delegate.h"
#include "storage/browser/file_system/file_system_url.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/size.h"
class Profile;
@@ -31,6 +34,9 @@ namespace file_manager::file_tasks {
FORWARD_DECLARE_TEST(DriveTest, OpenFileInDrive);
FORWARD_DECLARE_TEST(OneDriveTest, OpenFileFromODFS);
FORWARD_DECLARE_TEST(OneDriveTest, OpenFileNotFromODFS);
+FORWARD_DECLARE_TEST(OneDriveTest,
+ FailToOpenFileFromODFSReauthenticationRequired);
+FORWARD_DECLARE_TEST(OneDriveTest, FailToOpenFileFromODFSOtherAccessError);
FORWARD_DECLARE_TEST(OneDriveTest, OpenFileFromAndroidOneDriveViaODFS);
FORWARD_DECLARE_TEST(OneDriveTest,
FailToOpenFileFromAndroidOneDriveViaODFSDiffEmail);
@@ -54,24 +60,25 @@ enum class OfficeFilesTransferRequired {
namespace ash::cloud_upload {
struct ODFSFileSystemAndPath {
- file_system_provider::ProvidedFileSystemInterface* file_system;
+ // This field is not a raw_ptr<> because it was filtered by the rewriter
+ // for: #union
+ RAW_PTR_EXCLUSION file_system_provider::ProvidedFileSystemInterface*
+ file_system;
base::FilePath file_path_within_odfs;
};
// The string conversions of ash::cloud_upload::mojom::UserAction.
-const char kUserActionCancel[] = "cancel";
-const char kUserActionSetUpOneDrive[] = "setup-onedrive";
-const char kUserActionUploadToGoogleDrive[] = "upload-drive";
-const char kUserActionUploadToOneDrive[] = "upload-onedrive";
-const char kUserActionConfirmOrUploadToGoogleDrive[] =
+constexpr char kUserActionCancel[] = "cancel";
+constexpr char kUserActionCancelGoogleDrive[] = "cancel-drive";
+constexpr char kUserActionCancelOneDrive[] = "cancel-onedrive";
+constexpr char kUserActionSetUpOneDrive[] = "setup-onedrive";
+constexpr char kUserActionUploadToGoogleDrive[] = "upload-drive";
+constexpr char kUserActionUploadToOneDrive[] = "upload-onedrive";
+constexpr char kUserActionConfirmOrUploadToGoogleDrive[] =
"confirm-or-upload-google-drive";
-const char kUserActionConfirmOrUploadToOneDrive[] =
+constexpr char kUserActionConfirmOrUploadToOneDrive[] =
"confirm-or-upload-onedrive";
-// Custom action ids passed from ODFS.
-const char kOneDriveUrlActionId[] = "HIDDEN_ONEDRIVE_URL";
-const char kUserEmailActionId[] = "HIDDEN_ONEDRIVE_USER_EMAIL";
-
// Either OneDrive for the Office PWA or Drive for Drive Web editing.
enum class CloudProvider {
kGoogleDrive,
@@ -130,6 +137,10 @@ class CloudOpenTask : public BrowserListObserver,
FRIEND_TEST_ALL_PREFIXES(::file_manager::file_tasks::OneDriveTest,
OpenFileNotFromODFS);
FRIEND_TEST_ALL_PREFIXES(::file_manager::file_tasks::OneDriveTest,
+ FailToOpenFileFromODFSReauthenticationRequired);
+ FRIEND_TEST_ALL_PREFIXES(::file_manager::file_tasks::OneDriveTest,
+ FailToOpenFileFromODFSOtherAccessError);
+ FRIEND_TEST_ALL_PREFIXES(::file_manager::file_tasks::OneDriveTest,
OpenFileFromAndroidOneDriveViaODFS);
FRIEND_TEST_ALL_PREFIXES(::file_manager::file_tasks::OneDriveTest,
FailToOpenFileFromAndroidOneDriveViaODFSDiffEmail);
@@ -154,19 +165,23 @@ class CloudOpenTask : public BrowserListObserver,
bool ExecuteInternal();
void OpenOrMoveFiles();
void OpenAlreadyHostedDriveUrls();
- void OpenODFSUrls();
+ void OpenODFSUrls(const OfficeTaskResult task_result_uma);
void OpenAndroidOneDriveUrlsIfAccountMatchedODFS();
- void CheckEmailAndOpenURLs(const std::string& android_onedrive_email,
- const file_system_provider::Actions& actions,
- base::File::Error result);
+ void CheckEmailAndOpenURLs(
+ const std::string& android_onedrive_email,
+ base::expected<cloud_upload::ODFSMetadata, base::File::Error>
+ metadata_or_error);
bool ShouldShowConfirmationDialog();
void ConfirmMoveOrStartUpload();
void StartUpload();
- void FinishedDriveUpload(const GURL& url, int64_t size);
+ // Callbacks from `DriveUploadHandler` and `OneDriveUploadHandler`. URL passed
+ // to these callbacks will be `absl::nullopt` and size will be 0 if upload
+ // fails.
+ void FinishedDriveUpload(absl::optional<GURL> url, int64_t size);
void FinishedOneDriveUpload(base::WeakPtr<Profile> profile_weak_ptr,
- const storage::FileSystemURL& url,
+ absl::optional<storage::FileSystemURL> url,
int64_t size);
bool InitAndShowDialog(mojom::DialogPage dialog_page);
@@ -199,7 +214,7 @@ class CloudOpenTask : public BrowserListObserver,
std::unique_ptr<std::vector<std::string>> mime_types);
void RecordUploadLatencyUMA();
- raw_ptr<Profile, ExperimentalAsh> profile_;
+ raw_ptr<Profile, DanglingUntriaged | ExperimentalAsh> profile_;
std::vector<storage::FileSystemURL> file_urls_;
CloudProvider cloud_provider_;
gfx::NativeWindow modal_parent_;
@@ -208,14 +223,11 @@ class CloudOpenTask : public BrowserListObserver,
raw_ptr<CloudUploadDialog, ExperimentalAsh> pending_dialog_ = nullptr;
base::ElapsedTimer upload_timer_;
int64_t upload_total_size_ = 0;
+ bool has_upload_errors_ = false;
OfficeFilesTransferRequired transfer_required_ =
OfficeFilesTransferRequired::kNotRequired;
};
-// Return True if feature `kUploadOfficeToCloud` is enabled and is eligible for
-// the user of the |profile|. A user is eligible if they are not managed.
-bool IsEligibleAndEnabledUploadOfficeToCloud(Profile* profile);
-
// Returns True if OneDrive is the selected `cloud_provider` but either ODFS
// is not mounted or the Office PWA is not installed. Returns False otherwise.
bool ShouldFixUpOffice(Profile* profile, const CloudProvider cloud_provider);
@@ -256,6 +268,9 @@ absl::optional<ODFSFileSystemAndPath> AndroidOneDriveUrlToODFS(
// need it.
bool ShowConnectOneDriveDialog(gfx::NativeWindow modal_parent);
+// Launches the setup flow to set up opening Office files in Microsoft 365.
+void LaunchMicrosoft365Setup(Profile* profile, gfx::NativeWindow modal_parent);
+
// Defines the web dialog used to help users upload Office files to the cloud.
class CloudUploadDialog : public SystemWebDialogDelegate {
public:
@@ -270,14 +285,6 @@ class CloudUploadDialog : public SystemWebDialogDelegate {
const mojom::DialogPage dialog_page,
bool office_move_confirmation_shown);
- // Request ODFS be mounted. If there is an existing mount, ODFS will unmount
- // that one after authentication of the new mount.
- static void RequestODFSMount(
- Profile* profile,
- file_system_provider::RequestMountCallback callback);
- static bool IsODFSMounted(Profile* profile);
- static bool IsOfficeWebAppInstalled(Profile* profile);
-
void OnDialogShown(content::WebUI* webui) override;
void OnDialogClosed(const std::string& json_retval) override;
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog_browsertest.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog_browsertest.cc
index 8f568c038de..d45bb7266a3 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog_browsertest.cc
@@ -93,7 +93,7 @@ void CreateFakeWebApps(
for (int i = 0; i < n; ++i) {
std::string start_url =
"https://www.example" + base::NumberToString(i) + ".com";
- auto web_app_info = std::make_unique<WebAppInstallInfo>();
+ auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
web_app_info->start_url = GURL(start_url);
web_app_info->scope = GURL(start_url);
apps::FileHandler handler;
@@ -411,8 +411,7 @@ IN_PROC_BROWSER_TEST_F(FileHandlerDialogBrowserTest, OpenFileTaskFromDialog) {
// Check QuickOffice was observed by the dialog as it should always be shown.
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
- ASSERT_TRUE(file_manager::file_tasks::IsExtensionInstalled(
- profile(), extension_misc::kQuickOfficeComponentExtensionId));
+ ASSERT_TRUE(file_manager::file_tasks::IsQuickOfficeInstalled(profile()));
ASSERT_GE(PositionInList(observed_app_ids,
extension_misc::kQuickOfficeComponentExtensionId),
0);
@@ -918,10 +917,10 @@ IN_PROC_BROWSER_TEST_F(FixUpFlowBrowserTest,
}
// Click through the Upload Page.
- while (
- !content::ExecJs(web_contents,
- "document.querySelector('cloud-upload').$('upload-page')"
- ".querySelector('.action-button').click()")) {
+ while (!content::ExecJs(
+ web_contents,
+ "document.querySelector('cloud-upload').$('complete-page')"
+ ".querySelector('.action-button').click()")) {
}
// Check that the Office PWA has been made the default for doc and xlsx files.
@@ -987,10 +986,10 @@ IN_PROC_BROWSER_TEST_F(FixUpFlowBrowserTest,
}
// Click through the Upload Page.
- while (
- !content::ExecJs(web_contents,
- "document.querySelector('cloud-upload').$('upload-page')"
- ".querySelector('.action-button').click()")) {
+ while (!content::ExecJs(
+ web_contents,
+ "document.querySelector('cloud-upload').$('complete-page')"
+ ".querySelector('.action-button').click()")) {
}
// Check that the default task for doc files is still Drive, and not OneDrive,
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc
index e2b711ee9c5..50276507d72 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc
@@ -7,7 +7,9 @@
#include "ash/public/cpp/notification_utils.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "base/check.h"
+#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
+#include "base/i18n/message_formatter.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
@@ -15,7 +17,8 @@
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/notifications/notification_display_service_factory.h"
#include "chrome/browser/platform_util.h"
-#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
+#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
@@ -43,13 +46,13 @@ CloudUploadNotificationManager::CloudUploadNotificationManager(
const std::string& cloud_provider_name,
const std::string& target_app_name,
int num_files,
- file_manager::io_task::OperationType operation_type)
+ UploadType upload_type)
: profile_(profile),
file_name_(file_name),
cloud_provider_name_(cloud_provider_name),
target_app_name_(target_app_name),
num_files_(num_files),
- operation_type_(operation_type) {
+ upload_type_(upload_type) {
// Generate a unique ID for the cloud upload notifications.
notification_id_ =
"cloud-upload-" +
@@ -79,46 +82,65 @@ CloudUploadNotificationManager::GetNotificationDisplayService() {
std::unique_ptr<message_center::Notification>
CloudUploadNotificationManager::CreateUploadProgressNotification() {
- std::string operation =
- operation_type_ == file_manager::io_task::OperationType::kCopy ? "Copying"
- : "Moving";
- std::string title =
- // TODO(b/242685536) Use "files" for multi-files when support for
- // multi-files is added.
- operation + " " + base::NumberToString(num_files_) + " file to " +
- cloud_provider_name_;
- std::string message = "File will open in " + target_app_name_;
-
- return ash::CreateSystemNotificationPtr(
+ bool is_copy_operation = upload_type_ == UploadType::kCopy;
+ // TODO(b/242685536) Use "files" for multi-files when support for
+ // multi-files is added.
+ std::u16string title = base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(is_copy_operation
+ ? IDS_OFFICE_NOTIFICATION_COPYING_FILES
+ : IDS_OFFICE_NOTIFICATION_MOVING_FILES),
+ num_files_, cloud_provider_name_);
+ std::u16string message = base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(IDS_OFFICE_NOTIFICATION_FILES_WILL_OPEN),
+ num_files_, target_app_name_);
+
+ auto notification = ash::CreateSystemNotificationPtr(
/*type=*/message_center::NOTIFICATION_TYPE_PROGRESS,
- /*id=*/notification_id_, base::UTF8ToUTF16(title),
+ /*id=*/notification_id_, title,
// TODO(b/272601262) Display or delete this message.
- base::UTF8ToUTF16(message), /*display_source=*/display_source_,
+ /*message=*/{}, /*display_source=*/display_source_,
/*origin_url=*/GURL(), /*notifier_id=*/message_center::NotifierId(),
/*optional_fields=*/{},
/*delegate=*/
base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
base::BindRepeating(
- &CloudUploadNotificationManager::CloseNotification,
+ &CloudUploadNotificationManager::HandleProgressNotificationClick,
weak_ptr_factory_.GetWeakPtr())),
/*small_image=*/ash::kFolderIcon,
/*warning_level=*/message_center::SystemNotificationWarningLevel::NORMAL);
+
+ // For a progress notification the message parameter won't be displayed in the
+ // notification. Therefore, its value is passed to progress_status which will
+ // be displayed.
+ notification->set_progress_status(message);
+
+ // Add "Cancel" button if upload still cancellable.
+ if (CanCancel() && cancel_callback_) {
+ std::vector<message_center::ButtonInfo> notification_buttons = {
+ message_center::ButtonInfo(
+ l10n_util::GetStringUTF16(IDS_FILE_BROWSER_CANCEL_LABEL))};
+ notification->set_buttons(notification_buttons);
+ }
+
+ return notification;
}
std::unique_ptr<message_center::Notification>
CloudUploadNotificationManager::CreateUploadCompleteNotification() {
+ bool is_copy_operation = upload_type_ == UploadType::kCopy;
// TODO(b/242685536) Use "files" for multi-files when support for multi-files
// is added.
- std::string operation =
- operation_type_ == file_manager::io_task::OperationType::kCopy ? "Copied"
- : "Moved";
- std::string title = operation + " " + base::NumberToString(num_files_) +
- " file to " + cloud_provider_name_;
- std::string message = "Opening in " + target_app_name_;
+ std::u16string title = base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(is_copy_operation
+ ? IDS_OFFICE_NOTIFICATION_FILES_COPIED
+ : IDS_OFFICE_NOTIFICATION_FILES_MOVED),
+ num_files_, cloud_provider_name_);
+ std::u16string message =
+ l10n_util::GetStringFUTF16(IDS_OFFICE_NOTIFICATION_FILES_OPENING,
+ base::UTF8ToUTF16(target_app_name_));
auto notification = ash::CreateSystemNotificationPtr(
/*type=*/message_center::NOTIFICATION_TYPE_SIMPLE,
- /*id=*/notification_id_, base::UTF8ToUTF16(title),
- base::UTF8ToUTF16(message),
+ /*id=*/notification_id_, title, message,
/*display_source=*/display_source_,
/*origin_url=*/GURL(), /*notifier_id=*/message_center::NotifierId(),
/*optional_fields=*/{},
@@ -134,9 +156,10 @@ CloudUploadNotificationManager::CreateUploadCompleteNotification() {
DCHECK(!destination_path_.empty());
if (!destination_path_.empty()) {
// Add "Show in folder" button.
- std::string button_title = "Show in folder";
+ std::u16string button_title = l10n_util::GetStringUTF16(
+ IDS_OFFICE_NOTIFICATION_SHOW_IN_FOLDER_BUTTON);
std::vector<message_center::ButtonInfo> notification_buttons = {
- message_center::ButtonInfo(base::UTF8ToUTF16(button_title))};
+ message_center::ButtonInfo(button_title)};
notification->set_buttons(notification_buttons);
}
return notification;
@@ -145,19 +168,17 @@ CloudUploadNotificationManager::CreateUploadCompleteNotification() {
std::unique_ptr<message_center::Notification>
CloudUploadNotificationManager::CreateUploadErrorNotification(
std::string message) {
- // TODO(b/254586358): i18n these strings.
- std::string operation =
- operation_type_ == file_manager::io_task::OperationType::kCopy ? "copy"
- : "move";
- std::string file_string = num_files_ == 1 ? "file" : "files";
- std::string title =
- "Can't " + operation + " " + file_string + " to " + cloud_provider_name_;
+ bool is_copy_operation = upload_type_ == UploadType::kCopy;
+ std::u16string title = base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(is_copy_operation
+ ? IDS_OFFICE_UPLOAD_ERROR_CANT_COPY_FILES
+ : IDS_OFFICE_UPLOAD_ERROR_CANT_MOVE_FILES),
+ num_files_, cloud_provider_name_);
std::vector<message_center::ButtonInfo> notification_buttons;
auto notification = ash::CreateSystemNotificationPtr(
/*type=*/message_center::NOTIFICATION_TYPE_SIMPLE,
- /*id=*/notification_id_, base::UTF8ToUTF16(title),
- base::UTF8ToUTF16(message),
+ /*id=*/notification_id_, title, base::UTF8ToUTF16(message),
/*display_source=*/display_source_,
/*origin_url=*/GURL(), /*notifier_id=*/message_center::NotifierId(),
/*optional_fields=*/{},
@@ -171,9 +192,10 @@ CloudUploadNotificationManager::CreateUploadErrorNotification(
message_center::SystemNotificationWarningLevel::WARNING);
// Add "Sign in" button if this is a reauthentication error.
- if (message == kReauthenticationRequiredMessage) {
- std::string button_title = "Sign in";
- notification_buttons.emplace_back(base::UTF8ToUTF16(button_title));
+ if (message == GetReauthenticationRequiredMessage()) {
+ std::u16string button_title =
+ l10n_util::GetStringUTF16(IDS_OFFICE_NOTIFICATION_SIGN_IN_BUTTON);
+ notification_buttons.emplace_back(button_title);
}
notification->set_buttons(notification_buttons);
@@ -181,9 +203,10 @@ CloudUploadNotificationManager::CreateUploadErrorNotification(
}
void CloudUploadNotificationManager::ShowUploadProgress(int progress) {
+ progress_ = progress;
std::unique_ptr<message_center::Notification> notification =
CreateUploadProgressNotification();
- notification->set_progress(progress);
+ notification->set_progress(progress_);
notification->set_never_timeout(true);
GetNotificationDisplayService()->Display(NotificationHandler::Type::TRANSIENT,
*notification,
@@ -205,6 +228,10 @@ void CloudUploadNotificationManager::ShowCompleteNotification() {
std::unique_ptr<message_center::Notification> notification =
CreateUploadCompleteNotification();
notification->set_never_timeout(true);
+ // Close the progress notification before displaying the completed
+ // notification.
+ GetNotificationDisplayService()->Close(NotificationHandler::Type::TRANSIENT,
+ notification_id_);
GetNotificationDisplayService()->Display(NotificationHandler::Type::TRANSIENT,
*notification,
/*metadata=*/nullptr);
@@ -221,6 +248,9 @@ void CloudUploadNotificationManager::MarkUploadComplete() {
// Check if the "in progress" timeout has happened yet or not.
if (state_ == State::kInProgress) {
state_ = State::kWaitingForInProgressTimeout;
+ // Re-show progress notification without the "Cancel" button as the upload
+ // has now finished.
+ ShowUploadProgress(progress_);
} else if (state_ == State::kUninitialized ||
state_ == State::kInProgressTimedOut) {
// If the complete notification is shown before any progress notifications,
@@ -259,14 +289,25 @@ void CloudUploadNotificationManager::CloseNotification() {
}
}
+void CloudUploadNotificationManager::HandleProgressNotificationClick(
+ absl::optional<int> button_index) {
+ // If the "Cancel" button was pressed, rather than a click to somewhere
+ // else in the notification.
+ if (button_index && cancel_callback_ && CanCancel()) {
+ // Cancel upload.
+ std::move(cancel_callback_).Run();
+ }
+
+ CloseNotification();
+}
+
void CloudUploadNotificationManager::HandleErrorNotificationClick(
absl::optional<int> button_index) {
// If the "Sign in" button was pressed, rather than a click to somewhere
// else in the notification.
if (button_index) {
- // TODO(b/282619291) decide what callback should be.
// Request an ODFS mount which will trigger reauthentication.
- CloudUploadDialog::RequestODFSMount(profile_, base::DoNothing());
+ RequestODFSMount(profile_, base::DoNothing());
}
CloseNotification();
@@ -285,6 +326,11 @@ void CloudUploadNotificationManager::HandleCompleteNotificationClick(
CloseNotification();
}
+bool CloudUploadNotificationManager::CanCancel() {
+ return state_ == State::kUninitialized || state_ == State::kInProgress ||
+ state_ == State::kInProgressTimedOut;
+}
+
void CloudUploadNotificationManager::CloseForTest() {
CloseNotification();
}
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h
index 0cf88e54adf..a81163c1190 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h
@@ -15,16 +15,13 @@
#include "chrome/browser/ash/file_manager/io_task.h"
#include "chrome/browser/notifications/notification_display_service.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
#include "ui/message_center/public/cpp/notification.h"
class Profile;
namespace ash::cloud_upload {
-// TODO(b/254586358): i18n this string.
-const char kReauthenticationRequiredMessage[] =
- "Sign in to your Microsoft account and then try again";
-
// Creates, updates and deletes cloud upload system notifications. Ensures that
// notifications stay in the "in progress" state for a minimum of 5 seconds, and
// a minimum of 5 seconds for the 'complete' state unless the user chooses to
@@ -36,13 +33,12 @@ class CloudUploadNotificationManager
using HandleCompleteNotificationClickCallback =
base::OnceCallback<void(base::FilePath)>;
- CloudUploadNotificationManager(
- Profile* profile,
- const std::string& file_name,
- const std::string& cloud_provider_name,
- const std::string& target_app_name,
- int num_files,
- file_manager::io_task::OperationType operation_type);
+ CloudUploadNotificationManager(Profile* profile,
+ const std::string& file_name,
+ const std::string& cloud_provider_name,
+ const std::string& target_app_name,
+ int num_files,
+ UploadType upload_type);
// Creates the notification with "in progress" state if it doesn't exist, or
// updates the progress bar if it does. |progress| is within the 0-100 range.
@@ -68,6 +64,10 @@ class CloudUploadNotificationManager
destination_path_ = destination_path;
}
+ void SetCancelCallback(base::OnceClosure cancel_callback) {
+ cancel_callback_ = std::move(cancel_callback);
+ }
+
// Used in tests to set a callback to check if
// |HandleCompleteNotificationClick| is called with the expected
// |destination_path_|.
@@ -106,6 +106,9 @@ class CloudUploadNotificationManager
// closed, timers are interrupted and the completion callback has been called.
void CloseNotification();
+ // "Cancel" click handler for upload progress notification.
+ void HandleProgressNotificationClick(absl::optional<int> button_index);
+
// "Sign in" click handler for authentication error notification.
void HandleErrorNotificationClick(absl::optional<int> button_index);
@@ -124,11 +127,14 @@ class CloudUploadNotificationManager
kComplete
};
+ // Returns true if upload is still in progress.
+ bool CanCancel();
+
// Counts the total number of notification manager instances. This counter is
// never decremented.
static inline int notification_manager_counter_ = 0;
- const raw_ptr<Profile, ExperimentalAsh> profile_;
+ const raw_ptr<Profile, LeakedDanglingUntriaged | ExperimentalAsh> profile_;
CloudProvider provider_;
std::string file_name_;
std::string cloud_provider_name_;
@@ -136,9 +142,11 @@ class CloudUploadNotificationManager
std::string target_app_name_;
std::u16string display_source_;
int num_files_;
- file_manager::io_task::OperationType operation_type_;
+ int progress_;
+ UploadType upload_type_;
base::FilePath destination_path_;
base::OnceClosure callback_;
+ base::OnceClosure cancel_callback_;
HandleCompleteNotificationClickCallback callback_for_testing_;
base::OneShotTimer in_progress_timer_;
base::OneShotTimer complete_notification_timer_;
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager_unittest.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager_unittest.cc
index db2833bf71f..e27dd5ab230 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager_unittest.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h"
#include "base/files/file_path.h"
+#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/time/time.h"
@@ -63,6 +64,16 @@ class CloudUploadNotificationManagerTest : public testing::Test {
IDS_ASH_MESSAGE_CENTER_SYSTEM_APP_NAME_FILES);
}
+ bool HaveMoveProgressNotificationWithCancelButton() {
+ return HaveMoveProgressNotification() &&
+ !notification()->buttons().empty() &&
+ (notification()->buttons().front().title == u"Cancel");
+ }
+
+ bool HaveMoveProgressNotificationWithoutCancelButton() {
+ return HaveMoveProgressNotification() && notification()->buttons().empty();
+ }
+
bool HaveCopyProgressNotification() {
return notification().has_value() &&
notification()->type() ==
@@ -130,7 +141,7 @@ TEST_F(CloudUploadNotificationManagerTest,
scoped_refptr<CloudUploadNotificationManager> manager =
base::MakeRefCounted<CloudUploadNotificationManager>(
profile(), file_name_, "Google Drive", "Google Docs", 1,
- file_manager::io_task::OperationType::kMove);
+ UploadType::kMove);
ASSERT_EQ(absl::nullopt, notification());
manager->ShowUploadProgress(1);
@@ -144,7 +155,7 @@ TEST_F(CloudUploadNotificationManagerTest,
scoped_refptr<CloudUploadNotificationManager> manager =
base::MakeRefCounted<CloudUploadNotificationManager>(
profile(), file_name_, "Google Drive", "Google Docs", 1,
- file_manager::io_task::OperationType::kCopy);
+ UploadType::kCopy);
ASSERT_EQ(absl::nullopt, notification());
manager->ShowUploadProgress(1);
@@ -157,7 +168,7 @@ TEST_F(CloudUploadNotificationManagerTest, MinimumTimingForMove) {
scoped_refptr<CloudUploadNotificationManager> manager =
base::MakeRefCounted<CloudUploadNotificationManager>(
profile(), file_name_, "Google Drive", "Google Docs", 1,
- file_manager::io_task::OperationType::kMove);
+ UploadType::kMove);
manager->ShowUploadProgress(1);
manager->ShowUploadProgress(100);
@@ -186,7 +197,7 @@ TEST_F(CloudUploadNotificationManagerTest, MinimumTimingForCopy) {
scoped_refptr<CloudUploadNotificationManager> manager =
base::MakeRefCounted<CloudUploadNotificationManager>(
profile(), file_name_, "Google Drive", "Google Docs", 1,
- file_manager::io_task::OperationType::kCopy);
+ UploadType::kCopy);
manager->ShowUploadProgress(1);
manager->ShowUploadProgress(100);
@@ -215,7 +226,7 @@ TEST_F(CloudUploadNotificationManagerTest, CompleteWithoutProgress) {
scoped_refptr<CloudUploadNotificationManager> manager =
base::MakeRefCounted<CloudUploadNotificationManager>(
profile(), file_name_, "Google Drive", "Google Docs", 1,
- file_manager::io_task::OperationType::kMove);
+ UploadType::kMove);
manager->SetDestinationPath(file_path_);
manager->MarkUploadComplete();
@@ -230,11 +241,72 @@ TEST_F(CloudUploadNotificationManagerTest, CompleteWithoutProgress) {
ASSERT_EQ(absl::nullopt, notification());
}
+TEST_F(CloudUploadNotificationManagerTest, CancelClick) {
+ base::RunLoop run_loop;
+ base::OnceClosure cancel_callback = run_loop.QuitClosure();
+
+ scoped_refptr<CloudUploadNotificationManager> manager =
+ base::MakeRefCounted<CloudUploadNotificationManager>(
+ profile(), file_name_, "Google Drive", "Google Docs", 1,
+ UploadType::kMove);
+
+ manager->SetCancelCallback(std::move(cancel_callback));
+ manager->ShowUploadProgress(1);
+ ASSERT_TRUE(HaveMoveProgressNotificationWithCancelButton());
+
+ // Click "Cancel" button (0th button) which triggers |cancel_callback|.
+ display_service_->SimulateClick(NotificationHandler::Type::TRANSIENT,
+ notification()->id(), /*action_index=*/0,
+ absl::nullopt);
+
+ // Run loop until |cancel_callback| is called.
+ run_loop.Run();
+ manager->CloseForTest();
+}
+
+TEST_F(CloudUploadNotificationManagerTest,
+ CancelButtonDisappearsAfterProgressComplete) {
+ scoped_refptr<CloudUploadNotificationManager> manager =
+ base::MakeRefCounted<CloudUploadNotificationManager>(
+ profile(), file_name_, "Google Drive", "Google Docs", 1,
+ UploadType::kMove);
+
+ // TODO(b/244396230): remove CancelCallback once button always set for both
+ // Clouds.
+ manager->SetCancelCallback(base::DoNothing());
+ manager->ShowUploadProgress(1);
+ ASSERT_TRUE(HaveMoveProgressNotificationWithCancelButton());
+ manager->MarkUploadComplete();
+ ASSERT_TRUE(HaveMoveProgressNotificationWithoutCancelButton());
+ manager->CloseForTest();
+}
+
+TEST_F(CloudUploadNotificationManagerTest,
+ CancelButtonRemainsAfterMinimumTime) {
+ scoped_refptr<CloudUploadNotificationManager> manager =
+ base::MakeRefCounted<CloudUploadNotificationManager>(
+ profile(), file_name_, "Google Drive", "Google Docs", 1,
+ UploadType::kMove);
+
+ // TODO(b/244396230): remove CancelCallback once button always set for both
+ // Clouds.
+ manager->SetCancelCallback(base::DoNothing());
+ manager->ShowUploadProgress(1);
+ ASSERT_TRUE(HaveMoveProgressNotificationWithCancelButton());
+
+ // The Cancel button should still appear after the minimum progress
+ // notification time of 5s.
+ task_environment_.FastForwardBy(base::Milliseconds(6000));
+ ASSERT_TRUE(HaveMoveProgressNotificationWithCancelButton());
+
+ manager->CloseForTest();
+}
+
TEST_F(CloudUploadNotificationManagerTest, ShowInFolderClick) {
scoped_refptr<CloudUploadNotificationManager> manager =
base::MakeRefCounted<CloudUploadNotificationManager>(
profile(), file_name_, "Google Drive", "Google Docs", 1,
- file_manager::io_task::OperationType::kMove);
+ UploadType::kMove);
manager->SetDestinationPath(file_path_);
manager->MarkUploadComplete();
@@ -257,13 +329,14 @@ TEST_F(CloudUploadNotificationManagerTest, ShowInFolderClick) {
// Run loop until |HandleNotificationClick| is called.
run_loop.Run();
+ manager->CloseForTest();
}
TEST_F(CloudUploadNotificationManagerTest, ErrorStaysOpenForMove) {
scoped_refptr<CloudUploadNotificationManager> manager =
base::MakeRefCounted<CloudUploadNotificationManager>(
profile(), file_name_, "Google Drive", "Google Docs", 1,
- file_manager::io_task::OperationType::kMove);
+ UploadType::kMove);
manager->ShowUploadProgress(1);
manager->ShowUploadProgress(100);
@@ -282,7 +355,7 @@ TEST_F(CloudUploadNotificationManagerTest, ErrorStaysOpenForCopy) {
scoped_refptr<CloudUploadNotificationManager> manager =
base::MakeRefCounted<CloudUploadNotificationManager>(
profile(), file_name_, "Google Drive", "Google Docs", 1,
- file_manager::io_task::OperationType::kCopy);
+ UploadType::kCopy);
manager->ShowUploadProgress(1);
manager->ShowUploadProgress(100);
@@ -302,7 +375,7 @@ TEST_F(CloudUploadNotificationManagerTest, ManagerLifetime) {
scoped_refptr<CloudUploadNotificationManager> manager =
base::MakeRefCounted<CloudUploadNotificationManager>(
profile(), file_name_, "Google Drive", "Google Docs", 1,
- file_manager::io_task::OperationType::kMove);
+ UploadType::kMove);
manager->ShowUploadProgress(1);
manager->ShowUploadError("error");
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.cc
index 82800862281..cc8357c42d1 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.cc
@@ -14,7 +14,7 @@
#include "chrome/browser/ash/file_manager/file_tasks.h"
#include "chrome/browser/chromeos/office_web_app/office_web_app.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom.h"
-#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "components/webapps/browser/install_result_code.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
@@ -54,7 +54,7 @@ void CloudUploadPageHandler::GetDialogArgs(GetDialogArgsCallback callback) {
void CloudUploadPageHandler::IsOfficeWebAppInstalled(
IsOfficeWebAppInstalledCallback callback) {
- std::move(callback).Run(CloudUploadDialog::IsOfficeWebAppInstalled(profile_));
+ std::move(callback).Run(ash::cloud_upload::IsOfficeWebAppInstalled(profile_));
}
void CloudUploadPageHandler::InstallOfficeWebApp(
@@ -87,13 +87,19 @@ void CloudUploadPageHandler::InstallOfficeWebApp(
void CloudUploadPageHandler::IsODFSMounted(IsODFSMountedCallback callback) {
// Assume any file system mounted by ODFS is the correct one.
- std::move(callback).Run(CloudUploadDialog::IsODFSMounted(profile_));
+ std::move(callback).Run(ash::cloud_upload::IsODFSMounted(profile_));
}
void CloudUploadPageHandler::SignInToOneDrive(
SignInToOneDriveCallback callback) {
web_ui_->GetWebContents()->GetTopLevelNativeWindow()->Hide();
- CloudUploadDialog::RequestODFSMount(
+ if (!odfs_mount_called_) {
+ // Log only once per setup flow.
+ odfs_mount_called_ = true;
+ UMA_HISTOGRAM_BOOLEAN("FileBrowser.OfficeFiles.Setup.ODFSAvailability",
+ IsODFSInstalled(profile_));
+ }
+ RequestODFSMount(
profile_,
base::BindOnce(&CloudUploadPageHandler::OnMountResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
@@ -115,7 +121,6 @@ void CloudUploadPageHandler::RespondWithLocalTaskAndClose(int task_position) {
void CloudUploadPageHandler::SetOfficeAsDefaultHandler() {
using file_manager::file_tasks::kActionIdOpenInOffice;
- // TODO(b:275912658): Only set handlers if that group has no type set already
file_manager::file_tasks::SetWordFileHandlerToFilesSWA(profile_,
kActionIdOpenInOffice);
file_manager::file_tasks::SetExcelFileHandlerToFilesSWA(
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h
index 9da6be5061d..e1351e58b80 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h
@@ -75,6 +75,7 @@ class CloudUploadPageHandler : public mojom::PageHandler {
raw_ptr<Profile, ExperimentalAsh> profile_;
raw_ptr<content::WebUI, ExperimentalAsh> web_ui_;
mojom::DialogArgsPtr dialog_args_;
+ bool odfs_mount_called_ = false;
mojo::Receiver<PageHandler> receiver_;
RespondWithUserActionAndCloseCallback user_action_callback_;
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.cc
index 826b1993b7c..c3d74aa9c00 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.cc
@@ -4,8 +4,10 @@
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/chromeos/upload_office_to_cloud/upload_office_to_cloud.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
#include "chrome/browser/ui/webui/webui_util.h"
@@ -17,13 +19,14 @@
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/l10n/l10n_util.h"
#include "ui/webui/color_change_listener/color_change_handler.h"
namespace ash::cloud_upload {
bool CloudUploadUIConfig::IsWebUIEnabled(
content::BrowserContext* browser_context) {
- return cloud_upload::IsEligibleAndEnabledUploadOfficeToCloud(
+ return chromeos::IsEligibleAndEnabledUploadOfficeToCloud(
Profile::FromBrowserContext(browser_context));
}
@@ -33,17 +36,30 @@ CloudUploadUI::CloudUploadUI(content::WebUI* web_ui)
Profile::FromWebUI(web_ui), chrome::kChromeUICloudUploadHost);
static constexpr webui::LocalizedString kStrings[] = {
+ // Dialog buttons.
{"cancel", IDS_CANCEL},
{"close", IDS_CLOSE},
+ {"done", IDS_DONE},
{"open", IDS_OFFICE_FILE_HANDLER_OPEN_BUTTON},
+ {"install", IDS_INSTALL},
+ {"installing", IDS_OFFICE_INSTALL_PWA_INSTALLING_BUTTON},
+ {"installed", IDS_OFFICE_INSTALL_PWA_INSTALLED_BUTTON},
+ {"cancelSetup", IDS_OFFICE_CANCEL_SETUP_CANCEL_BUTTON},
+ {"continueSetup", IDS_OFFICE_CANCEL_SETUP_CONTINUE_BUTTON},
+ {"animationPlayText", IDS_OOBE_PLAY_ANIMATION_MESSAGE},
+ {"animationPauseText", IDS_OOBE_PAUSE_ANIMATION_MESSAGE},
+ {"moveAndOpen", IDS_OFFICE_MOVE_CONFIRMATION_MOVE_BUTTON},
+ {"copyAndOpen", IDS_OFFICE_MOVE_CONFIRMATION_COPY_BUTTON},
+ // Connect To OneDrive dialog.
{"connectToOneDriveTitle", IDS_CONNECT_TO_ONEDRIVE_TITLE},
+ {"connectToOneDriveSignInFlowBodyText",
+ IDS_CONNECT_TO_ONEDRIVE_SIGNIN_FLOW_BODY_TEXT},
{"connectToOneDriveBodyText", IDS_CONNECT_TO_ONEDRIVE_BODY_TEXT},
{"cantConnectOneDrive", IDS_CANT_CONNECT_ONEDRIVE},
{"connectOneDrive", IDS_CONNECT_ONEDRIVE},
{"oneDriveConnectedTitle", IDS_ONEDRIVE_CONNECTED_TITLE},
{"oneDriveConnectedBodyText", IDS_ONEDRIVE_CONNECTED_BODY_TEXT},
- {"animationPlayText", IDS_OOBE_PLAY_ANIMATION_MESSAGE},
- {"animationPauseText", IDS_OOBE_PAUSE_ANIMATION_MESSAGE},
+ // File Handler selection dialog.
{"fileHandlerTitle", IDS_OFFICE_FILE_HANDLER_TITLE},
{"word", IDS_OFFICE_FILE_HANDLER_FILE_TYPE_WORD},
{"excel", IDS_OFFICE_FILE_HANDLER_FILE_TYPE_EXCEL},
@@ -54,16 +70,50 @@ CloudUploadUI::CloudUploadUI(content::WebUI* web_ui)
{"microsoft365", IDS_OFFICE_FILE_HANDLER_APP_MICROSOFT},
{"otherApps", IDS_OFFICE_FILE_HANDLER_APP_OTHERS},
{"googleDriveStorage", IDS_OFFICE_FILE_HANDLER_STORAGE_GOOGLE},
- {"oneDriveStorage", IDS_OFFICE_FILE_HANDLER_STORAGE_MICROSOFT}};
+ {"oneDriveStorage", IDS_OFFICE_FILE_HANDLER_STORAGE_MICROSOFT},
+ // Install PWA dialog.
+ {"installPWATitle", IDS_OFFICE_INSTALL_PWA_TITLE},
+ {"installPWABodyText", IDS_OFFICE_INSTALL_PWA_BODY_TEXT},
+ // Cancel setup dialog.
+ {"cancelSetupTitle", IDS_OFFICE_CANCEL_SETUP_TITLE},
+ {"cancelSetupBodyText", IDS_OFFICE_CANCEL_SETUP_BODY_TEXT},
+ // OneDrive setup complete dialog.
+ {"oneDriveSetupCompleteTitle", IDS_OFFICE_ONEDRIVE_SETUP_COMPLETE_TITLE},
+ {"oneDriveSetupCompleteBodyText",
+ IDS_OFFICE_ONEDRIVE_SETUP_COMPLETE_BODY_TEXT},
+ // Welcome dialog.
+ {"welcomeBodyText", IDS_OFFICE_WELCOME_BODY_TEXT},
+ {"welcomeGetStarted", IDS_OFFICE_WELCOME_GET_STARTED},
+ {"welcomeInstallOdfs", IDS_OFFICE_WELCOME_CONNECT_ONEDRIVE},
+ {"welcomeInstallOfficeWebApp", IDS_OFFICE_WELCOME_INSTALL_MICROSOFT365},
+ {"welcomeMoveFiles", IDS_OFFICE_WELCOME_FILES_WILL_MOVE},
+ {"welcomeSetUp", IDS_OFFICE_WELCOME_SET_UP},
+ {"welcomeTitle", IDS_OFFICE_WELCOME_TITLE},
+ // Copy/Move confirmation dialog.
+ {"moveConfirmationMoveTitle", IDS_OFFICE_MOVE_CONFIRMATION_MOVE_TITLE},
+ {"moveConfirmationMoveTitlePlural",
+ IDS_OFFICE_MOVE_CONFIRMATION_MOVE_TITLE_PLURAL},
+ {"moveConfirmationCopyTitle", IDS_OFFICE_MOVE_CONFIRMATION_COPY_TITLE},
+ {"moveConfirmationCopyTitlePlural",
+ IDS_OFFICE_MOVE_CONFIRMATION_COPY_TITLE_PLURAL},
+ {"moveConfirmationOneDriveBodyText",
+ IDS_OFFICE_MOVE_CONFIRMATION_ONEDRIVE_BODY_TEXT},
+ {"moveConfirmationGoogleDriveBodyText",
+ IDS_OFFICE_MOVE_CONFIRMATION_GOOGLE_DRIVE_BODY_TEXT},
+ {"moveConfirmationAlwaysMove",
+ IDS_OFFICE_MOVE_CONFIRMATION_ALWAYS_MOVE_CHECKBOX},
+ {"oneDrive", IDS_OFFICE_CLOUD_PROVIDER_ONEDRIVE},
+ {"googleDrive", IDS_OFFICE_CLOUD_PROVIDER_GOOGLE_DRIVE},
+ };
source->AddLocalizedStrings(kStrings);
source->AddBoolean("isJellyEnabled", chromeos::features::IsJellyEnabled());
webui::SetupWebUIDataSource(
source, base::make_span(kCloudUploadResources, kCloudUploadResourcesSize),
IDR_CLOUD_UPLOAD_MAIN_HTML);
- // Required for lottie animations.
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::WorkerSrc,
"worker-src blob: chrome://resources 'self';");
+ ash::EnableTrustedTypesCSP(source);
}
CloudUploadUI::~CloudUploadUI() = default;
@@ -105,6 +155,12 @@ void CloudUploadUI::RespondWithUserActionAndCloseDialog(
case mojom::UserAction::kCancel:
args.Append(kUserActionCancel);
break;
+ case mojom::UserAction::kCancelGoogleDrive:
+ args.Append(kUserActionCancelGoogleDrive);
+ break;
+ case mojom::UserAction::kCancelOneDrive:
+ args.Append(kUserActionCancelOneDrive);
+ break;
case mojom::UserAction::kSetUpOneDrive:
args.Append(kUserActionSetUpOneDrive);
break;
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc
index a7692a4910a..f2794c1260a 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc
@@ -4,16 +4,44 @@
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
#include "base/files/file_path.h"
+#include "base/functional/bind.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/file_manager/io_task.h"
#include "chrome/browser/ash/file_manager/volume.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
#include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/ash/file_system_provider/service.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
+#include "chrome/browser/web_applications/web_app_id_constants.h"
#include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/services/app_service/public/cpp/types_util.h"
#include "content/public/browser/browser_thread.h"
+#include "ui/base/l10n/l10n_util.h"
namespace ash::cloud_upload {
+namespace {
+
+using file_system_provider::Action;
+using file_system_provider::Actions;
+using file_system_provider::ProvidedFileSystemInfo;
+using file_system_provider::ProvidedFileSystemInterface;
+using file_system_provider::ProviderId;
+using file_system_provider::Service;
+
+} // namespace
+
+std::string GetGenericErrorMessage() {
+ return l10n_util::GetStringUTF8(IDS_OFFICE_UPLOAD_ERROR_GENERIC);
+}
+
+std::string GetReauthenticationRequiredMessage() {
+ return l10n_util::GetStringUTF8(
+ IDS_OFFICE_UPLOAD_ERROR_REAUTHENTICATION_REQUIRED);
+}
storage::FileSystemURL FilePathToFileSystemURL(
Profile* profile,
@@ -75,13 +103,132 @@ SourceType GetSourceType(Profile* profile,
return SourceType::LOCAL;
}
-file_manager::io_task::OperationType GetOperationTypeForUpload(
- Profile* profile,
- const storage::FileSystemURL& source_url) {
+UploadType GetUploadType(Profile* profile,
+ const storage::FileSystemURL& source_url) {
SourceType source_type = GetSourceType(profile, source_url);
- return source_type == SourceType::LOCAL
- ? file_manager::io_task::OperationType::kMove
- : file_manager::io_task::OperationType::kCopy;
+ return source_type == SourceType::LOCAL ? UploadType::kMove
+ : UploadType::kCopy;
+}
+
+void RequestODFSMount(Profile* profile,
+ file_system_provider::RequestMountCallback callback) {
+ Service* service = Service::Get(profile);
+ ProviderId provider_id =
+ ProviderId::CreateFromExtensionId(extension_misc::kODFSExtensionId);
+ auto logging_callback = base::BindOnce(
+ [](file_system_provider::RequestMountCallback callback,
+ base::File::Error error) {
+ if (error != base::File::FILE_OK) {
+ LOG(ERROR) << "RequestMount: " << base::File::ErrorToString(error);
+ }
+ std::move(callback).Run(error);
+ },
+ std::move(callback));
+ service->RequestMount(provider_id, std::move(logging_callback));
+}
+
+absl::optional<ProvidedFileSystemInfo> GetODFSInfo(Profile* profile) {
+ Service* service = Service::Get(profile);
+ ProviderId provider_id =
+ ProviderId::CreateFromExtensionId(extension_misc::kODFSExtensionId);
+ auto odfs_infos = service->GetProvidedFileSystemInfoList(provider_id);
+
+ if (odfs_infos.size() == 0) {
+ LOG(ERROR) << "ODFS is not mounted";
+ return absl::nullopt;
+ }
+ if (odfs_infos.size() > 1u) {
+ LOG(ERROR) << "One and only one filesystem should be mounted for the ODFS "
+ "extension";
+ return absl::nullopt;
+ }
+
+ return odfs_infos[0];
+}
+
+ProvidedFileSystemInterface* GetODFS(Profile* profile) {
+ Service* service = Service::Get(profile);
+ ProviderId provider_id =
+ ProviderId::CreateFromExtensionId(extension_misc::kODFSExtensionId);
+ auto odfs_info = GetODFSInfo(profile);
+ if (!odfs_info) {
+ return nullptr;
+ }
+ return service->GetProvidedFileSystem(provider_id,
+ odfs_info->file_system_id());
+}
+
+bool IsODFSInstalled(Profile* profile) {
+ auto* service = ash::file_system_provider::Service::Get(profile);
+ for (const auto& [provider_id, provider] : service->GetProviders()) {
+ if (provider_id.GetType() ==
+ ash::file_system_provider::ProviderId::EXTENSION &&
+ provider_id.GetExtensionId() == extension_misc::kODFSExtensionId) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IsODFSMounted(Profile* profile) {
+ // Assume any file system mounted by ODFS is the correct one.
+ return GetODFSInfo(profile).has_value();
+}
+
+bool IsOfficeWebAppInstalled(Profile* profile) {
+ if (!apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) {
+ return false;
+ }
+ auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+ bool installed = false;
+ proxy->AppRegistryCache().ForOneApp(
+ web_app::kMicrosoft365AppId, [&installed](const apps::AppUpdate& update) {
+ installed = apps_util::IsInstalled(update.Readiness());
+ });
+ return installed;
+}
+
+// Convert |actions| to |ODFSMetadata| and pass the result to |callback|.
+// The action id's for the metadata are HIDDEN_ONEDRIVE_USER_EMAIL and
+// HIDDEN_ONEDRIVE_REAUTHENTICATION_REQUIRED.
+void OnODFSMetadataActions(GetODFSMetadataCallback callback,
+ const Actions& actions,
+ base::File::Error result) {
+ if (result != base::File::Error::FILE_OK) {
+ LOG(ERROR) << "Unexpectedly failed to get ODFS metadata actions as these "
+ "should always be returned: "
+ << result;
+ std::move(callback).Run(base::unexpected(result));
+ return;
+ }
+ ODFSMetadata metadata;
+ for (const Action& action : actions) {
+ if (action.id == kReauthenticationRequiredId) {
+ metadata.reauthentication_required = action.title == "true";
+ } else if (action.id == kUserEmailActionId) {
+ metadata.user_email = action.title;
+ }
+ }
+ std::move(callback).Run(metadata);
+}
+
+void GetODFSMetadata(ProvidedFileSystemInterface* file_system,
+ GetODFSMetadataCallback callback) {
+ file_system->GetActions(
+ {base::FilePath(cloud_upload::kODFSMetadataQueryPath)},
+ base::BindOnce(&OnODFSMetadataActions, std::move(callback)));
+}
+
+absl::optional<base::File::Error> GetFirstTaskError(
+ const ::file_manager::io_task::ProgressStatus& status) {
+ for (const auto* entries : {&status.sources, &status.outputs}) {
+ for (const ::file_manager::io_task::EntryStatus& entry : *entries) {
+ if (entry.error && *entry.error != base::File::Error::FILE_OK) {
+ return entry.error;
+ }
+ }
+ }
+ return absl::nullopt;
}
} // namespace ash::cloud_upload
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
index ccac9f5aa63..3a6860c329e 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
@@ -5,18 +5,31 @@
#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_
#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_
+#include <string>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "chrome/browser/ash/file_manager/io_task.h"
+#include "chrome/browser/ash/file_system_provider/provided_file_system_interface.h"
+#include "chrome/browser/ash/file_system_provider/provider_interface.h"
#include "chrome/browser/platform_util.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
class Profile;
namespace ash::cloud_upload {
+struct ODFSMetadata {
+ bool reauthentication_required = false;
+ std::string user_email;
+};
+
+typedef base::OnceCallback<void(
+ base::expected<ODFSMetadata, base::File::Error> metadata_or_error)>
+ GetODFSMetadataCallback;
+
// Type of the source location from which a given file is being uploaded.
enum class SourceType {
LOCAL = 0,
@@ -25,10 +38,53 @@ enum class SourceType {
kMaxValue = CLOUD,
};
+// Type of upload of a file to the Cloud.
+enum class UploadType {
+ kCopy = 0,
+ kMove = 1,
+ kMaxValue = kMove,
+};
+
+constexpr char kGoogleDriveTaskResultMetricName[] =
+ "FileBrowser.OfficeFiles.TaskResult.Drive";
+constexpr char kOneDriveTaskResultMetricName[] =
+ "FileBrowser.OfficeFiles.TaskResult.OneDrive";
+constexpr char kGoogleDriveUploadResultMetricName[] =
+ "FileBrowser.OfficeFiles.Open.UploadResult.GoogleDrive";
+constexpr char kOneDriveUploadResultMetricName[] =
+ "FileBrowser.OfficeFiles.Open.UploadResult.OneDrive";
+
+constexpr char kGoogleDriveMoveErrorMetricName[] =
+ "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Move";
+constexpr char kGoogleDriveCopyErrorMetricName[] =
+ "FileBrowser.OfficeFiles.Open.IOTaskError.GoogleDrive.Copy";
+constexpr char kOneDriveMoveErrorMetricName[] =
+ "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Move";
+constexpr char kOneDriveCopyErrorMetricName[] =
+ "FileBrowser.OfficeFiles.Open.IOTaskError.OneDrive.Copy";
+
+// List of UMA enum value for Web Drive Office task results. The enum values
+// must be kept in sync with OfficeTaskResult in
+// tools/metrics/histograms/enums.xml.
+enum class OfficeTaskResult {
+ kFallbackQuickOffice = 0,
+ kFallbackOther = 1,
+ kOpened = 2,
+ kMoved = 3,
+ kCancelled = 4,
+ kFailedToUpload = 5,
+ kFailedToOpen = 6,
+ kCopied = 7,
+ kMaxValue = kCopied,
+};
+
// The result of the "Upload to cloud" workflow for Office files.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
+//
+// The enum values must be kept in sync with OfficeFilesUploadResult in
+// tools/metrics/histograms/enums.xml.
enum class OfficeFilesUploadResult {
kSuccess = 0,
kOtherError = 1,
@@ -44,10 +100,27 @@ enum class OfficeFilesUploadResult {
kCloudMetadataError = 11,
kCloudQuotaFull = 12,
kCloudError = 13,
- kMaxValue = kCloudError,
+ kNoConnection = 14,
+ kDestinationUrlError = 15,
+ kInvalidURL = 16,
+ kMaxValue = kInvalidURL,
};
-const char kGenericErrorMessage[] = "Something went wrong. Try again.";
+// Query actions for this path to get ODFS Metadata.
+const char kODFSMetadataQueryPath[] = "/";
+
+// Custom action ids passed from ODFS.
+const char kOneDriveUrlActionId[] = "HIDDEN_ONEDRIVE_URL";
+const char kUserEmailActionId[] = "HIDDEN_ONEDRIVE_USER_EMAIL";
+const char kReauthenticationRequiredId[] =
+ "HIDDEN_ONEDRIVE_REAUTHENTICATION_REQUIRED";
+
+// Get generic error message for uploading office files.
+std::string GetGenericErrorMessage();
+// Get Microsoft authentication error message for uploading office files.
+std::string GetReauthenticationRequiredMessage();
+// Get access denied error message.
+std::string GetGenericOneDriveAccessErrorMessage();
// Converts an absolute FilePath into a filesystem URL.
storage::FileSystemURL FilePathToFileSystemURL(
@@ -67,11 +140,38 @@ void CreateDirectoryOnIOThread(
SourceType GetSourceType(Profile* profile,
const storage::FileSystemURL& source_path);
-// Returns the operation type (move or copy) for the upload flow based on the
+// Returns the upload type (move or copy) for the upload flow based on the
// source path of the file to upload.
-::file_manager::io_task::OperationType GetOperationTypeForUpload(
- Profile* profile,
- const storage::FileSystemURL& source_path);
+UploadType GetUploadType(Profile* profile,
+ const storage::FileSystemURL& source_path);
+
+// Request ODFS be mounted. If there is an existing mount, ODFS will unmount
+// that one after authentication of the new mount.
+void RequestODFSMount(Profile* profile,
+ file_system_provider::RequestMountCallback callback);
+
+// Get information of the currently provided ODFS. Expect there to be exactly
+// one ODFS.
+absl::optional<file_system_provider::ProvidedFileSystemInfo> GetODFSInfo(
+ Profile* profile);
+
+// Get currently provided ODFS, or null if not mounted.
+file_system_provider::ProvidedFileSystemInterface* GetODFS(Profile* profile);
+
+bool IsODFSMounted(Profile* profile);
+bool IsODFSInstalled(Profile* profile);
+bool IsOfficeWebAppInstalled(Profile* profile);
+
+// Get ODFS metadata as actions by doing a special GetActions request (for the
+// root directory) and return the actions to |OnODFSMetadataActions| which will
+// be converted to |ODFSMetadata| and passed to |callback|.
+void GetODFSMetadata(
+ file_system_provider::ProvidedFileSystemInterface* file_system,
+ GetODFSMetadataCallback callback);
+
+// Get the first task error that is not `base::File::Error::FILE_OK`.
+absl::optional<base::File::Error> GetFirstTaskError(
+ const ::file_manager::io_task::ProgressStatus& status);
} // namespace ash::cloud_upload
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
index 0288d989d29..66143a82041 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
@@ -6,19 +6,28 @@
#include "base/check_op.h"
#include "base/files/file_path.h"
+#include "base/functional/callback_forward.h"
+#include "base/i18n/message_formatter.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
+#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/file_manager/copy_or_move_io_task.h"
+#include "chrome/browser/ash/file_manager/delete_io_task.h"
#include "chrome/browser/ash/file_manager/file_tasks.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/file_manager/io_task.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
+#include "chrome/grit/generated_resources.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "google_apis/common/task_util.h"
+#include "ui/base/l10n/l10n_util.h"
using storage::FileSystemURL;
@@ -34,35 +43,29 @@ const int kAlternateUrlTimeout = 15;
// alternate URL.
const int kAlternateUrlPollInterval = 200;
-constexpr char kUploadResultMetricName[] =
- "FileBrowser.OfficeFiles.Open.UploadResult.GoogleDrive";
-
-constexpr char kSpaceExceededErrorMessage[] =
- "Free up space in Drive to move this file";
-
// Runs the callback provided to `DriveUploadHandler::Upload`.
void OnUploadDone(scoped_refptr<DriveUploadHandler> drive_upload_handler,
DriveUploadHandler::UploadCallback callback,
- const GURL& hosted_url,
+ absl::optional<GURL> hosted_url,
int64_t upload_size) {
- std::move(callback).Run(hosted_url, upload_size);
+ std::move(callback).Run(std::move(hosted_url), upload_size);
}
std::string GetTargetAppName(base::FilePath file_path) {
const std::string extension = base::ToLowerASCII(file_path.FinalExtension());
if (base::Contains(file_manager::file_tasks::WordGroupExtensions(),
extension)) {
- return "Google Docs";
+ return l10n_util::GetStringUTF8(IDS_OFFICE_FILE_HANDLER_APP_GOOGLE_DOCS);
}
if (base::Contains(file_manager::file_tasks::ExcelGroupExtensions(),
extension)) {
- return "Google Sheets";
+ return l10n_util::GetStringUTF8(IDS_OFFICE_FILE_HANDLER_APP_GOOGLE_SHEETS);
}
if (base::Contains(file_manager::file_tasks::PowerPointGroupExtensions(),
extension)) {
- return "Google Slides";
+ return l10n_util::GetStringUTF8(IDS_OFFICE_FILE_HANDLER_APP_GOOGLE_SLIDES);
}
- return "Google Docs";
+ return l10n_util::GetStringUTF8(IDS_OFFICE_FILE_HANDLER_APP_GOOGLE_DOCS);
}
} // namespace
@@ -85,17 +88,19 @@ DriveUploadHandler::DriveUploadHandler(Profile* profile,
file_manager::util::GetFileManagerFileSystemContext(profile)),
drive_integration_service_(
drive::DriveIntegrationServiceFactory::FindForProfile(profile)),
+ upload_type_(GetUploadType(profile, source_url)),
notification_manager_(
base::MakeRefCounted<CloudUploadNotificationManager>(
profile,
source_url.path().BaseName().value(),
- "Google Drive",
+ l10n_util::GetStringUTF8(IDS_OFFICE_CLOUD_PROVIDER_GOOGLE_DRIVE),
GetTargetAppName(source_url.path()),
// TODO(b/242685536) Update when support for multi-files is added.
/*num_files=*/1,
- GetOperationTypeForUpload(profile, source_url))),
+ upload_type_)),
source_url_(source_url) {
- observed_task_id_ = -1;
+ observed_copy_task_id_ = -1;
+ observed_delete_task_id_ = -1;
}
DriveUploadHandler::~DriveUploadHandler() {
@@ -106,6 +111,7 @@ DriveUploadHandler::~DriveUploadHandler() {
// Stop observing Drive updates.
if (drive_integration_service_) {
+ drive_integration_service_->RemoveObserver(this);
drive_integration_service_->GetDriveFsHost()->RemoveObserver(this);
}
}
@@ -117,8 +123,8 @@ void DriveUploadHandler::Run(UploadCallback callback) {
if (!profile_) {
LOG(ERROR) << "No profile";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kOtherError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kOtherError);
return;
}
@@ -126,22 +132,30 @@ void DriveUploadHandler::Run(UploadCallback callback) {
file_manager::VolumeManager::Get(profile_);
if (!volume_manager) {
LOG(ERROR) << "No volume manager";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kOtherError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kOtherError);
return;
}
io_task_controller_ = volume_manager->io_task_controller();
if (!io_task_controller_) {
LOG(ERROR) << "No task_controller";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kOtherError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kOtherError);
return;
}
if (!drive_integration_service_) {
LOG(ERROR) << "No Drive integration service";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kOtherError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kOtherError);
+ return;
+ }
+
+ if (drive::util::GetDriveConnectionStatus(profile_) !=
+ drive::util::ConnectionStatus::kConnected) {
+ LOG(ERROR) << "No connection to Drive";
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kNoConnection);
return;
}
@@ -149,12 +163,13 @@ void DriveUploadHandler::Run(UploadCallback callback) {
io_task_controller_->AddObserver(this);
// Observe Drive updates.
+ drive_integration_service_->AddObserver(this);
drive_integration_service_->GetDriveFsHost()->AddObserver(this);
if (!drive_integration_service_->IsMounted()) {
LOG(ERROR) << "Google Drive is not mounted";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kFileSystemNotFound,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kFileSystemNotFound);
return;
}
@@ -166,21 +181,20 @@ void DriveUploadHandler::Run(UploadCallback callback) {
// TODO (b/243095484) Define error behavior.
if (!destination_folder_url.is_valid()) {
LOG(ERROR) << "Unable to generate destination folder Drive URL";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kFileSystemNotFound,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kFileSystemNotFound);
return;
}
- const file_manager::io_task::OperationType operation_type =
- GetOperationTypeForUpload(profile_, source_url_);
std::vector<FileSystemURL> source_urls{source_url_};
- std::unique_ptr<file_manager::io_task::IOTask> task =
+ // Always use a copy task. Will convert to a move upon success.
+ std::unique_ptr<file_manager::io_task::IOTask> copy_task =
std::make_unique<file_manager::io_task::CopyOrMoveIOTask>(
- operation_type, std::move(source_urls),
+ file_manager::io_task::OperationType::kCopy, std::move(source_urls),
std::move(destination_folder_url), profile_, file_system_context_,
/*show_notification=*/false);
- observed_task_id_ = io_task_controller_->Add(std::move(task));
+ observed_copy_task_id_ = io_task_controller_->Add(std::move(copy_task));
}
void DriveUploadHandler::UpdateProgressNotification() {
@@ -190,33 +204,98 @@ void DriveUploadHandler::UpdateProgressNotification() {
notification_manager_->ShowUploadProgress(progress);
}
-void DriveUploadHandler::OnEndUpload(GURL hosted_url,
- OfficeFilesUploadResult result,
- std::string error_message) {
- UMA_HISTOGRAM_ENUMERATION(kUploadResultMetricName, result);
+void DriveUploadHandler::OnEndCopy(base::expected<GURL, std::string> hosted_url,
+ OfficeFilesUploadResult result_metric) {
+ if (copy_ended_) {
+ // Prevent loops in case Copy IO task and Drive sync fail separately.
+ return;
+ }
+ copy_ended_ = true;
+
+ // If copy to Drive was successful and intended operation is a copy, no delete
+ // is required.
+ if (hosted_url.has_value() && upload_type_ == UploadType::kCopy) {
+ OnEndUpload(hosted_url, result_metric);
+ return;
+ }
+
+ // If destination file doesn't exist, no delete is required.
+ base::FilePath rel_path;
+ bool destination_file_exists =
+ !observed_absolute_dest_path_.empty() &&
+ drive_integration_service_->GetRelativeDrivePath(
+ observed_absolute_dest_path_, &rel_path);
+ if (!destination_file_exists) {
+ OnEndUpload(hosted_url, result_metric);
+ return;
+ }
+
+ end_upload_callback_ =
+ base::BindOnce(&DriveUploadHandler::OnEndUpload,
+ weak_ptr_factory_.GetWeakPtr(), hosted_url, result_metric);
+
+ std::vector<FileSystemURL> file_urls;
+ if (hosted_url.has_value()) {
+ // If copy to Drive was successful, delete source file to convert the upload
+ // to a move to Drive.
+ file_urls.push_back(source_url_);
+ } else {
+ // If copy to Drive was unsuccessful, delete destination file to undo the
+ // copy to Drive.
+ FileSystemURL dest_url = FilePathToFileSystemURL(
+ profile_, file_system_context_, observed_absolute_dest_path_);
+ file_urls.push_back(dest_url);
+ }
+
+ std::unique_ptr<file_manager::io_task::IOTask> task =
+ std::make_unique<file_manager::io_task::DeleteIOTask>(
+ std::move(file_urls), file_system_context_,
+ /*show_notification=*/false);
+ observed_delete_task_id_ = io_task_controller_->Add(std::move(task));
+}
+
+void DriveUploadHandler::OnEndUpload(
+ base::expected<GURL, std::string> hosted_url,
+ OfficeFilesUploadResult result_metric) {
+ UMA_HISTOGRAM_ENUMERATION(kGoogleDriveUploadResultMetricName, result_metric);
// TODO (b/243095484) Define error behavior on invalid hosted URL.
observed_relative_drive_path_.clear();
// Stop suppressing Drive events for the observed file.
scoped_suppress_drive_notifications_for_path_.reset();
- // Resolve notifications.
- if (notification_manager_) {
- if (hosted_url.is_valid()) {
+ if (hosted_url.has_value()) {
+ // Resolve notifications.
+ if (notification_manager_) {
notification_manager_->MarkUploadComplete();
- } else if (!error_message.empty()) {
- LOG(ERROR) << "Cloud upload: " << error_message;
+ }
+ if (callback_) {
+ std::move(callback_).Run(hosted_url.value(), upload_size_);
+ }
+ } else {
+ if (const std::string& error_message = hosted_url.error();
+ notification_manager_ && !error_message.empty()) {
+ LOG(ERROR) << "Upload to Google Drive: " << error_message;
notification_manager_->ShowUploadError(error_message);
}
- }
- if (callback_) {
- std::move(callback_).Run(hosted_url, upload_size_);
+ if (callback_) {
+ std::move(callback_).Run(absl::nullopt, 0);
+ }
}
}
void DriveUploadHandler::OnIOTaskStatus(
const file_manager::io_task::ProgressStatus& status) {
- if (status.task_id != observed_task_id_) {
+ if (status.task_id == observed_copy_task_id_) {
+ OnCopyStatus(status);
+ return;
+ }
+ if (status.task_id == observed_delete_task_id_) {
+ OnDeleteStatus(status);
return;
}
+}
+
+void DriveUploadHandler::OnCopyStatus(
+ const ::file_manager::io_task::ProgressStatus& status) {
switch (status.state) {
case file_manager::io_task::State::kScanning:
// TODO(crbug.com/1361915): Potentially adapt to show scanning.
@@ -235,8 +314,8 @@ void DriveUploadHandler::OnIOTaskStatus(
if (!drive_integration_service_) {
LOG(ERROR) << "No Drive integration service";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kOtherError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kOtherError);
return;
}
@@ -244,8 +323,9 @@ void DriveUploadHandler::OnIOTaskStatus(
// destination file name is not known in advance, given that it's
// generated from the IOTaskController which resolves potential name
// clashes.
+ observed_absolute_dest_path_ = status.outputs[0].url.path();
drive_integration_service_->GetRelativeDrivePath(
- status.outputs[0].url.path(), &observed_relative_drive_path_);
+ observed_absolute_dest_path_, &observed_relative_drive_path_);
scoped_suppress_drive_notifications_for_path_ = std::make_unique<
file_manager::ScopedSuppressDriveNotificationsForPath>(
profile_, observed_relative_drive_path_);
@@ -260,16 +340,17 @@ void DriveUploadHandler::OnIOTaskStatus(
DCHECK_EQ(status.outputs.size(), 1u);
return;
case file_manager::io_task::State::kCancelled:
- if (status.type == file_manager::io_task::OperationType::kCopy) {
- OnEndUpload(GURL(), OfficeFilesUploadResult::kCopyOperationCancelled,
- kGenericErrorMessage);
+ LOG(ERROR) << "Upload to Google Drive cancelled";
+ if (upload_type_ == UploadType::kCopy) {
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kCopyOperationCancelled);
} else {
- OnEndUpload(GURL(), OfficeFilesUploadResult::kMoveOperationCancelled,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kMoveOperationCancelled);
}
return;
case file_manager::io_task::State::kError:
- ConvertFileErrorToUploadError(status);
+ ShowIOTaskError(status);
return;
case file_manager::io_task::State::kNeedPassword:
NOTREACHED() << "Encrypted file should not need password to be copied or "
@@ -278,30 +359,51 @@ void DriveUploadHandler::OnIOTaskStatus(
}
}
-void DriveUploadHandler::ConvertFileErrorToUploadError(
+void DriveUploadHandler::OnDeleteStatus(
+ const ::file_manager::io_task::ProgressStatus& status) {
+ switch (status.state) {
+ case file_manager::io_task::State::kCancelled:
+ NOTREACHED() << "Deletion of source or destination file should not have "
+ "been cancelled.";
+ ABSL_FALLTHROUGH_INTENDED;
+ case file_manager::io_task::State::kError:
+ case file_manager::io_task::State::kSuccess:
+ std::move(end_upload_callback_).Run();
+ return;
+ default:
+ return;
+ }
+}
+
+void DriveUploadHandler::ShowIOTaskError(
const file_manager::io_task::ProgressStatus& status) {
OfficeFilesUploadResult upload_result;
std::string error_message;
- bool copy = status.type == file_manager::io_task::OperationType::kCopy;
- std::string operation = copy ? "copied" : "moved";
+ bool copy = upload_type_ == UploadType::kCopy;
- base::File::Error file_error = base::File::FILE_ERROR_FAILED;
// TODO(b/242685536) Find most relevant error in a multi-file upload when
// support for multi-files is added.
- // Find the first not base::File::Error::FILE_OK.
- if (status.sources.size() > 0 && status.sources[0].error.has_value() &&
- status.sources[0].error.value() != base::File::Error::FILE_OK) {
- file_error = status.sources[0].error.value();
- } else if (status.outputs.size() > 0 && status.outputs[0].error.has_value()) {
- file_error = status.outputs[0].error.value();
- }
+ base::File::Error file_error =
+ GetFirstTaskError(status).value_or(base::File::FILE_ERROR_FAILED);
+
+ base::UmaHistogramExactLinear(
+ copy ? kGoogleDriveCopyErrorMetricName : kGoogleDriveMoveErrorMetricName,
+ -file_error, -base::File::FILE_ERROR_MAX);
switch (file_error) {
case base::File::FILE_ERROR_NO_SPACE:
upload_result = OfficeFilesUploadResult::kCloudQuotaFull;
// TODO(b/242685536) Use "these files" for multi-files when support for
// multi-files is added.
- error_message = kSpaceExceededErrorMessage;
+ error_message = base::UTF16ToUTF8(
+ base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(
+ copy ? IDS_OFFICE_UPLOAD_ERROR_FREE_UP_SPACE_TO_COPY
+ : IDS_OFFICE_UPLOAD_ERROR_FREE_UP_SPACE_TO_MOVE),
+ // TODO(b/242685536) Update when support for multi-files is added.
+ 1,
+ l10n_util::GetStringUTF16(
+ IDS_OFFICE_CLOUD_PROVIDER_GOOGLE_DRIVE_SHORT)));
break;
case base::File::FILE_ERROR_NOT_FOUND:
if (copy) {
@@ -309,8 +411,9 @@ void DriveUploadHandler::ConvertFileErrorToUploadError(
} else {
upload_result = OfficeFilesUploadResult::kMoveOperationError;
}
- error_message =
- "The file could not be " + operation + " because it no longer exists";
+ error_message = l10n_util::GetStringUTF8(
+ copy ? IDS_OFFICE_UPLOAD_ERROR_FILE_NOT_EXIST_TO_COPY
+ : IDS_OFFICE_UPLOAD_ERROR_FILE_NOT_EXIST_TO_MOVE);
break;
default:
if (copy) {
@@ -318,10 +421,11 @@ void DriveUploadHandler::ConvertFileErrorToUploadError(
} else {
upload_result = OfficeFilesUploadResult::kMoveOperationError;
}
- error_message = kGenericErrorMessage;
+ LOG(ERROR) << "IO Task error";
+ error_message = GetGenericErrorMessage();
}
- OnEndUpload(GURL(), upload_result, error_message);
+ OnEndCopy(base::unexpected(error_message), upload_result);
}
void DriveUploadHandler::OnUnmounted() {}
@@ -368,13 +472,13 @@ void DriveUploadHandler::OnSyncingStatusUpdate(
return;
case drivefs::mojom::ItemEvent::State::kFailed:
LOG(ERROR) << "Drive sync error";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kCloudError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kCloudError);
return;
default:
LOG(ERROR) << "Drive sync error + invalid sync state";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kCloudError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kCloudError);
return;
}
}
@@ -384,20 +488,42 @@ void DriveUploadHandler::OnError(const drivefs::mojom::DriveError& error) {
if (base::FilePath(error.path) != observed_relative_drive_path_) {
return;
}
+ bool copy = upload_type_ == UploadType::kCopy;
switch (error.type) {
case drivefs::mojom::DriveError::Type::kCantUploadStorageFull:
case drivefs::mojom::DriveError::Type::kCantUploadStorageFullOrganization:
case drivefs::mojom::DriveError::Type::kCantUploadSharedDriveStorageFull:
- OnEndUpload(GURL(), OfficeFilesUploadResult::kCloudQuotaFull,
- kSpaceExceededErrorMessage);
+ OnEndCopy(
+ base::unexpected(base::UTF16ToUTF8(
+ base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(
+ copy ? IDS_OFFICE_UPLOAD_ERROR_FREE_UP_SPACE_TO_COPY
+ : IDS_OFFICE_UPLOAD_ERROR_FREE_UP_SPACE_TO_MOVE),
+ // TODO(b/242685536) Update when support for
+ // multi-files is added.
+ 1,
+ l10n_util::GetStringUTF16(
+ IDS_OFFICE_CLOUD_PROVIDER_GOOGLE_DRIVE_SHORT)))),
+ OfficeFilesUploadResult::kCloudQuotaFull);
break;
case drivefs::mojom::DriveError::Type::kPinningFailedDiskFull:
- OnEndUpload(GURL(), OfficeFilesUploadResult::kPinningFailedDiskFull,
- kGenericErrorMessage);
+ LOG(ERROR) << "Pinning failed, disk full";
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kPinningFailedDiskFull);
break;
default:
- OnEndUpload(GURL(), OfficeFilesUploadResult::kCloudError,
- kGenericErrorMessage);
+ LOG(ERROR) << "Cloud error";
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kCloudError);
+ }
+}
+
+void DriveUploadHandler::OnDriveConnectionStatusChanged(
+ drive::util::ConnectionStatus status) {
+ if (status != drive::util::ConnectionStatus::kConnected) {
+ LOG(ERROR) << "Lost connection to Drive during upload";
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kNoConnection);
}
}
@@ -408,8 +534,8 @@ void DriveUploadHandler::OnGetDriveMetadata(
if (error != drive::FILE_ERROR_OK) {
if (timed_out) {
LOG(ERROR) << "Drive Metadata error";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kCloudMetadataError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kCloudMetadataError);
} else {
alternate_url_poll_timer_.Start(
FROM_HERE, base::Milliseconds(kAlternateUrlPollInterval),
@@ -422,8 +548,8 @@ void DriveUploadHandler::OnGetDriveMetadata(
if (!hosted_url.is_valid()) {
if (timed_out) {
LOG(ERROR) << "Invalid alternate URL - Drive editing unavailable";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kCloudMetadataError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kCloudMetadataError);
} else {
alternate_url_poll_timer_.Start(
FROM_HERE, base::Milliseconds(kAlternateUrlPollInterval),
@@ -438,8 +564,8 @@ void DriveUploadHandler::OnGetDriveMetadata(
if (hosted_url.host() != "docs.google.com") {
if (timed_out) {
LOG(ERROR) << "Unexpected alternate URL - Drive editing unavailable";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kCloudMetadataError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kCloudMetadataError);
} else {
alternate_url_poll_timer_.Start(
FROM_HERE, base::Milliseconds(kAlternateUrlPollInterval),
@@ -452,14 +578,14 @@ void DriveUploadHandler::OnGetDriveMetadata(
// Success.
alternate_url_timeout_.Stop();
alternate_url_poll_timer_.Stop();
- OnEndUpload(hosted_url, OfficeFilesUploadResult::kSuccess);
+ OnEndCopy(hosted_url, OfficeFilesUploadResult::kSuccess);
}
void DriveUploadHandler::CheckAlternateUrl(bool timed_out) {
if (!drive_integration_service_) {
LOG(ERROR) << "No Drive integration service";
- OnEndUpload(GURL(), OfficeFilesUploadResult::kOtherError,
- kGenericErrorMessage);
+ OnEndCopy(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kOtherError);
return;
}
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h b/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h
index 0725c34a446..b885e20a4ab 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h
@@ -6,13 +6,16 @@
#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_DRIVE_UPLOAD_HANDLER_H_
#include <memory>
+#include <string>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/types/expected.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
+#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/extensions/file_manager/scoped_suppress_drive_notifications_for_path.h"
#include "chrome/browser/ash/file_manager/io_task_controller.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h"
@@ -21,6 +24,7 @@
#include "chromeos/ash/components/drivefs/mojom/drivefs.mojom.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
class Profile;
@@ -35,9 +39,11 @@ namespace ash::cloud_upload {
class DriveUploadHandler
: public ::file_manager::io_task::IOTaskController::Observer,
public drivefs::DriveFsHostObserver,
+ public drive::DriveIntegrationServiceObserver,
public base::RefCounted<DriveUploadHandler> {
public:
- using UploadCallback = base::OnceCallback<void(const GURL&, int64_t)>;
+ using UploadCallback =
+ base::OnceCallback<void(absl::optional<GURL>, int64_t)>;
// Starts the upload workflow for the file specified at construct time.
static void Upload(Profile* profile,
@@ -52,28 +58,47 @@ class DriveUploadHandler
DriveUploadHandler(Profile* profile, const storage::FileSystemURL source_url);
~DriveUploadHandler() override;
- // Starts the upload workflow. Initiated by the `UploadToCloud` static method.
+ // Starts the upload workflow:
+ // - Copy the file via an IO task.
+ // - Sync to Drive.
+ // - Remove the source file in case of a move operation. Move mode of the
+ // `CopyOrMoveIOTask` is not used because the source file should only be
+ // deleted at the end of the sync operation.
+ // Initiated by the `Upload` static method.
void Run(UploadCallback callback);
- // Updates the progress notification for the upload workflow (move + syncing).
+ // Updates the progress notification for the upload workflow (copy + syncing).
void UpdateProgressNotification();
- // Ends upload and runs Upload callback.
- void OnEndUpload(GURL hosted_url,
- OfficeFilesUploadResult result,
- std::string error_message = "");
+ // Called upon a copy to Drive success or failure. If required, complete or
+ // undo the operation. Then call |OnEndUpload| to end the upload.
+ void OnEndCopy(base::expected<GURL, std::string> hosted_url,
+ OfficeFilesUploadResult result_metric);
+
+ // Ends the upload by showing any complete or error notifications. Runs the
+ // upload callback.
+ void OnEndUpload(base::expected<GURL, std::string> hosted_url,
+ OfficeFilesUploadResult result_metric);
// Callback for when ImmediatelyUpload() is called on DriveFS.
void ImmediatelyUploadDone(drive::FileError error);
- // IOTaskController::Observer:
+ // Directs IO task status updates to |OnCopyStatus| or |OnDeleteStatus| based
+ // on task id.
void OnIOTaskStatus(
const ::file_manager::io_task::ProgressStatus& status) override;
- // Find base::File::Error error returned by the IO Task and convert them to an
- // appropriate error notification.
- void ConvertFileErrorToUploadError(
- const file_manager::io_task::ProgressStatus& status);
+ // Observes copy to Drive IO task status updates. Calls |OnEndCopy| upon any
+ // error.
+ void OnCopyStatus(const ::file_manager::io_task::ProgressStatus& status);
+
+ // Observes delete IO task status updates from the delete task for cleaning up
+ // the source file. Calls `OnEndUpload` once the delete is finished.
+ void OnDeleteStatus(const ::file_manager::io_task::ProgressStatus& status);
+
+ // Find the base::File::Error error returned by the IO Task and convert it to
+ // an appropriate error notification.
+ void ShowIOTaskError(const file_manager::io_task::ProgressStatus& status);
// DriveFsHostObserver:
void OnUnmounted() override;
@@ -81,6 +106,9 @@ class DriveUploadHandler
const drivefs::mojom::SyncingStatus& status) override;
void OnError(const drivefs::mojom::DriveError& error) override;
+ void OnDriveConnectionStatusChanged(
+ drive::util::ConnectionStatus status) override;
+
// Checks the alternate URL from the request file's metadata.
void OnGetDriveMetadata(bool timed_out,
drive::FileError error,
@@ -96,14 +124,19 @@ class DriveUploadHandler
io_task_controller_;
const raw_ptr<drive::DriveIntegrationService, ExperimentalAsh>
drive_integration_service_;
+ const UploadType upload_type_;
scoped_refptr<CloudUploadNotificationManager> notification_manager_;
const storage::FileSystemURL source_url_;
- ::file_manager::io_task::IOTaskId observed_task_id_;
+ ::file_manager::io_task::IOTaskId observed_copy_task_id_;
+ ::file_manager::io_task::IOTaskId observed_delete_task_id_;
+ base::FilePath observed_absolute_dest_path_;
base::FilePath observed_relative_drive_path_;
+ bool copy_ended_ = false;
int move_progress_ = 0;
int sync_progress_ = 0;
base::OneShotTimer alternate_url_timeout_;
base::OneShotTimer alternate_url_poll_timer_;
+ base::OnceClosure end_upload_callback_;
UploadCallback callback_;
// Total size (in bytes) required to upload.
int64_t upload_size_ = 0;
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc
index 61e96a562db..067a795a6b3 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,12 +6,17 @@
#include "base/files/file_util.h"
#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
-#include "chrome/browser/ash/drive/drivefs_test_support.h"
+#include "chrome/browser/ash/drive/file_system_util.h"
+#include "chrome/browser/ash/file_manager/file_manager_test_util.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
+#include "chrome/browser/ash/file_manager/io_task.h"
#include "chrome/browser/ash/file_manager/io_task_controller.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
@@ -21,8 +26,11 @@
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/ash/components/drivefs/mojom/drivefs.mojom.h"
#include "chromeos/constants/chromeos_features.h"
+#include "components/drive/file_errors.h"
#include "content/public/test/browser_test.h"
+#include "content/public/test/network_connection_change_simulator.h"
#include "mojo/public/cpp/bindings/remote.h"
+#include "net/base/network_change_notifier.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/file_system/file_system_url.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -31,7 +39,11 @@ using storage::FileSystemURL;
namespace ash::cloud_upload {
+using ::base::test::RunClosure;
using ::base::test::RunOnceCallback;
+using drive::DriveIntegrationService;
+using drive::util::ConnectionStatus;
+using drive::util::SetDriveConnectionStatusForTesting;
using testing::_;
namespace {
@@ -65,6 +77,8 @@ class DriveUploadHandlerTest
drive_root_dir_ = drive_mount_point_.AppendASCII("root");
my_files_dir_ = temp_dir_.GetPath().Append("myfiles");
read_only_dir_ = temp_dir_.GetPath().Append("readonly");
+
+ net::NetworkChangeNotifier::SetTestNotificationsOnly(true);
}
DriveUploadHandlerTest(const DriveUploadHandlerTest&) = delete;
@@ -80,20 +94,29 @@ class DriveUploadHandlerTest
&create_drive_integration_service_);
}
+ void SetUpOnMainThread() override {
+ SetDriveConnectionStatusForTesting(ConnectionStatus::kConnected);
+ InProcessBrowserTest::SetUpOnMainThread();
+ }
+
void TearDown() override {
InProcessBrowserTest::TearDown();
storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
}
- drive::DriveIntegrationService* CreateDriveIntegrationService(
- Profile* profile) {
+ void TearDownOnMainThread() override {
+ RemoveObservers();
+ InProcessBrowserTest::TearDownOnMainThread();
+ }
+
+ DriveIntegrationService* CreateDriveIntegrationService(Profile* profile) {
base::ScopedAllowBlockingForTesting allow_blocking;
fake_drivefs_helpers_[profile] =
- std::make_unique<drive::FakeDriveFsHelper>(profile, drive_mount_point_);
- auto* integration_service = new drive::DriveIntegrationService(
+ std::make_unique<file_manager::test::FakeSimpleDriveFsHelper>(
+ profile, drive_mount_point_);
+ return new DriveIntegrationService(
profile, "", drive_mount_point_,
fake_drivefs_helpers_[profile]->CreateFakeDriveFsListenerFactory());
- return integration_service;
}
// Creates mount point for My files and registers local filesystem.
@@ -131,37 +154,58 @@ class DriveUploadHandlerTest
ash::DeviceType::kUnknown, /*read_only=*/true);
}
- void SetUpSourceFile(const std::string& test_file_name,
- base::FilePath source_path) {
+ void SetUpDrive() {
+ // Create Drive root directory.
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ EXPECT_TRUE(base::CreateDirectory(drive_root_dir_));
+ }
+ }
+
+ // Create and add a file with |test_file_name| to the file system
+ // |source_path|. Return the created |source_file_url|.
+ FileSystemURL SetUpSourceFile(const std::string& test_file_name,
+ base::FilePath source_path) {
test_file_name_ = test_file_name;
- source_file_path_ = source_path.AppendASCII(test_file_name);
+ source_file_path_ = source_path.AppendASCII(test_file_name_);
const base::FilePath test_file_path = GetTestFilePath(test_file_name_);
{
base::ScopedAllowBlockingForTesting allow_blocking;
CHECK(base::CopyFile(test_file_path, source_file_path_));
}
+
+ // Check that the source file exists at the intended source location and is
+ // not in Drive.
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ EXPECT_TRUE(base::PathExists(source_path.AppendASCII(test_file_name)));
+ CheckPathNotFoundOnDrive(observed_relative_drive_path());
+ }
+
+ FileSystemURL source_file_url = FilePathToFileSystemURL(
+ profile(),
+ file_manager::util::GetFileManagerFileSystemContext(profile()),
+ source_file_path_);
+
+ return source_file_url;
}
- // Starts the upload flow.
- void InitiateUpload() {
+ void SetUpObservers() {
// Subscribe to IOTasks updates to track the copy/move to Drive progress.
file_manager::VolumeManager::Get(profile())
->io_task_controller()
->AddObserver(this);
+ }
- FileSystemURL source_file_url = FilePathToFileSystemURL(
- profile(),
- file_manager::util::GetFileManagerFileSystemContext(profile()),
- source_file_path_);
- DriveUploadHandler::Upload(
- profile(), source_file_url,
- base::BindOnce(&DriveUploadHandlerTest::OnUploadDone,
- base::Unretained(this)));
+ void RemoveObservers() {
+ file_manager::VolumeManager::Get(profile())
+ ->io_task_controller()
+ ->RemoveObserver(this);
}
// Resolves once the `OnUploadDone` callback is called with a valid URL, which
// indicates the successful completion of the upload flow.
- void WaitForUploadComplete() {
+ void Wait() {
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_FALSE(run_loop_);
run_loop_ = std::make_unique<base::RunLoop>();
@@ -169,6 +213,51 @@ class DriveUploadHandlerTest
run_loop_ = nullptr;
}
+ void EndWait() {
+ ASSERT_TRUE(run_loop_);
+ run_loop_->Quit();
+ }
+
+ void CheckPathExistsOnDrive(const base::FilePath& path) {
+ drive_integration_service()->GetDriveFsInterface()->GetMetadata(
+ path,
+ base::BindOnce(&DriveUploadHandlerTest::OnGetMetadataExpectSuccess,
+ base::Unretained(this)));
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ Wait();
+ }
+
+ void CheckPathNotFoundOnDrive(const base::FilePath& path) {
+ drive_integration_service()->GetDriveFsInterface()->GetMetadata(
+ path,
+ base::BindOnce(&DriveUploadHandlerTest::OnGetMetadataExpectNotFound,
+ base::Unretained(this)));
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ Wait();
+ }
+
+ void OnGetMetadataExpectSuccess(drive::FileError error,
+ drivefs::mojom::FileMetadataPtr metadata) {
+ EXPECT_EQ(drive::FILE_ERROR_OK, error);
+ EndWait();
+ }
+
+ void OnGetMetadataExpectNotFound(drive::FileError error,
+ drivefs::mojom::FileMetadataPtr metadata) {
+ EXPECT_EQ(drive::FILE_ERROR_NOT_FOUND, error);
+ EndWait();
+ }
+
+ // `Wait` will not complete until this is called.
+ void OnUploadDone(absl::optional<GURL> url, int64_t size) {
+ if (fail_sync_) {
+ ASSERT_FALSE(url);
+ } else {
+ ASSERT_TRUE(url);
+ }
+ EndWait();
+ }
+
Profile* profile() { return browser()->profile(); }
const base::FilePath source_file_path() { return source_file_path_; }
@@ -177,18 +266,20 @@ class DriveUploadHandlerTest
return fake_drivefs().delegate();
}
+ DriveIntegrationService* drive_integration_service() {
+ return drive::DriveIntegrationServiceFactory::FindForProfile(profile());
+ }
+
base::FilePath observed_relative_drive_path() {
- drive::DriveIntegrationService* drive_integration_service =
- drive::DriveIntegrationServiceFactory::FindForProfile(profile());
base::FilePath observed_relative_drive_path;
- drive_integration_service->GetRelativeDrivePath(
+ drive_integration_service()->GetRelativeDrivePath(
drive_root_dir_.AppendASCII(test_file_name_),
&observed_relative_drive_path);
return observed_relative_drive_path;
}
protected:
- drivefs::FakeDriveFs& fake_drivefs() {
+ file_manager::test::FakeSimpleDriveFs& fake_drivefs() {
return fake_drivefs_helpers_[profile()]->fake_drivefs();
}
@@ -197,14 +288,26 @@ class DriveUploadHandlerTest
base::FilePath drive_mount_point_;
base::FilePath drive_root_dir_;
+ bool fail_sync_ = false;
+ // Overrides `fail_sync_`
+ base::RepeatingClosure on_transfer_complete_callback_;
+
private:
// IOTaskController::Observer:
void OnIOTaskStatus(
const file_manager::io_task::ProgressStatus& status) override {
- if (status.sources.size() == 1 &&
+ // Wait for the copy task to complete before starting the Drive sync.
+ if (status.type == file_manager::io_task::OperationType::kCopy &&
+ status.sources.size() == 1 &&
status.sources[0].url.path() == source_file_path_ &&
status.state == file_manager::io_task::State::kSuccess) {
- SimulateDriveUploadEvents();
+ if (on_transfer_complete_callback_) {
+ on_transfer_complete_callback_.Run();
+ } else if (fail_sync_) {
+ SimulateDriveUploadFailure();
+ } else {
+ SimulateDriveUploadEvents();
+ }
}
}
@@ -243,12 +346,25 @@ class DriveUploadHandlerTest
drivefs_delegate().FlushForTesting();
}
- // The exit point of the test. `WaitForUploadComplete` will not complete until
- // this is called.
- void OnUploadDone(const GURL& url, int64_t size) {
- ASSERT_FALSE(url.is_empty());
- ASSERT_TRUE(run_loop_);
- run_loop_->Quit();
+ void SimulateDriveUploadFailure() {
+ // Simulate server sync events.
+ drivefs::mojom::SyncingStatusPtr status =
+ drivefs::mojom::SyncingStatus::New();
+ status->item_events.emplace_back(
+ absl::in_place, 12, 34, observed_relative_drive_path().value(),
+ drivefs::mojom::ItemEvent::State::kQueued, 123, 456,
+ drivefs::mojom::ItemEventReason::kTransfer);
+ drivefs_delegate()->OnSyncingStatusUpdate(status.Clone());
+ drivefs_delegate().FlushForTesting();
+
+ drivefs::mojom::SyncingStatusPtr fail_status =
+ drivefs::mojom::SyncingStatus::New();
+ fail_status->item_events.emplace_back(
+ absl::in_place, 12, 34, observed_relative_drive_path().value(),
+ drivefs::mojom::ItemEvent::State::kFailed, 123, 456,
+ drivefs::mojom::ItemEventReason::kTransfer);
+ drivefs_delegate()->OnSyncingStatusUpdate(fail_status->Clone());
+ drivefs_delegate().FlushForTesting();
}
base::test::ScopedFeatureList feature_list_;
@@ -259,7 +375,8 @@ class DriveUploadHandlerTest
create_drive_integration_service_;
std::unique_ptr<drive::DriveIntegrationServiceFactory::ScopedFactoryForTest>
service_factory_for_test_;
- std::map<Profile*, std::unique_ptr<drive::FakeDriveFsHelper>>
+ std::map<Profile*,
+ std::unique_ptr<file_manager::test::FakeSimpleDriveFsHelper>>
fake_drivefs_helpers_;
// Used to track the upload progress during the tests.
@@ -268,70 +385,135 @@ class DriveUploadHandlerTest
};
IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest, UploadFromMyFiles) {
- const std::string test_file_name = "text.docx";
+ SetUpObservers();
SetUpMyFiles();
-
+ SetUpDrive();
// Define the source file as a test docx file within My files.
- SetUpSourceFile(test_file_name, my_files_dir_);
-
- // Create Drive root directory.
- {
- base::ScopedAllowBlockingForTesting allow_blocking;
- EXPECT_TRUE(base::CreateDirectory(drive_root_dir_));
- }
-
- // Check that the source file exists at the intended source location.
- {
- base::ScopedAllowBlockingForTesting allow_blocking;
- EXPECT_TRUE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
- EXPECT_FALSE(base::PathExists(drive_root_dir_.AppendASCII(test_file_name)));
- }
+ const std::string test_file_name = "text.docx";
+ FileSystemURL source_file_url =
+ SetUpSourceFile(test_file_name, my_files_dir_);
EXPECT_CALL(fake_drivefs(), ImmediatelyUpload(_, _))
.WillOnce(RunOnceCallback<1>(drive::FileError::FILE_ERROR_OK));
- InitiateUpload();
- WaitForUploadComplete();
+ DriveUploadHandler::Upload(
+ profile(), source_file_url,
+ base::BindOnce(&DriveUploadHandlerTest::OnUploadDone,
+ base::Unretained(this)));
+ Wait();
// Check that the source file has been moved to Drive.
{
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_FALSE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
- EXPECT_TRUE(base::PathExists(drive_root_dir_.AppendASCII(test_file_name)));
+ CheckPathExistsOnDrive(observed_relative_drive_path());
}
}
IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest, UploadFromReadOnlyFileSystem) {
- const std::string test_file_name = "text.docx";
+ SetUpObservers();
SetUpReadOnlyLocation();
-
+ SetUpDrive();
// Define the source file as a test docx file within My files.
- SetUpSourceFile(test_file_name, read_only_dir_);
+ const std::string test_file_name = "text.docx";
+ FileSystemURL source_file_url =
+ SetUpSourceFile(test_file_name, read_only_dir_);
- // Create Drive root directory.
+ EXPECT_CALL(fake_drivefs(), ImmediatelyUpload(_, _))
+ .WillOnce(RunOnceCallback<1>(drive::FileError::FILE_ERROR_OK));
+
+ DriveUploadHandler::Upload(
+ profile(), source_file_url,
+ base::BindOnce(&DriveUploadHandlerTest::OnUploadDone,
+ base::Unretained(this)));
+ Wait();
+
+ // Check that the source file has been copied to Drive.
{
base::ScopedAllowBlockingForTesting allow_blocking;
- EXPECT_TRUE(base::CreateDirectory(drive_root_dir_));
+ EXPECT_TRUE(base::PathExists(read_only_dir_.AppendASCII(test_file_name)));
+ CheckPathExistsOnDrive(observed_relative_drive_path());
}
+}
+
+// Test that when the sync to Drive fails, the file is not moved to Drive.
+IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest, UploadFails) {
+ fail_sync_ = true;
+ SetUpObservers();
+ SetUpMyFiles();
+ SetUpDrive();
+ // Define the source file as a test docx file within My files.
+ const std::string test_file_name = "text.docx";
+ FileSystemURL source_file_url =
+ SetUpSourceFile(test_file_name, my_files_dir_);
+
+ EXPECT_CALL(fake_drivefs(), ImmediatelyUpload(_, _))
+ .WillOnce(RunOnceCallback<1>(drive::FileError::FILE_ERROR_FAILED));
+
+ DriveUploadHandler::Upload(
+ profile(), source_file_url,
+ base::BindOnce(&DriveUploadHandlerTest::OnUploadDone,
+ base::Unretained(this)));
+ Wait();
+ // Check that the source file has not been moved to Drive.
{
base::ScopedAllowBlockingForTesting allow_blocking;
- EXPECT_TRUE(base::PathExists(read_only_dir_.AppendASCII(test_file_name)));
- EXPECT_FALSE(base::PathExists(drive_root_dir_.AppendASCII(test_file_name)));
+ EXPECT_TRUE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
+ CheckPathNotFoundOnDrive(observed_relative_drive_path());
}
+}
- EXPECT_CALL(fake_drivefs(), ImmediatelyUpload(_, _))
- .WillOnce(RunOnceCallback<1>(drive::FileError::FILE_ERROR_OK));
+IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest, UploadFromMyFilesNoConnection) {
+ SetUpObservers();
+ SetUpMyFiles();
+ SetUpDrive();
+ SetDriveConnectionStatusForTesting(ConnectionStatus::kNoNetwork);
- InitiateUpload();
- WaitForUploadComplete();
+ // Define the source file as a test docx file within My files.
+ const std::string test_file_name = "text.docx";
+ FileSystemURL source_file_url =
+ SetUpSourceFile(test_file_name, my_files_dir_);
- // Check that the source file has been copied to Drive.
+ EXPECT_CALL(fake_drivefs(), ImmediatelyUpload(_, _)).Times(0);
+
+ base::RunLoop run_loop;
+ base::MockCallback<DriveUploadHandler::UploadCallback> upload_callback;
+ EXPECT_CALL(upload_callback, Run(absl::optional<GURL>(absl::nullopt), _))
+ .WillOnce(RunClosure(run_loop.QuitClosure()));
+ DriveUploadHandler::Upload(profile(), source_file_url, upload_callback.Get());
+ run_loop.Run();
+
+ // Check that the source file has not been moved to Drive.
{
base::ScopedAllowBlockingForTesting allow_blocking;
- EXPECT_TRUE(base::PathExists(read_only_dir_.AppendASCII(test_file_name)));
- EXPECT_TRUE(base::PathExists(drive_root_dir_.AppendASCII(test_file_name)));
+ EXPECT_TRUE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
+ CheckPathNotFoundOnDrive(observed_relative_drive_path());
}
}
+IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest,
+ UploadFromMyFilesConnectionLostDuringUpload) {
+ SetUpObservers();
+ SetUpMyFiles();
+ SetUpDrive();
+
+ // Define the source file as a test docx file within My files.
+ const std::string test_file_name = "text.docx";
+ FileSystemURL source_file_url =
+ SetUpSourceFile(test_file_name, my_files_dir_);
+
+ on_transfer_complete_callback_ = base::BindLambdaForTesting([this] {
+ SetDriveConnectionStatusForTesting(ConnectionStatus::kNoNetwork);
+ drive_integration_service()->OnNetworkChanged();
+ });
+
+ base::RunLoop run_loop;
+ base::MockCallback<DriveUploadHandler::UploadCallback> upload_callback;
+ EXPECT_CALL(upload_callback, Run(absl::optional<GURL>(absl::nullopt), _))
+ .WillOnce(RunClosure(run_loop.QuitClosure()));
+ DriveUploadHandler::Upload(profile(), source_file_url, upload_callback.Get());
+ run_loop.Run();
+}
+
} // namespace ash::cloud_upload
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
index 64207e7d3b5..6ab6e7bbbb7 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
@@ -5,8 +5,11 @@
#include "chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h"
#include "base/check_op.h"
+#include "base/debug/dump_without_crashing.h"
#include "base/files/file.h"
#include "base/functional/bind.h"
+#include "base/i18n/message_formatter.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/timer/elapsed_timer.h"
#include "chrome/browser/ash/file_manager/copy_or_move_io_task.h"
@@ -17,9 +20,12 @@
#include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/ash/file_system_provider/service.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/grit/generated_resources.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "google_apis/common/task_util.h"
+#include "ui/base/l10n/l10n_util.h"
using ash::file_system_provider::ProvidedFileSystemInfo;
using ash::file_system_provider::ProviderId;
@@ -35,9 +41,9 @@ constexpr char kUploadResultMetricName[] =
// Runs the callback provided to `OneDriveUploadHandler::Upload`.
void OnUploadDone(scoped_refptr<OneDriveUploadHandler> one_drive_upload_handler,
OneDriveUploadHandler::UploadCallback callback,
- const FileSystemURL& uploaded_file_url,
+ absl::optional<FileSystemURL> uploaded_file_url,
int64_t upload_size) {
- std::move(callback).Run(uploaded_file_url, upload_size);
+ std::move(callback).Run(std::move(uploaded_file_url), upload_size);
}
} // namespace
@@ -62,11 +68,11 @@ OneDriveUploadHandler::OneDriveUploadHandler(Profile* profile,
base::MakeRefCounted<CloudUploadNotificationManager>(
profile,
source_url.path().BaseName().value(),
- "Microsoft OneDrive",
- "Microsoft 365",
+ l10n_util::GetStringUTF8(IDS_OFFICE_CLOUD_PROVIDER_ONEDRIVE),
+ l10n_util::GetStringUTF8(IDS_OFFICE_FILE_HANDLER_APP_MICROSOFT),
// TODO(b/242685536) Update when support for multi-files is added.
/*num_files=*/1,
- GetOperationTypeForUpload(profile, source_url))),
+ GetUploadType(profile, source_url))),
source_url_(source_url) {
observed_task_id_ = -1;
}
@@ -85,8 +91,8 @@ void OneDriveUploadHandler::Run(UploadCallback callback) {
if (!profile_) {
LOG(ERROR) << "No profile";
- OnEndUpload(FileSystemURL(), OfficeFilesUploadResult::kOtherError,
- kGenericErrorMessage);
+ OnEndUpload(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kOtherError);
return;
}
@@ -94,15 +100,15 @@ void OneDriveUploadHandler::Run(UploadCallback callback) {
(file_manager::VolumeManager::Get(profile_));
if (!volume_manager) {
LOG(ERROR) << "No volume manager";
- OnEndUpload(FileSystemURL(), OfficeFilesUploadResult::kOtherError,
- kGenericErrorMessage);
+ OnEndUpload(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kOtherError);
return;
}
io_task_controller_ = volume_manager->io_task_controller();
if (!io_task_controller_) {
LOG(ERROR) << "No task_controller";
- OnEndUpload(FileSystemURL(), OfficeFilesUploadResult::kOtherError,
- kGenericErrorMessage);
+ OnEndUpload(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kOtherError);
return;
}
@@ -110,35 +116,31 @@ void OneDriveUploadHandler::Run(UploadCallback callback) {
io_task_controller_->AddObserver(this);
// Destination url.
- ProviderId provider_id = ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile_));
- Service* service = Service::Get(profile_);
- std::vector<ProvidedFileSystemInfo> file_systems =
- service->GetProvidedFileSystemInfoList(provider_id);
- // One and only one filesystem should be mounted for the ODFS extension.
- if (file_systems.size() != 1u) {
- if (file_systems.empty()) {
- LOG(ERROR) << "No file systems found for the ODFS Extension";
- } else {
- LOG(ERROR) << "Multiple file systems found for the ODFS Extension";
- }
- OnEndUpload(FileSystemURL(), OfficeFilesUploadResult::kFileSystemNotFound,
- kGenericErrorMessage);
+ auto odfs_info = GetODFSInfo(profile_);
+ if (!odfs_info) {
+ // TODO(b/293363474): Remove when the underlying cause is diagnosed.
+ base::debug::DumpWithoutCrashing(FROM_HERE);
+ OnEndUpload(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kFileSystemNotFound);
return;
}
- destination_folder_path_ = file_systems[0].mount_path();
+ destination_folder_path_ = odfs_info->mount_path();
FileSystemURL destination_folder_url = FilePathToFileSystemURL(
profile_, file_system_context_, destination_folder_path_);
// TODO (b/243095484) Define error behavior.
if (!destination_folder_url.is_valid()) {
LOG(ERROR) << "Unable to generate destination folder ODFS URL";
- OnEndUpload(FileSystemURL(), OfficeFilesUploadResult::kFileSystemNotFound,
- kGenericErrorMessage);
+ // TODO(b/293363474): Remove when the underlying cause is diagnosed.
+ base::debug::DumpWithoutCrashing(FROM_HERE);
+ OnEndUpload(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kDestinationUrlError);
return;
}
const file_manager::io_task::OperationType operation_type =
- GetOperationTypeForUpload(profile_, source_url_);
+ GetUploadType(profile_, source_url_) == UploadType::kCopy
+ ? file_manager::io_task::OperationType::kCopy
+ : file_manager::io_task::OperationType::kMove;
std::vector<FileSystemURL> source_urls{source_url_};
std::unique_ptr<file_manager::io_task::IOTask> task =
std::make_unique<file_manager::io_task::CopyOrMoveIOTask>(
@@ -149,21 +151,27 @@ void OneDriveUploadHandler::Run(UploadCallback callback) {
observed_task_id_ = io_task_controller_->Add(std::move(task));
}
-void OneDriveUploadHandler::OnEndUpload(const FileSystemURL& uploaded_file_url,
- OfficeFilesUploadResult result,
- std::string error_message) {
- UMA_HISTOGRAM_ENUMERATION(kUploadResultMetricName, result);
- // Resolve notifications.
- if (notification_manager_) {
- if (uploaded_file_url.is_valid()) {
+void OneDriveUploadHandler::OnEndUpload(
+ base::expected<storage::FileSystemURL, std::string> url,
+ OfficeFilesUploadResult result_metric) {
+ UMA_HISTOGRAM_ENUMERATION(kUploadResultMetricName, result_metric);
+ if (url.has_value()) {
+ // Resolve notifications.
+ if (notification_manager_) {
notification_manager_->MarkUploadComplete();
- } else if (!error_message.empty()) {
+ }
+ if (callback_) {
+ std::move(callback_).Run(url.value(), upload_size_);
+ }
+ } else {
+ if (const std::string& error_message = url.error();
+ notification_manager_ && !error_message.empty()) {
LOG(ERROR) << "Upload to OneDrive: " << error_message;
notification_manager_->ShowUploadError(error_message);
}
- }
- if (callback_) {
- std::move(callback_).Run(uploaded_file_url, upload_size_);
+ if (callback_) {
+ std::move(callback_).Run(absl::nullopt, 0);
+ }
}
}
@@ -194,13 +202,11 @@ void OneDriveUploadHandler::OnIOTaskStatus(
return;
case file_manager::io_task::State::kCancelled:
if (status.type == file_manager::io_task::OperationType::kCopy) {
- OnEndUpload(FileSystemURL(),
- OfficeFilesUploadResult::kCopyOperationCancelled,
- kGenericErrorMessage);
+ OnEndUpload(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kCopyOperationCancelled);
} else {
- OnEndUpload(FileSystemURL(),
- OfficeFilesUploadResult::kMoveOperationCancelled,
- kGenericErrorMessage);
+ OnEndUpload(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kMoveOperationCancelled);
}
return;
case file_manager::io_task::State::kError:
@@ -213,38 +219,66 @@ void OneDriveUploadHandler::OnIOTaskStatus(
}
}
+void OneDriveUploadHandler::OnGetReauthenticationRequired(
+ base::expected<ODFSMetadata, base::File::Error> metadata_or_error) {
+ std::string error_message = GetGenericErrorMessage();
+ if (!metadata_or_error.has_value()) {
+ LOG(ERROR) << "Failed to get reauthentication required state: "
+ << metadata_or_error.error();
+ } else if (metadata_or_error->reauthentication_required) {
+ // Show the reauthentication required error notification.
+ error_message = GetReauthenticationRequiredMessage();
+ }
+ OnEndUpload(base::unexpected(error_message),
+ OfficeFilesUploadResult::kCloudAuthError);
+}
+
+void OneDriveUploadHandler::ShowAccessDeniedError() {
+ file_system_provider::ProvidedFileSystemInterface* file_system =
+ GetODFS(profile_);
+ if (!file_system) {
+ OnEndUpload(base::unexpected(GetGenericErrorMessage()),
+ OfficeFilesUploadResult::kCloudAuthError);
+ return;
+ }
+ GetODFSMetadata(
+ file_system,
+ base::BindOnce(&OneDriveUploadHandler::OnGetReauthenticationRequired,
+ this));
+}
+
void OneDriveUploadHandler::ShowIOTaskError(
const file_manager::io_task::ProgressStatus& status) {
OfficeFilesUploadResult upload_result;
std::string error_message;
bool copy = status.type == file_manager::io_task::OperationType::kCopy;
- std::string operation = copy ? "copy" : "move";
- std::string operation_past_tense = copy ? "copied" : "moved";
- base::File::Error file_error = base::File::FILE_ERROR_FAILED;
// TODO(b/242685536) Find most relevant error in a multi-file upload when
// support for multi-files is added.
- // Find the first not base::File::Error::FILE_OK.
- if (status.sources.size() > 0 && status.sources[0].error.has_value() &&
- status.sources[0].error.value() != base::File::Error::FILE_OK) {
- file_error = status.sources[0].error.value();
- } else if (status.outputs.size() > 0 && status.outputs[0].error.has_value()) {
- file_error = status.outputs[0].error.value();
- }
+ base::File::Error file_error =
+ GetFirstTaskError(status).value_or(base::File::FILE_ERROR_FAILED);
+
+ base::UmaHistogramExactLinear(
+ copy ? kOneDriveCopyErrorMetricName : kOneDriveMoveErrorMetricName,
+ -file_error, -base::File::FILE_ERROR_MAX);
switch (file_error) {
case base::File::FILE_ERROR_ACCESS_DENIED:
- // TODO(b/288022200): query '/' actions to distinguish between
- // reauthentication required and generic access error.
- upload_result = OfficeFilesUploadResult::kCloudAuthError;
- error_message = kReauthenticationRequiredMessage;
- break;
+ ShowAccessDeniedError();
+ return;
case base::File::FILE_ERROR_NO_SPACE:
upload_result = OfficeFilesUploadResult::kCloudQuotaFull;
// TODO(b/242685536) Use "these files" for multi-files when support for
// multi-files is added.
- error_message =
- "Free up space in OneDrive to " + operation + " this file";
+ error_message = base::UTF16ToUTF8(
+ base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(
+ copy ? IDS_OFFICE_UPLOAD_ERROR_FREE_UP_SPACE_TO_COPY
+ : IDS_OFFICE_UPLOAD_ERROR_FREE_UP_SPACE_TO_MOVE),
+ // TODO(b/242685536) Update when support for multi-files is added.
+ 1,
+ l10n_util::GetStringUTF16(
+ IDS_OFFICE_CLOUD_PROVIDER_ONEDRIVE_SHORT)));
break;
case base::File::FILE_ERROR_NOT_FOUND:
if (copy) {
@@ -252,8 +286,9 @@ void OneDriveUploadHandler::ShowIOTaskError(
} else {
upload_result = OfficeFilesUploadResult::kMoveOperationError;
}
- error_message = "The file could not be " + operation_past_tense +
- " because it no longer exists";
+ error_message = l10n_util::GetStringUTF8(
+ copy ? IDS_OFFICE_UPLOAD_ERROR_FILE_NOT_EXIST_TO_COPY
+ : IDS_OFFICE_UPLOAD_ERROR_FILE_NOT_EXIST_TO_MOVE);
break;
default:
if (copy) {
@@ -261,10 +296,9 @@ void OneDriveUploadHandler::ShowIOTaskError(
} else {
upload_result = OfficeFilesUploadResult::kMoveOperationError;
}
- error_message = kGenericErrorMessage;
+ error_message = GetGenericErrorMessage();
}
-
- OnEndUpload(FileSystemURL(), upload_result, error_message);
+ OnEndUpload(base::unexpected(error_message), upload_result);
}
} // namespace ash::cloud_upload
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h b/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
index 668f4409f2f..3e13b68ff65 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
@@ -12,13 +12,14 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/types/expected.h"
#include "chrome/browser/ash/file_manager/io_task_controller.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
-#include "url/gurl.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
class Profile;
@@ -34,7 +35,7 @@ class OneDriveUploadHandler
public base::RefCounted<OneDriveUploadHandler> {
public:
using UploadCallback =
- base::OnceCallback<void(const storage::FileSystemURL&, int64_t)>;
+ base::OnceCallback<void(absl::optional<storage::FileSystemURL>, int64_t)>;
// Starts the upload workflow for the file specified at construct time.
static void Upload(Profile* profile,
@@ -54,9 +55,9 @@ class OneDriveUploadHandler
void Run(UploadCallback callback);
// Ends upload and runs Upload callback.
- void OnEndUpload(const storage::FileSystemURL& uploaded_file_url,
- OfficeFilesUploadResult result,
- std::string error_message = "");
+ void OnEndUpload(
+ base::expected<storage::FileSystemURL, std::string> url_or_error,
+ OfficeFilesUploadResult result_metric);
// IOTaskController::Observer:
void OnIOTaskStatus(
@@ -66,6 +67,17 @@ class OneDriveUploadHandler
// an appropriate error notification.
void ShowIOTaskError(const file_manager::io_task::ProgressStatus& status);
+ // Show the correct error notification for
+ // base::File::FILE_ERROR_ACCESS_DENIED. Request ODFS metadata and show the
+ // correct notification in the |OnGetReauthenticationRequired| callback.
+ void ShowAccessDeniedError();
+
+ // Check if reauthentication to OneDrive is required from the ODFS metadata
+ // and show the reuathentication is required notification if true. Otherwise
+ // show the generic access error notification.
+ void OnGetReauthenticationRequired(
+ base::expected<ODFSMetadata, base::File::Error> metadata_or_error);
+
// OnGetActions callback which checks the |result| to see if reauthentication
// is required. If reauthentication is required, show the reauthentication
// required error. Otherwise show a generic move upload error.
diff --git a/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc
index c58b753c242..af683b79325 100644
--- a/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc
@@ -1,28 +1,29 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/functional/callback_forward.h"
+#include "base/test/bind.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h"
+#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/ash/file_manager/file_manager_test_util.h"
#include "chrome/browser/ash/file_manager/file_tasks.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
#include "chrome/browser/ash/file_system_provider/fake_extension_provider.h"
-#include "chrome/browser/ash/file_system_provider/fake_provided_file_system.h"
#include "chrome/browser/ash/file_system_provider/mount_path_util.h"
-#include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/ash/file_system_provider/provided_file_system_interface.h"
-#include "chrome/browser/ash/file_system_provider/service.h"
+#include "chrome/browser/notifications/notification_display_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
-#include "chrome/common/extensions/extension_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/constants/chromeos_features.h"
#include "content/public/test/browser_test.h"
@@ -53,7 +54,8 @@ base::FilePath GetTestFilePath(const std::string& file_name) {
// Tests the OneDrive upload workflow using the static
// `OneDriveUploadHandler::Upload` method. Ensures that the upload completes
// with the expected results.
-class OneDriveUploadHandlerTest : public InProcessBrowserTest {
+class OneDriveUploadHandlerTest : public InProcessBrowserTest,
+ public NotificationDisplayService::Observer {
public:
OneDriveUploadHandlerTest() {
feature_list_.InitAndEnableFeature(
@@ -72,6 +74,11 @@ class OneDriveUploadHandlerTest : public InProcessBrowserTest {
storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
}
+ void TearDownOnMainThread() override {
+ RemoveObservers();
+ InProcessBrowserTest::TearDownOnMainThread();
+ }
+
// Creates mount point for My files and registers local filesystem.
void SetUpMyFiles() {
{
@@ -109,25 +116,60 @@ class OneDriveUploadHandlerTest : public InProcessBrowserTest {
// Creates and mounts fake provided file system for OneDrive.
void SetUpODFS() {
- file_system_provider::Service* service =
- file_system_provider::Service::Get(profile());
- file_system_provider::MountOptions options("odfs", "ODFS");
- const file_system_provider::ProviderId provider_id =
- file_system_provider::ProviderId::CreateFromExtensionId(
- extension_misc::kODFSExtensionId);
- service->RegisterProvider(
- file_system_provider::FakeExtensionProvider::Create(
- extension_misc::kODFSExtensionId));
- EXPECT_EQ(base::File::FILE_OK,
- service->MountFileSystem(provider_id, options));
- std::vector<file_system_provider::ProvidedFileSystemInfo> file_systems =
- service->GetProvidedFileSystemInfoList(provider_id);
- // One and only one filesystem should be mounted for the ODFS extension.
- EXPECT_EQ(1u, file_systems.size());
provided_file_system_ =
- static_cast<file_system_provider::FakeProvidedFileSystem*>(
- service->GetProvidedFileSystem(provider_id,
- file_systems[0].file_system_id()));
+ file_manager::test::CreateFakeProvidedFileSystemOneDrive(profile());
+ }
+
+ // Create and add a file with |test_file_name| to the file system
+ // |source_path|. Return the created |source_file_url|.
+ FileSystemURL SetUpSourceFile(const std::string& test_file_name,
+ base::FilePath source_path) {
+ const base::FilePath source_file_path =
+ source_path.AppendASCII(test_file_name);
+ // Create test docx file within My files.
+ const base::FilePath test_file_path = GetTestFilePath(test_file_name);
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ CHECK(base::CopyFile(test_file_path, source_file_path));
+ }
+
+ // Check that the source file exists at the intended source location and is
+ // not in ODFS.
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ EXPECT_TRUE(base::PathExists(source_path.AppendASCII(test_file_name)));
+ CheckPathNotFoundOnODFS(base::FilePath("/").AppendASCII(test_file_name));
+ }
+
+ FileSystemURL source_file_url = FilePathToFileSystemURL(
+ profile(),
+ file_manager::util::GetFileManagerFileSystemContext(profile()),
+ source_file_path);
+
+ return source_file_url;
+ }
+
+ void SetUpObservers() {
+ // Subscribe to Notification updates to track copy/move ODFS notifications.
+ NotificationDisplayService::GetForProfile(profile())->AddObserver(this);
+ }
+
+ void RemoveObservers() {
+ NotificationDisplayService::GetForProfile(browser()->profile())
+ ->RemoveObserver(this);
+ }
+
+ void Wait() {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ ASSERT_FALSE(run_loop_);
+ run_loop_ = std::make_unique<base::RunLoop>();
+ run_loop_->Run();
+ run_loop_ = nullptr;
+ }
+
+ void EndWait() {
+ ASSERT_TRUE(run_loop_);
+ run_loop_->Quit();
}
void CheckPathExistsOnODFS(const base::FilePath& path) {
@@ -137,10 +179,7 @@ class OneDriveUploadHandlerTest : public InProcessBrowserTest {
base::BindOnce(&OneDriveUploadHandlerTest::OnGetMetadataExpectSuccess,
base::Unretained(this)));
base::ScopedAllowBlockingForTesting allow_blocking;
- ASSERT_FALSE(run_loop_);
- run_loop_ = std::make_unique<base::RunLoop>();
- run_loop_->Run();
- run_loop_ = nullptr;
+ Wait();
}
void CheckPathNotFoundOnODFS(const base::FilePath& path) {
@@ -150,43 +189,47 @@ class OneDriveUploadHandlerTest : public InProcessBrowserTest {
base::BindOnce(&OneDriveUploadHandlerTest::OnGetMetadataExpectNotFound,
base::Unretained(this)));
base::ScopedAllowBlockingForTesting allow_blocking;
- ASSERT_FALSE(run_loop_);
- run_loop_ = std::make_unique<base::RunLoop>();
- run_loop_->Run();
- run_loop_ = nullptr;
+ Wait();
}
void OnGetMetadataExpectSuccess(
std::unique_ptr<file_system_provider::EntryMetadata> entry_metadata,
base::File::Error result) {
EXPECT_EQ(base::File::Error::FILE_OK, result);
- ASSERT_TRUE(run_loop_);
- run_loop_->Quit();
+ EndWait();
}
void OnGetMetadataExpectNotFound(
std::unique_ptr<file_system_provider::EntryMetadata> entry_metadata,
base::File::Error result) {
EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_FOUND, result);
- ASSERT_TRUE(run_loop_);
- run_loop_->Quit();
+ EndWait();
}
- // The exit point of the test. `WaitForUploadComplete` will not complete until
- // this is called.
- void OnUploadDone(const storage::FileSystemURL& uploaded_file_url,
+ // Watch for a valid `uploaded_file_url`.
+ void OnUploadDone(absl::optional<storage::FileSystemURL> uploaded_file_url,
int64_t size) {
- ASSERT_TRUE(uploaded_file_url.is_valid());
- ASSERT_TRUE(run_loop_);
- run_loop_->Quit();
+ ASSERT_TRUE(uploaded_file_url.has_value());
+ EndWait();
}
- void WaitForUploadComplete() {
- base::ScopedAllowBlockingForTesting allow_blocking;
- ASSERT_FALSE(run_loop_);
- run_loop_ = std::make_unique<base::RunLoop>();
- run_loop_->Run();
- run_loop_ = nullptr;
+ // Run |on_notification_displayed_callback_| with observed |notification|.
+ void OnNotificationDisplayed(
+ const message_center::Notification& notification,
+ const NotificationCommon::Metadata* const metadata) override {
+ if (on_notification_displayed_callback_) {
+ std::move(on_notification_displayed_callback_).Run(notification);
+ }
+ }
+
+ void OnNotificationClosed(const std::string& notification_id) override {}
+ void OnNotificationDisplayServiceDestroyed(
+ NotificationDisplayService* service) override {}
+
+ void SetOnNotificationDisplayedCallback(
+ base::RepeatingCallback<void(const message_center::Notification&)>
+ callback) {
+ on_notification_displayed_callback_ = std::move(callback);
}
Profile* profile() { return browser()->profile(); }
@@ -194,47 +237,33 @@ class OneDriveUploadHandlerTest : public InProcessBrowserTest {
protected:
base::FilePath my_files_dir_;
base::FilePath read_only_dir_;
+ raw_ptr<file_manager::test::FakeProvidedFileSystemOneDrive,
+ DanglingUntriaged | ExperimentalAsh>
+ provided_file_system_; // Owned by Service.
private:
- raw_ptr<file_system_provider::FakeProvidedFileSystem, ExperimentalAsh>
- provided_file_system_; // Owned by Service.
base::test::ScopedFeatureList feature_list_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<base::RunLoop> run_loop_;
+ // Used to observe upload notifications during the tests.
+ base::RepeatingCallback<void(const message_center::Notification&)>
+ on_notification_displayed_callback_;
};
IN_PROC_BROWSER_TEST_F(OneDriveUploadHandlerTest, UploadFromMyFiles) {
- const std::string test_file_name = "text.docx";
- const base::FilePath source_file_path =
- my_files_dir_.AppendASCII(test_file_name);
-
SetUpMyFiles();
SetUpODFS();
-
- // Create test docx file within My files.
- const base::FilePath test_file_path = GetTestFilePath(test_file_name);
- {
- base::ScopedAllowBlockingForTesting allow_blocking;
- CHECK(base::CopyFile(test_file_path, source_file_path));
- }
-
- // Check that the source file exists at the intended source location.
- {
- base::ScopedAllowBlockingForTesting allow_blocking;
- EXPECT_TRUE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
- CheckPathNotFoundOnODFS(base::FilePath("/").AppendASCII(test_file_name));
- }
+ const std::string test_file_name = "text.docx";
+ FileSystemURL source_file_url =
+ SetUpSourceFile(test_file_name, my_files_dir_);
// Start the upload workflow and end the test once the upload has completed
// successfully.
- FileSystemURL source_file_url = FilePathToFileSystemURL(
- profile(), file_manager::util::GetFileManagerFileSystemContext(profile()),
- source_file_path);
OneDriveUploadHandler::Upload(
profile(), source_file_url,
base::BindOnce(&OneDriveUploadHandlerTest::OnUploadDone,
base::Unretained(this)));
- WaitForUploadComplete();
+ Wait();
// Check that the source file has been moved to OneDrive.
{
@@ -246,39 +275,21 @@ IN_PROC_BROWSER_TEST_F(OneDriveUploadHandlerTest, UploadFromMyFiles) {
IN_PROC_BROWSER_TEST_F(OneDriveUploadHandlerTest,
UploadFromReadOnlyFileSystem) {
- const std::string test_file_name = "text.docx";
- const base::FilePath source_file_path =
- read_only_dir_.AppendASCII(test_file_name);
-
SetUpReadOnlyLocation();
SetUpODFS();
-
- // Create test docx file within My files.
- const base::FilePath test_file_path = GetTestFilePath(test_file_name);
- {
- base::ScopedAllowBlockingForTesting allow_blocking;
- CHECK(base::CopyFile(test_file_path, source_file_path));
- }
-
- // Check that the source file exists at the intended source location.
- {
- base::ScopedAllowBlockingForTesting allow_blocking;
- EXPECT_TRUE(base::PathExists(read_only_dir_.AppendASCII(test_file_name)));
- CheckPathNotFoundOnODFS(base::FilePath("/").AppendASCII(test_file_name));
- }
+ const std::string test_file_name = "text.docx";
+ FileSystemURL source_file_url =
+ SetUpSourceFile(test_file_name, read_only_dir_);
// Start the upload workflow and end the test once the upload has completed
// successfully.
- FileSystemURL source_file_url = FilePathToFileSystemURL(
- profile(), file_manager::util::GetFileManagerFileSystemContext(profile()),
- source_file_path);
OneDriveUploadHandler::Upload(
profile(), source_file_url,
base::BindOnce(&OneDriveUploadHandlerTest::OnUploadDone,
base::Unretained(this)));
- WaitForUploadComplete();
+ Wait();
- // Check that the source file has been moved to OneDrive.
+ // Check that the source file has been copied to OneDrive.
{
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::PathExists(read_only_dir_.AppendASCII(test_file_name)));
@@ -286,4 +297,81 @@ IN_PROC_BROWSER_TEST_F(OneDriveUploadHandlerTest,
}
}
+// Test that when the upload to ODFS fails due reauthentication to OneDrive
+// being required, the reauthentication required notification is shown.
+IN_PROC_BROWSER_TEST_F(OneDriveUploadHandlerTest,
+ FailToUploadDueToReauthenticationRequired) {
+ SetUpObservers();
+ SetUpMyFiles();
+ SetUpODFS();
+ // Ensure upload fails due to reauthentication to OneDrive being required.
+ provided_file_system_->SetCreateFileError(
+ base::File::Error::FILE_ERROR_ACCESS_DENIED);
+ provided_file_system_->SetReauthenticationRequired(true);
+ // Expect the reauthentication required notification.
+ const std::string test_file_name = "text.docx";
+ FileSystemURL source_file_url =
+ SetUpSourceFile(test_file_name, my_files_dir_);
+
+ // Start the upload workflow and end the test once the upload has failed due
+ // to reauthentication being required.
+ auto on_notification = base::BindLambdaForTesting(
+ [&](const message_center::Notification& notification) {
+ if (notification.message() ==
+ base::UTF8ToUTF16(GetReauthenticationRequiredMessage())) {
+ EndWait();
+ }
+ });
+ SetOnNotificationDisplayedCallback(std::move(on_notification));
+ OneDriveUploadHandler::Upload(profile(), source_file_url, base::DoNothing());
+ Wait();
+
+ // Check that the source file still exists only at the intended source
+ // location and did not get uploaded to ODFS.
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ EXPECT_TRUE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
+ CheckPathNotFoundOnODFS(base::FilePath("/").AppendASCII(test_file_name));
+ }
+}
+
+// Test that when the upload to ODFS fails due an access error that is not
+// because reauthentication to OneDrive is required, the generic error
+// notification is shown.
+IN_PROC_BROWSER_TEST_F(OneDriveUploadHandlerTest,
+ FailToUploadDueToOtherAccessError) {
+ SetUpObservers();
+ SetUpMyFiles();
+ SetUpODFS();
+ // Ensure Upload fails due to some access error which is not because
+ // reauthentication to OneDrive is required.
+ provided_file_system_->SetCreateFileError(
+ base::File::Error::FILE_ERROR_ACCESS_DENIED);
+ provided_file_system_->SetReauthenticationRequired(false);
+ const std::string test_file_name = "text.docx";
+ FileSystemURL source_file_url =
+ SetUpSourceFile(test_file_name, my_files_dir_);
+
+ // Start the upload workflow and end the test once the upload has failed due
+ // to some access error.
+ auto on_notification = base::BindLambdaForTesting(
+ [&](const message_center::Notification& notification) {
+ if (notification.message() ==
+ base::UTF8ToUTF16(GetGenericErrorMessage())) {
+ EndWait();
+ }
+ });
+ SetOnNotificationDisplayedCallback(std::move(on_notification));
+ OneDriveUploadHandler::Upload(profile(), source_file_url, base::DoNothing());
+ Wait();
+
+ // Check that the source file still exists only at the intended source
+ // location and did not get uploaded to ODFS.
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ EXPECT_TRUE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
+ CheckPathNotFoundOnODFS(base::FilePath("/").AppendASCII(test_file_name));
+ }
+}
+
} // namespace ash::cloud_upload
diff --git a/chromium/chrome/browser/ui/webui/ash/cros_components_browsertest.cc b/chromium/chrome/browser/ui/webui/ash/cros_components_browsertest.cc
index 975f14c19be..a5a8c1832f3 100644
--- a/chromium/chrome/browser/ui/webui/ash/cros_components_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/cros_components_browsertest.cc
@@ -161,6 +161,16 @@ static constexpr const ComponentTestData kComponentsTestData[] = {
.component_name = "cros-switch",
.gtest_name = "CrosSwitch",
},
+ {
+ .script_src = "chrome://resources/cros_components/slider/slider.js",
+ .component_name = "cros-slider",
+ .gtest_name = "CrosSlider",
+ },
+ {
+ .script_src = "chrome://resources/cros_components/tag/tag.js",
+ .component_name = "cros-tag",
+ .gtest_name = "CrosTag",
+ },
};
INSTANTIATE_TEST_SUITE_P(All,
diff --git a/chromium/chrome/browser/ui/webui/ash/drive_internals_ui.cc b/chromium/chrome/browser/ui/webui/ash/drive_internals_ui.cc
index b5b37304cab..c64250e898d 100644
--- a/chromium/chrome/browser/ui/webui/ash/drive_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/drive_internals_ui.cc
@@ -12,23 +12,19 @@
#include <memory>
#include <sstream>
#include <string>
-#include <type_traits>
#include <utility>
#include <vector>
#include "ash/constants/ash_features.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
-#include "base/format_macros.h"
#include "base/functional/bind.h"
-#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/strings/pattern.h"
#include "base/strings/strcat.h"
-#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "base/task/sequenced_task_runner.h"
@@ -43,7 +39,6 @@
#include "chrome/browser/file_util_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/services/file_util/public/cpp/zip_file_creator.h"
#include "chromeos/ash/components/drivefs/drivefs_pin_manager.h"
@@ -53,7 +48,6 @@
#include "components/drive/drive_pref_names.h"
#include "components/drive/event_logger.h"
#include "components/prefs/pref_service.h"
-#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents.h"
@@ -67,8 +61,12 @@
namespace ash {
namespace {
+using base::FileEnumerator;
+using base::FilePath;
+using base::Value;
using content::BrowserThread;
using drive::DriveIntegrationService;
+using drive::prefs::kDriveFsBulkPinningEnabled;
using drivefs::pinning::PinManager;
constexpr char kKey[] = "key";
@@ -78,10 +76,10 @@ constexpr char kClass[] = "class";
constexpr const char* const kLogLevelName[] = {"info", "warning", "error"};
size_t SeverityToLogLevelNameIndex(logging::LogSeverity severity) {
- if (severity <= logging::LOG_INFO) {
+ if (severity <= logging::LOGGING_INFO) {
return 0;
}
- if (severity == logging::LOG_WARNING) {
+ if (severity == logging::LOGGING_WARNING) {
return 1;
}
return 2;
@@ -123,28 +121,27 @@ std::string ToPercent(const T num, const T total) {
// },...]
//
// The list is sorted by the path.
-std::pair<base::Value::List, base::Value::Dict> GetGCacheContents(
- const base::FilePath& root_path) {
+std::pair<Value::List, Value::Dict> GetGCacheContents(
+ const FilePath& root_path) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
// Use this map to sort the result list by the path.
- std::map<base::FilePath, base::Value::Dict> files;
+ std::map<FilePath, Value::Dict> files;
- const int options =
- (base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES |
- base::FileEnumerator::SHOW_SYM_LINKS);
- base::FileEnumerator enumerator(root_path, true /* recursive */, options);
+ const int options = (FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
+ FileEnumerator::SHOW_SYM_LINKS);
+ FileEnumerator enumerator(root_path, true /* recursive */, options);
int64_t total_size = 0;
- for (base::FilePath current = enumerator.Next(); !current.empty();
+ for (FilePath current = enumerator.Next(); !current.empty();
current = enumerator.Next()) {
- base::FileEnumerator::FileInfo info = enumerator.GetInfo();
+ FileEnumerator::FileInfo info = enumerator.GetInfo();
int64_t size = info.GetSize();
const bool is_directory = info.IsDirectory();
const bool is_symbolic_link = base::IsLink(info.GetName());
const base::Time last_modified = info.GetLastModifiedTime();
- base::Value::Dict entry;
+ Value::Dict entry;
entry.Set("path", current.value());
// Use double instead of integer for large files.
entry.Set("size", static_cast<double>(size));
@@ -160,7 +157,7 @@ std::pair<base::Value::List, base::Value::Dict> GetGCacheContents(
total_size += size;
}
- std::pair<base::Value::List, base::Value::Dict> result;
+ std::pair<Value::List, Value::Dict> result;
// Convert |files| into response.
for (auto& it : files) {
result.first.Append(std::move(it.second));
@@ -171,11 +168,11 @@ std::pair<base::Value::List, base::Value::Dict> GetGCacheContents(
// Appends {'key': key, 'value': value, 'class': clazz} dictionary to the
// |list|.
-void AppendKeyValue(base::Value::List& list,
+void AppendKeyValue(Value::List& list,
std::string key,
std::string value,
std::string clazz = std::string()) {
- base::Value::Dict dict;
+ Value::Dict dict;
dict.Set(kKey, std::move(key));
dict.Set(kValue, std::move(value));
if (!clazz.empty()) {
@@ -184,7 +181,7 @@ void AppendKeyValue(base::Value::List& list,
list.Append(std::move(dict));
}
-ino_t GetInodeValue(const base::FilePath& path) {
+ino_t GetInodeValue(const FilePath& path) {
struct stat file_stats;
if (stat(path.value().c_str(), &file_stats) != 0) {
return 0;
@@ -192,11 +189,10 @@ ino_t GetInodeValue(const base::FilePath& path) {
return file_stats.st_ino;
}
-std::pair<ino_t, base::Value::List> GetServiceLogContents(
- const base::FilePath& log_path,
- ino_t inode,
- int from_line_number) {
- base::Value::List result;
+std::pair<ino_t, Value::List> GetServiceLogContents(const FilePath& log_path,
+ ino_t inode,
+ int from_line_number) {
+ Value::List result;
std::ifstream log(log_path.value());
if (log.good()) {
@@ -255,12 +251,11 @@ void ZipLogs(Profile* profile,
// Class to handle messages from chrome://drive-internals.
class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
- public PinManager::Observer {
+ DriveIntegrationService::Observer {
public:
~DriveInternalsWebUIHandler() override {
- if (pin_manager_) {
- VLOG(1) << "DriveInternalsWebUIHandler dropped before PinManager";
- pin_manager_->RemoveObserver(this);
+ if (DriveIntegrationService* const service = GetIntegrationService()) {
+ service->RemoveObserver(this);
}
}
@@ -269,17 +264,17 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
DriveInternalsWebUIHandler& operator=(const DriveInternalsWebUIHandler&) =
delete;
- void DownloadLogsZip(const base::FilePath& path) {
+ void DownloadLogsZip(const FilePath& path) {
web_ui()->GetWebContents()->GetController().LoadURL(
net::FilePathToFileURL(path), {}, {}, {});
}
- void OnZipDone() { MaybeCallJavascript("onZipDone", base::Value()); }
+ void OnZipDone() { MaybeCallJavascript("onZipDone", Value()); }
private:
void MaybeCallJavascript(const std::string& function,
- base::Value data1,
- base::Value data2 = {}) {
+ Value data1,
+ Value data2 = {}) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (IsJavascriptAllowed()) {
CallJavascriptFunction(function, std::move(data1), std::move(data2));
@@ -288,8 +283,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
// Hide or show a section of the page.
void SetSectionEnabled(const std::string& section, bool enable) {
- MaybeCallJavascript("setSectionEnabled", base::Value(section),
- base::Value(enable));
+ MaybeCallJavascript("setSectionEnabled", Value(section), Value(enable));
}
// WebUIMessageHandler override.
@@ -303,6 +297,10 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
base::BindRepeating(&DriveInternalsWebUIHandler::OnPeriodicUpdate,
weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
+ "setBulkPinningVisible",
+ base::BindRepeating(&DriveInternalsWebUIHandler::SetBulkPinningVisible,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
"setVerboseLoggingEnabled",
base::BindRepeating(
&DriveInternalsWebUIHandler::SetVerboseLoggingEnabled,
@@ -322,10 +320,6 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
weak_ptr_factory_.GetWeakPtr(),
drivefs::mojom::MirrorPathStatus::kStop));
web_ui()->RegisterMessageCallback(
- "setBulkPinningEnabled",
- base::BindRepeating(&DriveInternalsWebUIHandler::SetBulkPinningEnabled,
- weak_ptr_factory_.GetWeakPtr()));
- web_ui()->RegisterMessageCallback(
"enableTracing",
base::BindRepeating(&DriveInternalsWebUIHandler::SetTracingEnabled,
weak_ptr_factory_.GetWeakPtr(), true));
@@ -377,15 +371,18 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
"loadAccountSettings",
base::BindRepeating(&DriveInternalsWebUIHandler::LoadAccountSettings,
weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "setBulkPinningEnabled",
+ base::BindRepeating(&DriveInternalsWebUIHandler::SetBulkPinningEnabled,
+ weak_ptr_factory_.GetWeakPtr()));
}
// Called when the page is first loaded.
- void OnPageLoaded(const base::Value::List& args) {
+ void OnPageLoaded(const Value::List& args) {
AllowJavascript();
DriveIntegrationService* const service = GetIntegrationService();
if (!service) {
- LOG(ERROR) << "No DriveFS integration service";
return;
}
@@ -399,7 +396,6 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
UpdateInFlightOperationsSection();
UpdateDriveDebugSection();
UpdateMirrorSyncSection();
- UpdateBulkPinningSection();
// When the drive-internals page is reloaded by the reload key, the page
// content is recreated, but this WebUI object is not (instead, OnPageLoaded
@@ -413,7 +409,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
// Called when the page requests periodic update.
- void OnPeriodicUpdate(const base::Value::List& args) {
+ void OnPeriodicUpdate(const Value::List& args) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DriveIntegrationService* const service = GetIntegrationService();
@@ -433,38 +429,18 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
void UpdateConnectionStatusSection() {
SetSectionEnabled("connection-status-section", true);
- std::string status;
- switch (drive::util::GetDriveConnectionStatus(profile())) {
- case drive::util::DRIVE_DISCONNECTED_NOSERVICE:
- status = "no service";
- break;
- case drive::util::DRIVE_DISCONNECTED_NONETWORK:
- status = "no network";
- break;
- case drive::util::DRIVE_DISCONNECTED_NOTREADY:
- status = "not ready";
- break;
- case drive::util::DRIVE_CONNECTED_METERED:
- status = "metered";
- break;
- case drive::util::DRIVE_CONNECTED:
- status = "connected";
- break;
- }
-
- base::Value::Dict connection_status;
- connection_status.Set("status", std::move(status));
- drive::DriveNotificationManager* drive_notification_manager =
+ Value::Dict connection_status;
+ connection_status.Set(
+ "status", ToString(drive::util::GetDriveConnectionStatus(profile())));
+ drive::DriveNotificationManager* const manager =
drive::DriveNotificationManagerFactory::FindForBrowserContext(
profile());
connection_status.Set(
"push-notification-enabled",
- drive_notification_manager
- ? drive_notification_manager->push_notification_enabled()
- : false);
+ manager ? manager->push_notification_enabled() : false);
MaybeCallJavascript("updateConnectionStatus",
- base::Value(std::move(connection_status)));
+ Value(std::move(connection_status)));
}
void UpdateAboutResourceSection() {
@@ -485,7 +461,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
void UpdatePathConfigurationsSection() {
SetSectionEnabled("path-configurations-section", true);
- base::Value::List paths;
+ Value::List paths;
AppendKeyValue(paths, "Downloads",
file_manager::util::GetDownloadsFolderForProfile(profile())
.AsUTF8Unsafe());
@@ -499,21 +475,23 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
prefs::kSaveFileDefaultDirectory,
prefs::kDownloadDefaultDirectory,
};
+
for (const char* key : kPathPreferences) {
AppendKeyValue(paths, key, GetPrefs()->GetFilePath(key).AsUTF8Unsafe());
}
- MaybeCallJavascript("updatePathConfigurations",
- base::Value(std::move(paths)));
+ MaybeCallJavascript("updatePathConfigurations", Value(std::move(paths)));
}
void UpdateDriveDebugSection() {
SetSectionEnabled("drive-debug", true);
-
- bool verbose_logging_enabled =
- GetPrefs()->GetBoolean(drive::prefs::kDriveFsEnableVerboseLogging);
- MaybeCallJavascript("updateVerboseLogging",
- base::Value(verbose_logging_enabled));
+ const PrefService* const prefs = GetPrefs();
+ MaybeCallJavascript(
+ "updateBulkPinningVisible",
+ Value(prefs->GetBoolean(drive::prefs::kDriveFsBulkPinningVisible)));
+ MaybeCallJavascript(
+ "updateVerboseLogging",
+ Value(prefs->GetBoolean(drive::prefs::kDriveFsEnableVerboseLogging)));
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
@@ -522,7 +500,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
weak_ptr_factory_.GetWeakPtr()));
// Propagate the amount of local free space in bytes.
- base::FilePath home_path;
+ FilePath home_path;
if (base::PathService::Get(base::DIR_HOME, &home_path)) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
@@ -544,7 +522,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
bool mirroring_enabled =
GetPrefs()->GetBoolean(drive::prefs::kDriveFsEnableMirrorSync);
- MaybeCallJavascript("updateMirroring", base::Value(mirroring_enabled));
+ MaybeCallJavascript("updateMirroring", Value(mirroring_enabled));
SetSectionEnabled("mirror-sync-paths", mirroring_enabled);
SetSectionEnabled("mirror-path-form", mirroring_enabled);
if (!mirroring_enabled) {
@@ -562,20 +540,20 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
void OnGetSyncingPaths(drive::FileError status,
- const std::vector<base::FilePath>& paths) {
+ const std::vector<FilePath>& paths) {
if (status != drive::FILE_ERROR_OK) {
LOG(ERROR) << "Error retrieving syncing paths: " << status;
return;
}
- for (const base::FilePath& sync_path : paths) {
+ for (const FilePath& sync_path : paths) {
MaybeCallJavascript(
- "onAddSyncPath", base::Value(sync_path.value()),
- base::Value(drive::FileErrorToString(drive::FILE_ERROR_OK)));
+ "onAddSyncPath", Value(sync_path.value()),
+ Value(drive::FileErrorToString(drive::FILE_ERROR_OK)));
}
}
void ToggleSyncPath(drivefs::mojom::MirrorPathStatus status,
- const base::Value::List& args) {
+ const Value::List& args) {
if (!features::IsDriveFsMirroringEnabled()) {
return;
}
@@ -586,7 +564,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
if (args.size() == 1 && args[0].is_string()) {
- const base::FilePath sync_path(args[0].GetString());
+ const FilePath sync_path(args[0].GetString());
auto callback =
base::BindOnce((status == drivefs::mojom::MirrorPathStatus::kStart)
? &DriveInternalsWebUIHandler::OnAddSyncPath
@@ -596,54 +574,45 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
}
- void OnAddSyncPath(const base::FilePath& sync_path, drive::FileError status) {
- MaybeCallJavascript("onAddSyncPath", base::Value(sync_path.value()),
- base::Value(drive::FileErrorToString(status)));
+ void OnAddSyncPath(const FilePath& sync_path, drive::FileError status) {
+ MaybeCallJavascript("onAddSyncPath", Value(sync_path.value()),
+ Value(drive::FileErrorToString(status)));
}
- void OnRemoveSyncPath(const base::FilePath& sync_path,
- drive::FileError status) {
- MaybeCallJavascript("onRemoveSyncPath", base::Value(sync_path.value()),
- base::Value(drive::FileErrorToString(status)));
+ void OnRemoveSyncPath(const FilePath& sync_path, drive::FileError status) {
+ MaybeCallJavascript("onRemoveSyncPath", Value(sync_path.value()),
+ Value(drive::FileErrorToString(status)));
}
- void UpdateBulkPinningSection() {
+ void UpdateBulkPinningDeveloperSection() {
DriveIntegrationService* const service = GetIntegrationService();
if (!service) {
return;
}
- if (pin_manager_) {
- pin_manager_->RemoveObserver(this);
- }
-
- pin_manager_ = service->GetPinManager();
- if (!pin_manager_) {
- LOG(ERROR) << "No DriveFS pin manager";
- SetSectionEnabled("bulk-pinning-section", false);
+ const bool enabled = drive::util::IsDriveFsBulkPinningEnabled(profile());
+ SetSectionEnabled("bulk-pinning-section", enabled);
+ if (!enabled) {
return;
}
- pin_manager_->AddObserver(this);
+ service->RemoveObserver(this);
+ service->AddObserver(this);
- SetSectionEnabled("bulk-pinning-section", true);
- OnProgress(pin_manager_->GetProgress());
- MaybeCallJavascript("updateBulkPinning",
- base::Value(GetPrefs()->GetBoolean(
- drive::prefs::kDriveFsBulkPinningEnabled)));
- }
+ MaybeCallJavascript(
+ "updateBulkPinning",
+ Value(GetPrefs()->GetBoolean(kDriveFsBulkPinningEnabled)));
- void OnDrop() override {
- if (pin_manager_) {
- VLOG(1) << "PinManager dropped before DriveInternalsWebUIHandler";
- pin_manager_ = nullptr;
+ if (PinManager* const manager = service->GetPinManager()) {
+ OnBulkPinProgress(manager->GetProgress());
}
}
- void OnProgress(const drivefs::pinning::Progress& progress) override {
+ void OnBulkPinProgress(const drivefs::pinning::Progress& progress) override {
using drivefs::pinning::HumanReadableSize;
- base::Value::Dict d;
+ Value::Dict d;
+ d.Set("enabled", GetPrefs()->GetBoolean(kDriveFsBulkPinningEnabled));
d.Set("stage", drivefs::pinning::ToString(progress.stage));
d.Set("free_space", ToString(HumanReadableSize(progress.free_space)));
d.Set("required_space",
@@ -670,7 +639,9 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
drivefs::pinning::ToString(progress.time_spent_listing_items));
d.Set("time_spent_pinning_files",
drivefs::pinning::ToString(progress.time_spent_pinning_files));
- MaybeCallJavascript("onBulkPinningProgress", base::Value(std::move(d)));
+ d.Set("remaining_time",
+ drivefs::pinning::ToString(progress.remaining_time));
+ MaybeCallJavascript("onBulkPinningProgress", Value(std::move(d)));
}
// Called when GetDeveloperMode() is complete.
@@ -694,17 +665,18 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
void OnGetStartupArguments(const std::string& arguments) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(developer_mode_);
- MaybeCallJavascript("updateStartupArguments", base::Value(arguments));
+ MaybeCallJavascript("updateStartupArguments", Value(arguments));
SetSectionEnabled("developer-mode-controls", true);
+ UpdateBulkPinningDeveloperSection();
}
// Called when AmountOfFreeDiskSpace() is complete.
void OnGetFreeDiskSpace(int64_t free_space) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- base::Value::Dict local_storage_summary;
+ Value::Dict local_storage_summary;
local_storage_summary.Set("free_space", static_cast<double>(free_space));
MaybeCallJavascript("updateLocalStorageUsage",
- base::Value(std::move(local_storage_summary)));
+ Value(std::move(local_storage_summary)));
}
void UpdateDriveRelatedPreferencesSection() {
@@ -720,7 +692,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
};
PrefService* const prefs = GetPrefs();
- base::Value::List preferences;
+ Value::List preferences;
for (const char* key : kDriveRelatedPreferences) {
// As of now, all preferences are boolean.
AppendKeyValue(preferences, key,
@@ -728,7 +700,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
MaybeCallJavascript("updateDriveRelatedPreferences",
- base::Value(std::move(preferences)));
+ Value(std::move(preferences)));
}
void UpdateEventLogSection() {
@@ -740,9 +712,9 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
const std::vector<drive::EventLogger::Event> log =
- service->event_logger()->GetHistory();
+ service->GetLogger()->GetHistory();
- base::Value::List list;
+ Value::List list;
for (const drive::EventLogger::Event& event : log) {
// Skip events which were already sent.
if (event.id <= last_sent_event_id_) {
@@ -758,7 +730,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
last_sent_event_id_ = event.id;
}
if (!list.empty()) {
- MaybeCallJavascript("updateEventLog", base::Value(std::move(list)));
+ MaybeCallJavascript("updateEventLog", Value(std::move(list)));
}
}
@@ -775,14 +747,14 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
return;
}
- base::FilePath log_path = service->GetDriveFsLogPath();
+ FilePath log_path = service->GetDriveFsLogPath();
if (log_path.empty()) {
return;
}
MaybeCallJavascript(
"updateOtherServiceLogsUrl",
- base::Value(net::FilePathToFileURL(log_path.DirName()).spec()));
+ Value(net::FilePathToFileURL(log_path.DirName()).spec()));
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
@@ -793,7 +765,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
// Called when service logs are read.
- void OnServiceLogRead(std::pair<ino_t, base::Value::List> response) {
+ void OnServiceLogRead(std::pair<ino_t, Value::List> response) {
if (service_log_file_inode_ != response.first) {
service_log_file_inode_ = response.first;
last_sent_line_number_ = 0;
@@ -801,7 +773,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
if (!response.second.empty()) {
last_sent_line_number_ += response.second.size();
MaybeCallJavascript("updateServiceLog",
- base::Value(std::move(response.second)));
+ Value(std::move(response.second)));
}
service_log_file_is_processing_ = false;
}
@@ -814,7 +786,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
void UpdateGCacheContentsSection() {
SetSectionEnabled("gcache-contents-section", true);
- const base::FilePath root_path =
+ const FilePath root_path =
drive::util::GetCacheRootPath(profile()).DirName();
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
@@ -824,16 +796,15 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
// Called when GetGCacheContents() is complete.
- void OnGetGCacheContents(
- std::pair<base::Value::List, base::Value::Dict> response) {
+ void OnGetGCacheContents(std::pair<Value::List, Value::Dict> response) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
MaybeCallJavascript("updateGCacheContents",
- base::Value(std::move(response.first)),
- base::Value(std::move(response.second)));
+ Value(std::move(response.first)),
+ Value(std::move(response.second)));
}
// Called when the "Verbose Logging" checkbox on the page is changed.
- void SetVerboseLoggingEnabled(const base::Value::List& args) {
+ void SetVerboseLoggingEnabled(const Value::List& args) {
AllowJavascript();
DriveIntegrationService* const service = GetIntegrationService();
if (!service) {
@@ -844,11 +815,11 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
bool enabled = args[0].GetBool();
GetPrefs()->SetBoolean(drive::prefs::kDriveFsEnableVerboseLogging,
enabled);
- RestartDrive(base::Value::List());
+ RestartDrive(Value::List());
}
}
- void SetMirroringEnabled(const base::Value::List& args) {
+ void SetMirroringEnabled(const Value::List& args) {
AllowJavascript();
DriveIntegrationService* const service = GetIntegrationService();
if (!service) {
@@ -863,21 +834,37 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
}
- void SetBulkPinningEnabled(const base::Value::List& args) {
+ // Called when the "bulk-pinning-visible" checkbox on the page is changed.
+ void SetBulkPinningVisible(const Value::List& args) {
+ AllowJavascript();
+
+ if (args.size() != 1 || !args[0].is_bool()) {
+ LOG(ERROR) << "Args in not a bool";
+ return;
+ }
+
+ const bool b = args[0].GetBool();
+ VLOG(1) << "Set pref " << drive::prefs::kDriveFsBulkPinningVisible << " to "
+ << b;
+ GetPrefs()->SetBoolean(drive::prefs::kDriveFsBulkPinningVisible, b);
+ }
+
+ void SetBulkPinningEnabled(const Value::List& args) {
AllowJavascript();
if (args.size() != 1 || !args[0].is_bool()) {
- LOG(ERROR) << "args in not a bool";
+ LOG(ERROR) << "Args in not a bool";
return;
}
- const bool enabled = args[0].GetBool();
- GetPrefs()->SetBoolean(drive::prefs::kDriveFsBulkPinningEnabled, enabled);
- UpdateBulkPinningSection();
+ GetPrefs()->SetBoolean(kDriveFsBulkPinningEnabled, args[0].GetBool());
+ UpdateBulkPinningDeveloperSection();
+ drivefs::pinning::RecordBulkPinningEnabledSource(
+ drivefs::pinning::BulkPinningEnabledSource::kDriveInternal);
}
// Called when the "Startup Arguments" field on the page is submitted.
- void SetStartupArguments(const base::Value::List& args) {
+ void SetStartupArguments(const Value::List& args) {
AllowJavascript();
CHECK(developer_mode_);
@@ -903,19 +890,19 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(developer_mode_);
if (success) {
- RestartDrive(base::Value::List());
+ RestartDrive(Value::List());
}
- MaybeCallJavascript("updateStartupArgumentsStatus", base::Value(success));
+ MaybeCallJavascript("updateStartupArgumentsStatus", Value(success));
}
- void SetTracingEnabled(bool enabled, const base::Value::List& args) {
+ void SetTracingEnabled(bool enabled, const Value::List& args) {
AllowJavascript();
if (DriveIntegrationService* const service = GetIntegrationService()) {
service->SetTracingEnabled(enabled);
}
}
- void SetNetworkingEnabled(bool enabled, const base::Value::List& args) {
+ void SetNetworkingEnabled(bool enabled, const Value::List& args) {
AllowJavascript();
CHECK(developer_mode_);
if (DriveIntegrationService* const service = GetIntegrationService()) {
@@ -923,7 +910,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
}
- void ForcePauseSyncing(bool enabled, const base::Value::List& args) {
+ void ForcePauseSyncing(bool enabled, const Value::List& args) {
AllowJavascript();
CHECK(developer_mode_);
if (DriveIntegrationService* const service = GetIntegrationService()) {
@@ -931,7 +918,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
}
- void DumpAccountSettings(const base::Value::List& args) {
+ void DumpAccountSettings(const Value::List& args) {
AllowJavascript();
CHECK(developer_mode_);
if (DriveIntegrationService* const service = GetIntegrationService()) {
@@ -939,7 +926,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
}
- void LoadAccountSettings(const base::Value::List& args) {
+ void LoadAccountSettings(const Value::List& args) {
AllowJavascript();
CHECK(developer_mode_);
if (DriveIntegrationService* const service = GetIntegrationService()) {
@@ -948,7 +935,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
// Called when the "Restart Drive" button on the page is pressed.
- void RestartDrive(const base::Value::List& args) {
+ void RestartDrive(const Value::List& args) {
AllowJavascript();
if (DriveIntegrationService* const service = GetIntegrationService()) {
@@ -957,7 +944,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
// Called when the corresponding button on the page is pressed.
- void ResetDriveFileSystem(const base::Value::List& args) {
+ void ResetDriveFileSystem(const Value::List& args) {
AllowJavascript();
if (DriveIntegrationService* const service = GetIntegrationService()) {
@@ -967,7 +954,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
}
}
- void ZipDriveFsLogs(const base::Value::List& args) {
+ void ZipDriveFsLogs(const Value::List& args) {
AllowJavascript();
DriveIntegrationService* const service = GetIntegrationService();
@@ -981,7 +968,7 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
// Called after file system reset for ResetDriveFileSystem is done.
void ResetFinished(bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- MaybeCallJavascript("updateResetStatus", base::Value(success));
+ MaybeCallJavascript("updateResetStatus", Value(success));
}
Profile* profile() { return Profile::FromWebUI(web_ui()); }
@@ -993,11 +980,19 @@ class DriveInternalsWebUIHandler : public content::WebUIMessageHandler,
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DriveIntegrationService* const service =
drive::DriveIntegrationServiceFactory::FindForProfile(profile());
- return service && service->is_enabled() ? service : nullptr;
- }
- // DriveFS bulk-pinning manager.
- raw_ptr<PinManager> pin_manager_ = nullptr;
+ if (!service) {
+ LOG(ERROR) << "No DriveFS integration service";
+ return nullptr;
+ }
+
+ if (!service->is_enabled()) {
+ LOG(ERROR) << "DriveFS integration service is disabled";
+ return nullptr;
+ }
+
+ return service;
+ }
// The last event sent to the JavaScript side.
int last_sent_event_id_ = -1;
@@ -1043,7 +1038,7 @@ class LogsZipper : public download::AllDownloadItemNotifier::Observer {
private:
static constexpr char kLogsZipName[] = "drivefs_logs.zip";
- void ZipLogFiles(const std::vector<base::FilePath>& files) {
+ void ZipLogFiles(const std::vector<FilePath>& files) {
const scoped_refptr<ZipFileCreator> creator =
base::MakeRefCounted<ZipFileCreator>(logs_directory_, files, zip_path_);
creator->SetCompletionCallback(base::BindOnce(
@@ -1051,18 +1046,17 @@ class LogsZipper : public download::AllDownloadItemNotifier::Observer {
creator->Start(LaunchFileUtilService());
}
- static std::vector<base::FilePath> EnumerateLogFiles(
- base::FilePath logs_path,
- base::FilePath zip_path) {
+ static std::vector<FilePath> EnumerateLogFiles(FilePath logs_path,
+ FilePath zip_path) {
// Note: this may be racy if multiple attempts to export logs are run
// concurrently, but it's a debug page and it requires explicit action to
// cause problems.
base::DeleteFile(zip_path);
- std::vector<base::FilePath> log_files;
- base::FileEnumerator enumerator(logs_path, false /* recursive */,
- base::FileEnumerator::FILES);
+ std::vector<FilePath> log_files;
+ FileEnumerator enumerator(logs_path, false /* recursive */,
+ FileEnumerator::FILES);
- for (base::FilePath current = enumerator.Next(); !current.empty();
+ for (FilePath current = enumerator.Next(); !current.empty();
current = enumerator.Next()) {
if (!current.MatchesExtension(".zip")) {
log_files.push_back(current.BaseName());
@@ -1103,8 +1097,8 @@ class LogsZipper : public download::AllDownloadItemNotifier::Observer {
}
const raw_ptr<Profile, ExperimentalAsh> profile_;
- const base::FilePath logs_directory_;
- const base::FilePath zip_path_;
+ const FilePath logs_directory_;
+ const FilePath zip_path_;
const base::WeakPtr<DriveInternalsWebUIHandler> drive_internals_;
diff --git a/chromium/chrome/browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler.cc b/chromium/chrome/browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler.cc
index 1d26cfed0ea..09cf35a8859 100644
--- a/chromium/chrome/browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler.cc
@@ -296,8 +296,6 @@ void EduCoexistenceLoginHandler::SendInitializeEduArgs() {
params.Set("deviceId", GetDeviceIdForActiveUserProfile());
params.Set("signinTime", GetSigninTime().ToJsTimeIgnoringNull());
- // TODO(crbug.com/1202135): Remove along with JS part.
- params.Set("newOobeLayoutEnabled", true);
// If the secondary edu account is being reauthenticated, the email address
// will be provided via the url of the webcontent. Example
diff --git a/chromium/chrome/browser/ui/webui/ash/emoji/BUILD.gn b/chromium/chrome/browser/ui/webui/ash/emoji/BUILD.gn
index e334cd3427b..a1048270c90 100644
--- a/chromium/chrome/browser/ui/webui/ash/emoji/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/ash/emoji/BUILD.gn
@@ -8,7 +8,10 @@ import("//mojo/public/tools/bindings/mojom.gni")
assert(is_chromeos_ash)
mojom("mojo_bindings") {
- sources = [ "emoji_picker.mojom" ]
+ sources = [
+ "emoji_picker.mojom",
+ "new_window_proxy.mojom",
+ ]
public_deps = [
"//mojo/public/mojom/base",
"//ui/gfx/geometry/mojom",
diff --git a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.cc b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.cc
index 224e1d6468d..4d13488b76a 100644
--- a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/ash/emoji/emoji_page_handler.h"
#include "ash/constants/ash_features.h"
+#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/system/toast_data.h"
#include "ash/public/cpp/system/toast_manager.h"
#include "base/memory/raw_ptr.h"
@@ -17,6 +18,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/emoji/emoji_ui.h"
#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
#include "content/public/browser/storage_partition.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/ime/ash/ime_bridge.h"
@@ -105,11 +107,15 @@ class InsertObserver : public ui::InputMethodObserver {
void OnTextInputStateChanged(const ui::TextInputClient* client) override {
focus_change_count_++;
- // 2 focus changes - 1 for loss of focus in emoji picker, second for
- // focusing in the new text field. You would expect this to fail if
- // the emoji picker window does not have focus in the text field, but
- // waiting for 2 focus changes is still correct behavior.
- if (focus_change_count_ == 2) {
+ // At least 2 focus changes - 1 for loss of focus in emoji picker, second
+ // for focusing in the new text field.
+ // And in lacros, we may expect third change to correct text input type (
+ // from initial value to actual correct value).
+ // You would expect this to fail if the emoji picker window does not have
+ // focus in the text field, but waiting for at least 2 focus changes is
+ // still correct behavior.
+
+ if (focus_change_count_ >= 2) {
// Need to get the client via the IME as InsertText is non-const.
// Can't use this->ime_ either as it may not be active, want to ensure
// that we get the active IME.
@@ -133,12 +139,11 @@ class InsertObserver : public ui::InputMethodObserver {
}
PerformInsert(input_client);
- DestroySelf();
+ if (this->inserted_) {
+ DestroySelf();
+ }
return;
}
- if (focus_change_count_ > 2) {
- DestroySelf();
- }
}
void OnFocus() override {}
void OnBlur() override {}
@@ -157,7 +162,7 @@ class InsertObserver : public ui::InputMethodObserver {
}
int focus_change_count_ = 0;
base::OneShotTimer delete_timer_;
- raw_ptr<ui::InputMethod, ExperimentalAsh> ime_;
+ raw_ptr<ui::InputMethod, LeakedDanglingUntriaged | ExperimentalAsh> ime_;
bool inserted_ = false;
};
@@ -170,6 +175,12 @@ class EmojiObserver : public InsertObserver {
: InsertObserver(ime), emoji_to_insert_(emoji_to_insert) {}
void PerformInsert(ui::TextInputClient* input_client) override {
+ if (input_client->GetTextInputType() ==
+ ui::TextInputType::TEXT_INPUT_TYPE_NONE) {
+ // In some clients (e.g. Sheets), there is an extra focus before the
+ // "real" text input field. so we skip this insertion.
+ return;
+ }
input_client->InsertText(
base::UTF8ToUTF16(emoji_to_insert_),
ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
@@ -213,6 +224,18 @@ EmojiPageHandler::EmojiPageHandler(
incognito_mode_(incognito_mode),
no_text_field_(no_text_field) {
Profile* profile = Profile::FromWebUI(web_ui);
+
+ // There are two conditions to control the GIF support:
+ // 1. Feature flag is turned on.
+ // 2. For managed users, the policy is turned on.
+ gif_support_enabled_ =
+ base::FeatureList::IsEnabled(features::kImeSystemEmojiPickerGIFSupport) &&
+ (profile->GetPrefs()->IsManagedPreference(
+ prefs::kEmojiPickerGifSupportEnabled)
+ ? profile->GetPrefs()->GetBoolean(
+ prefs::kEmojiPickerGifSupportEnabled)
+ : true);
+
url_loader_factory_ = profile->GetDefaultStoragePartition()
->GetURLLoaderFactoryForBrowserProcess();
}
@@ -242,10 +265,15 @@ void EmojiPageHandler::GetFeatureList(GetFeatureListCallback callback) {
enabled_features.push_back(
emoji_picker::mojom::Feature::EMOJI_PICKER_SEARCH_EXTENSION);
}
- if (base::FeatureList::IsEnabled(features::kImeSystemEmojiPickerGIFSupport)) {
+ if (gif_support_enabled_) {
enabled_features.push_back(
emoji_picker::mojom::Feature::EMOJI_PICKER_GIF_SUPPORT);
}
+ if (base::FeatureList::IsEnabled(
+ features::kImeSystemEmojiPickerJellySupport)) {
+ enabled_features.push_back(
+ emoji_picker::mojom::Feature::EMOJI_PICKER_JELLY_SUPPORT);
+ }
std::move(callback).Run(enabled_features);
}
diff --git a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.h b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.h
index 5ffd5486eea..77868ca71a7 100644
--- a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_page_handler.h
@@ -52,6 +52,7 @@ class EmojiPageHandler : public emoji_picker::mojom::PageHandler {
base::TimeTicks shown_time_;
const raw_ptr<EmojiUI, ExperimentalAsh> webui_controller_;
+ bool gif_support_enabled_;
bool incognito_mode_;
bool no_text_field_;
GifTenorApiFetcher gif_tenor_api_fetcher_;
diff --git a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom
index e55f6ead910..d11e0ffb602 100644
--- a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom
+++ b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_picker.mojom
@@ -18,6 +18,8 @@ enum Feature {
// Extends the emoji picker to include gifs as an insertable option for the
// user.
EMOJI_PICKER_GIF_SUPPORT = 2,
+ // Extends the emoji picker to display jelly coloring.
+ EMOJI_PICKER_JELLY_SUPPORT = 3,
};
// The below structs are used to represent the Response format from the Tenor
diff --git a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.cc b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.cc
index 356b2d3f869..198485e9a79 100644
--- a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.cc
@@ -25,6 +25,7 @@
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/ime/ash/ime_bridge.h"
#include "ui/resources/grit/webui_resources.h"
+#include "ui/webui/color_change_listener/color_change_handler.h"
namespace {
constexpr gfx::Size kExtensionWindowSize(420, 480);
@@ -138,6 +139,12 @@ void EmojiUI::Show(Profile* profile) {
WEB_UI_CONTROLLER_TYPE_IMPL(EmojiUI)
void EmojiUI::BindInterface(
+ mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
+ color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
+ web_ui()->GetWebContents(), std::move(receiver));
+}
+
+void EmojiUI::BindInterface(
mojo::PendingReceiver<emoji_picker::mojom::PageHandlerFactory> receiver) {
page_factory_receiver_.reset();
page_factory_receiver_.Bind(std::move(receiver));
diff --git a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.h b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.h
index 1e04014eb6e..ef685c4f467 100644
--- a/chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.h
+++ b/chromium/chrome/browser/ui/webui/ash/emoji/emoji_ui.h
@@ -19,9 +19,14 @@
#include "mojo/public/cpp/bindings/receiver.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/webui/mojo_bubble_web_ui_controller.h"
+#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
class Profile;
+namespace ui {
+class ColorChangeHandler;
+} // namespace ui
+
namespace ash {
class EmojiUI;
@@ -45,6 +50,12 @@ class EmojiUI : public ui::MojoBubbleWebUIController,
static bool ShouldShow(const ui::TextInputClient* input_client);
static void Show(Profile* profile);
+ // Instantiates the implementor of the mojom::PageHandler mojo interface
+ // passing the pending receiver that will be internally bound.
+ void BindInterface(
+ mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+ receiver);
+
// Instantiates the implementor of the mojom::PageHandlerFactory mojo
// interface passing the pending receiver that will be internally bound.
void BindInterface(
@@ -55,12 +66,13 @@ class EmojiUI : public ui::MojoBubbleWebUIController,
receiver) override;
private:
+ std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
std::unique_ptr<EmojiPageHandler> page_handler_;
mojo::Receiver<emoji_picker::mojom::PageHandlerFactory>
page_factory_receiver_{this};
- bool incognito_mode_;
- bool no_text_field_;
+ bool incognito_mode_ = false;
+ bool no_text_field_ = false;
WEB_UI_CONTROLLER_TYPE_DECL();
};
diff --git a/chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.cc b/chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.cc
new file mode 100644
index 00000000000..abfcaef4a6e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.cc
@@ -0,0 +1,23 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/emoji/new_window_proxy.h"
+
+#include "ash/public/cpp/new_window_delegate.h"
+
+namespace ash {
+
+NewWindowProxy::NewWindowProxy(
+ mojo::PendingReceiver<new_window_proxy::mojom::NewWindowProxy> receiver)
+ : receiver_(this, std::move(receiver)) {}
+
+NewWindowProxy::~NewWindowProxy() {}
+
+void NewWindowProxy::OpenUrl(const GURL& url) {
+ ash::NewWindowDelegate::GetPrimary()->OpenUrl(
+ url, ash::NewWindowDelegate::OpenUrlFrom::kUnspecified,
+ ash::NewWindowDelegate::Disposition::kNewForegroundTab);
+}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.h b/chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.h
new file mode 100644
index 00000000000..a86ed8cad7e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.h
@@ -0,0 +1,29 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_EMOJI_NEW_WINDOW_PROXY_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_EMOJI_NEW_WINDOW_PROXY_H_
+
+#include "chrome/browser/ui/webui/ash/emoji/new_window_proxy.mojom.h"
+#include "content/public/browser/render_frame_host.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace ash {
+
+class NewWindowProxy : public new_window_proxy::mojom::NewWindowProxy {
+ public:
+ explicit NewWindowProxy(
+ mojo::PendingReceiver<new_window_proxy::mojom::NewWindowProxy> receiver);
+ ~NewWindowProxy() override;
+
+ void OpenUrl(const GURL& url) override;
+
+ private:
+ mojo::Receiver<new_window_proxy::mojom::NewWindowProxy> receiver_;
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_EMOJI_NEW_WINDOW_PROXY_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.mojom b/chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.mojom
new file mode 100644
index 00000000000..3afbc2f7822
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/emoji/new_window_proxy.mojom
@@ -0,0 +1,13 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module new_window_proxy.mojom;
+
+import "url/mojom/url.mojom";
+
+// Used by the WebUI to open new browser windows.
+interface NewWindowProxy {
+ // Request to open a new tab in primary browser with the given url
+ OpenUrl(url.mojom.Url url);
+};
diff --git a/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.cc b/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.cc
index b7dc77d0e63..c2b593814b8 100644
--- a/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.cc
@@ -12,6 +12,7 @@
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
+#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
@@ -102,7 +103,7 @@ class DeviceEmulatorMessageHandler::BluetoothObserver
void DeviceRemoved(const dbus::ObjectPath& object_path) override;
private:
- DeviceEmulatorMessageHandler* owner_;
+ raw_ptr<DeviceEmulatorMessageHandler, ExperimentalAsh> owner_;
};
void DeviceEmulatorMessageHandler::BluetoothObserver::DeviceAdded(
@@ -152,7 +153,7 @@ class DeviceEmulatorMessageHandler::CrasAudioObserver
}
private:
- DeviceEmulatorMessageHandler* owner_;
+ raw_ptr<DeviceEmulatorMessageHandler, ExperimentalAsh> owner_;
};
class DeviceEmulatorMessageHandler::PowerObserver
@@ -172,7 +173,7 @@ class DeviceEmulatorMessageHandler::PowerObserver
void PowerChanged(const power_manager::PowerSupplyProperties& proto) override;
private:
- DeviceEmulatorMessageHandler* owner_;
+ raw_ptr<DeviceEmulatorMessageHandler, ExperimentalAsh> owner_;
};
void DeviceEmulatorMessageHandler::PowerObserver::PowerChanged(
diff --git a/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.h b/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.h
index 97a5594d17b..324117ecfb2 100644
--- a/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_message_handler.h
@@ -7,6 +7,7 @@
#include <memory>
+#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ash/system/pointer_device_observer.h"
@@ -131,12 +132,14 @@ class DeviceEmulatorMessageHandler
void UpdateAudioNodes();
- bluez::FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
+ raw_ptr<bluez::FakeBluetoothDeviceClient, ExperimentalAsh>
+ fake_bluetooth_device_client_;
std::unique_ptr<BluetoothObserver> bluetooth_observer_;
std::unique_ptr<CrasAudioObserver> cras_audio_observer_;
- chromeos::FakePowerManagerClient* fake_power_manager_client_;
+ raw_ptr<chromeos::FakePowerManagerClient, ExperimentalAsh>
+ fake_power_manager_client_;
std::unique_ptr<PowerObserver> power_observer_;
scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
diff --git a/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_ui.cc b/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_ui.cc
index 568e2e5dac8..8c381d722dc 100644
--- a/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/emulator/device_emulator_ui.cc
@@ -6,6 +6,7 @@
#include <memory>
+#include "ash/webui/common/trusted_types_util.h"
#include "base/system/sys_info.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
@@ -25,8 +26,7 @@ void CreateAndAddDeviceEmulatorUIDataSource(content::WebUI* web_ui) {
content::WebUIDataSource* html = content::WebUIDataSource::CreateAndAdd(
web_ui->GetWebContents()->GetBrowserContext(),
chrome::kChromeUIDeviceEmulatorHost);
-
- html->DisableTrustedTypesCSP();
+ ash::EnableTrustedTypesCSP(html);
// Add resources.
html->AddResourcePath("audio_settings.js",
diff --git a/chromium/chrome/browser/ui/webui/waffle/OWNERS b/chromium/chrome/browser/ui/webui/ash/enterprise_reporting/OWNERS
index c3c51f1df48..f298905fcb2 100644
--- a/chromium/chrome/browser/ui/webui/waffle/OWNERS
+++ b/chromium/chrome/browser/ui/webui/ash/enterprise_reporting/OWNERS
@@ -1,3 +1,5 @@
-file://chrome/browser/ui/waffle/OWNERS
+file://components/reporting/OWNERS
+
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
+
diff --git a/chromium/chrome/browser/ui/webui/ash/in_session_password_change/password_change_ui.cc b/chromium/chrome/browser/ui/webui/ash/in_session_password_change/password_change_ui.cc
index 700fe010afa..dfb56a509f2 100644
--- a/chromium/chrome/browser/ui/webui/ash/in_session_password_change/password_change_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/in_session_password_change/password_change_ui.cc
@@ -7,6 +7,7 @@
#include <memory>
#include "ash/constants/ash_switches.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/json/json_writer.h"
@@ -94,7 +95,7 @@ PasswordChangeUI::PasswordChangeUI(content::WebUI* web_ui)
prefs::kSamlInSessionPasswordChangeEnabled));
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
profile, chrome::kChromeUIPasswordChangeHost);
- webui::EnableTrustedTypesCSP(source);
+ ash::EnableTrustedTypesCSP(source);
const std::string password_change_url = GetPasswordChangeUrl(profile);
web_ui->AddMessageHandler(
@@ -128,7 +129,7 @@ ConfirmPasswordChangeUI::ConfirmPasswordChangeUI(content::WebUI* web_ui)
prefs::kSamlInSessionPasswordChangeEnabled));
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
profile, chrome::kChromeUIConfirmPasswordChangeHost);
- webui::EnableTrustedTypesCSP(source);
+ ash::EnableTrustedTypesCSP(source);
static constexpr webui::LocalizedString kLocalizedStrings[] = {
{"title", IDS_PASSWORD_CHANGE_CONFIRM_DIALOG_TITLE},
@@ -180,7 +181,7 @@ UrgentPasswordExpiryNotificationUI::UrgentPasswordExpiryNotificationUI(
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
profile, chrome::kChromeUIUrgentPasswordExpiryNotificationHost);
- webui::EnableTrustedTypesCSP(source);
+ ash::EnableTrustedTypesCSP(source);
SamlPasswordAttributes attrs = SamlPasswordAttributes::LoadFromPrefs(prefs);
if (attrs.has_expiration_time()) {
diff --git a/chromium/chrome/browser/ui/webui/ash/internet_config_dialog.cc b/chromium/chrome/browser/ui/webui/ash/internet_config_dialog.cc
index e13437fd6a2..3bb7461e4b0 100644
--- a/chromium/chrome/browser/ui/webui/ash/internet_config_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/ash/internet_config_dialog.cc
@@ -4,8 +4,8 @@
#include "chrome/browser/ui/webui/ash/internet_config_dialog.h"
-#include "ash/constants/ash_features.h"
#include "ash/public/cpp/network_config_service.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
@@ -152,10 +152,8 @@ std::string InternetConfigDialog::GetDialogArgs() const {
// Provide the UI with information on whether a user is currently logged in.
// This information is used to avoid an edge case when configuring a network.
// For more information see b/253247084.
- if (base::FeatureList::IsEnabled(ash::features::kHiddenNetworkMigration)) {
- args.Set("loggedIn", base::Value(LoginState::IsInitialized() &&
- LoginState::Get()->IsUserLoggedIn()));
- }
+ args.Set("loggedIn", base::Value(LoginState::IsInitialized() &&
+ LoginState::Get()->IsUserLoggedIn()));
std::string json;
base::JSONWriter::Write(args, &json);
return json;
@@ -168,8 +166,6 @@ InternetConfigDialogUI::InternetConfigDialogUI(content::WebUI* web_ui)
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
Profile::FromWebUI(web_ui), chrome::kChromeUIInternetConfigDialogHost);
- source->DisableTrustedTypesCSP();
-
source->AddBoolean("isJellyEnabled", ::chromeos::features::IsJellyEnabled());
AddInternetStrings(source);
source->AddLocalizedString("title", IDS_SETTINGS_INTERNET_CONFIG);
@@ -179,6 +175,10 @@ InternetConfigDialogUI::InternetConfigDialogUI(content::WebUI* web_ui)
base::make_span(kInternetConfigDialogResources,
kInternetConfigDialogResourcesSize),
IDR_INTERNET_CONFIG_DIALOG_INTERNET_CONFIG_DIALOG_CONTAINER_HTML);
+ // Enabling trusted types via trusted_types_util must be done after
+ // webui::SetupWebUIDataSource to override the trusted type CSP with correct
+ // policies for JS WebUIs.
+ ash::EnableTrustedTypesCSP(source);
}
InternetConfigDialogUI::~InternetConfigDialogUI() {}
diff --git a/chromium/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_browsertest.cc b/chromium/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_browsertest.cc
index ee2e274ed64..d1010f32277 100644
--- a/chromium/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_browsertest.cc
@@ -84,7 +84,7 @@ class KerberosInBrowserDialogButtonTest : public InProcessBrowserTest {
}))->Wait();
}
- raw_ptr<content::WebUI, ExperimentalAsh> webui_;
+ raw_ptr<content::WebUI, DanglingUntriaged | ExperimentalAsh> webui_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_network_ui.cc b/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_network_ui.cc
index e7c3e1fd5b4..2ad0004fec9 100644
--- a/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_network_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_network_ui.cc
@@ -9,6 +9,7 @@
#include <utility>
#include "ash/public/cpp/network_config_service.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/functional/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
@@ -73,7 +74,7 @@ LockScreenNetworkUI::LockScreenNetworkUI(content::WebUI* web_ui)
content::WebUIDataSource* html = content::WebUIDataSource::CreateAndAdd(
web_ui->GetWebContents()->GetBrowserContext(),
chrome::kChromeUILockScreenNetworkHost);
- webui::EnableTrustedTypesCSP(html);
+ ash::EnableTrustedTypesCSP(html);
html->AddLocalizedStrings(localized_strings);
diff --git a/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc b/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc
index 337d87628ca..8332696ca66 100644
--- a/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc
@@ -42,8 +42,9 @@ namespace ash {
namespace {
bool ShouldDoSamlRedirect(const std::string& email) {
- if (email.empty())
+ if (email.empty()) {
return false;
+ }
// If there's a populated email, we must check first that this user is using
// SAML in order to decide whether to show the interstitial page.
@@ -193,22 +194,19 @@ void LockScreenReauthHandler::OnSetCookieForLoadGaiaWithPartition(
"gaiaPath",
GaiaUrls::GetInstance()->embedded_setup_chromeos_url().path().substr(1));
params.Set("clientId", GaiaUrls::GetInstance()->oauth2_chrome_client_id());
- params.Set("dontResizeNonEmbeddedPages", false);
- params.Set("enableGaiaActionButtons", false);
std::string hosted_domain = GetHostedDomain(context.gaia_id);
-
- if (hosted_domain.empty()) {
- LOG(ERROR) << "Couldn't get hosted_domain from account info.";
- params.Set("doSamlRedirect", force_saml_redirect_for_testing_);
+ bool do_saml_redirect =
+ force_saml_redirect_for_testing_ || ShouldDoSamlRedirect(context.email);
+ params.Set("doSamlRedirect", do_saml_redirect);
+ if (force_saml_redirect_for_testing_) {
+ params.Set("enterpriseEnrollmentDomain", kIdpTestingDomain);
+ } else if (!hosted_domain.empty()) {
+ params.Set("enterpriseEnrollmentDomain", hosted_domain);
} else {
- params.Set("enterpriseEnrollmentDomain", force_saml_redirect_for_testing_
- ? kIdpTestingDomain
- : hosted_domain);
- params.Set("doSamlRedirect", force_saml_redirect_for_testing_ ||
- ShouldDoSamlRedirect(context.email));
+ LOG(ERROR) << "Couldn't get hosted_domain from account info.";
}
-
+ params.Set("enableGaiaActionButtons", !do_saml_redirect);
const std::string sso_profile(GetSSOProfile());
if (!sso_profile.empty()) {
params.Set("ssoProfile", sso_profile);
@@ -407,6 +405,11 @@ void LockScreenReauthHandler::SamlConfirmPassword(
}
void LockScreenReauthHandler::HandleWebviewLoadAborted(int error_code) {
+ if (error_code == net::ERR_BLOCKED_BY_ADMINISTRATOR) {
+ // Ignore this error to let the user see the error screen for blocked sites.
+ return;
+ }
+
if (error_code == net::ERR_INVALID_AUTH_CREDENTIALS) {
// Silently ignore this error - it is used as an intermediate state for
// committed interstitials (see https://crbug.com/1049349 for details).
@@ -460,8 +463,9 @@ void LockScreenReauthHandler::RegisterMessages() {
bool LockScreenReauthHandler::IsAuthenticatorLoaded(
base::OnceClosure callback) {
- if (authenticator_state_ == AuthenticatorState::LOADED)
+ if (authenticator_state_ == AuthenticatorState::LOADED) {
return true;
+ }
waiting_caller_ = std::move(callback);
return false;
diff --git a/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc b/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc
index d4e35c055b6..b50400a079a 100644
--- a/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc
@@ -7,6 +7,7 @@
#include <memory>
#include "ash/constants/ash_features.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
@@ -18,6 +19,8 @@
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/gaia_action_buttons_resources.h"
+#include "chrome/grit/gaia_action_buttons_resources_map.h"
#include "chrome/grit/gaia_auth_host_resources_map.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/lock_screen_reauth_resources.h"
@@ -48,7 +51,7 @@ LockScreenStartReauthUI::LockScreenStartReauthUI(content::WebUI* web_ui)
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
profile, chrome::kChromeUILockScreenStartReauthHost);
- webui::EnableTrustedTypesCSP(source);
+ ash::EnableTrustedTypesCSP(source);
auto main_handler = std::make_unique<LockScreenReauthHandler>(email);
main_handler_ = main_handler.get();
@@ -137,6 +140,8 @@ LockScreenStartReauthUI::LockScreenStartReauthUI(content::WebUI* web_ui)
source->AddResourcePaths(base::make_span(kLockScreenReauthResources,
kLockScreenReauthResourcesSize));
+ source->AddResourcePaths(base::make_span(kGaiaActionButtonsResources,
+ kGaiaActionButtonsResourcesSize));
source->SetDefaultResource(
IDR_LOCK_SCREEN_REAUTH_LOCK_SCREEN_REAUTH_APP_HTML);
diff --git a/chromium/chrome/browser/ui/webui/ash/login/add_child_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/add_child_screen_handler.cc
new file mode 100644
index 00000000000..334326e1dc4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/login/add_child_screen_handler.cc
@@ -0,0 +1,45 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/login/add_child_screen_handler.h"
+#include "base/logging.h"
+#include "chrome/browser/ash/login/oobe_screen.h"
+#include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "ui/chromeos/devicetype_utils.h"
+
+namespace ash {
+
+AddChildScreenHandler::AddChildScreenHandler() : BaseScreenHandler(kScreenId) {}
+
+AddChildScreenHandler::~AddChildScreenHandler() = default;
+
+void AddChildScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->AddF("childSignInTitle", IDS_OOBE_USER_CREATION_CHILD_SIGNIN_TITLE,
+ ui::GetChromeOSDeviceName());
+ builder->Add("childSignInSubtitle",
+ IDS_OOBE_USER_CREATION_CHILD_SIGNIN_SUBTITLE);
+ builder->Add("createAccountForChildLabel",
+ IDS_OOBE_USER_CREATION_CHILD_ACCOUNT_CREATION_BUTTON_LABEL);
+ builder->Add("signInForChildLabel",
+ IDS_OOBE_USER_CREATION_CHILD_SIGN_IN_BUTTON_LABEL);
+ builder->AddF("childSignInParentNotificationText",
+ IDS_OOBE_USER_CREATION_CHILD_SIGN_IN_PARENT_NOTIFICATION_TEXT,
+ ui::GetChromeOSDeviceName());
+ builder->Add("childSignInLearnMore",
+ IDS_OOBE_USER_CREATION_CHILD_SIGNIN_LEARN_MORE);
+ builder->Add("childSignInLearnMoreDialogTitle",
+ IDS_OOBE_USER_CREATION_CHILD_SIGN_IN_LEARN_MORE_DIALOG_TITLE);
+ builder->Add("childSignInLearnMoreDialogText",
+ IDS_OOBE_USER_CREATION_CHILD_SIGN_IN_LEARN_MORE_DIALOG_TEXT);
+}
+
+void AddChildScreenHandler::Show() {
+ ShowInWebUI();
+}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/login/add_child_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/add_child_screen_handler.h
new file mode 100644
index 00000000000..2c65d534808
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/login/add_child_screen_handler.h
@@ -0,0 +1,49 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_ADD_CHILD_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_ADD_CHILD_SCREEN_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
+
+namespace ash {
+
+// Interface for dependency injection between AddChildScreen and its
+// WebUI representation.
+class AddChildScreenView : public base::SupportsWeakPtr<AddChildScreenView> {
+ public:
+ inline constexpr static StaticOobeScreenId kScreenId{"add-child",
+ "AddChildScreen"};
+
+ virtual ~AddChildScreenView() = default;
+
+ // Shows the contents of the screen.
+ virtual void Show() = 0;
+};
+
+class AddChildScreenHandler : public BaseScreenHandler,
+ public AddChildScreenView {
+ public:
+ using TView = AddChildScreenView;
+
+ AddChildScreenHandler();
+
+ AddChildScreenHandler(const AddChildScreenHandler&) = delete;
+ AddChildScreenHandler& operator=(const AddChildScreenHandler&) = delete;
+
+ ~AddChildScreenHandler() override;
+
+ // BaseScreenHandler:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+
+ // AddChildScreenView:
+ void Show() override;
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_ADD_CHILD_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.cc
index 26e75f507fb..e9444a17122 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.cc
@@ -14,11 +14,11 @@
#include "chrome/browser/ash/login/oobe_screen.h"
#include "chrome/browser/ash/login/screens/network_error.h"
#include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/network_state_informer.h"
#include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
#include "chrome/grit/chrome_unscaled_resources.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
-#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "components/login/localized_values_builder.h"
#include "ui/base/l10n/l10n_util.h"
@@ -29,17 +29,6 @@ namespace ash {
namespace {
-// Returns network name by service path.
-std::string GetNetworkName(const std::string& service_path) {
- const NetworkState* network =
- NetworkHandler::Get()->network_state_handler()->GetNetworkState(
- service_path);
- if (!network) {
- return std::string();
- }
- return network->name();
-}
-
base::Value::Dict ConvertAppToDict(KioskAppManagerBase::App app) {
base::Value::Dict out_info;
@@ -68,15 +57,9 @@ base::Value::Dict ConvertAppToDict(KioskAppManagerBase::App app) {
AppLaunchSplashScreenHandler::AppLaunchSplashScreenHandler(
const scoped_refptr<NetworkStateInformer>& network_state_informer,
ErrorScreen* error_screen)
- : BaseScreenHandler(kScreenId),
- network_state_informer_(network_state_informer),
- error_screen_(error_screen) {
- network_state_informer_->AddObserver(this);
-}
+ : BaseScreenHandler(kScreenId), error_screen_(error_screen) {}
-AppLaunchSplashScreenHandler::~AppLaunchSplashScreenHandler() {
- network_state_informer_->RemoveObserver(this);
-}
+AppLaunchSplashScreenHandler::~AppLaunchSplashScreenHandler() = default;
void AppLaunchSplashScreenHandler::DeclareLocalizedValues(
::login::LocalizedValuesBuilder* builder) {
@@ -105,13 +88,6 @@ void AppLaunchSplashScreenHandler::Show(KioskAppManagerBase::App app_data) {
DoToggleNetworkConfig(toggle_network_config_on_show_.value());
toggle_network_config_on_show_.reset();
}
- if (network_config_shown_) {
- ShowNetworkConfigureUI();
- }
-}
-
-void AppLaunchSplashScreenHandler::SetNetworkRequired() {
- is_network_required_ = true;
}
void AppLaunchSplashScreenHandler::DeclareJSCallbacks() {
@@ -144,25 +120,15 @@ void AppLaunchSplashScreenHandler::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
}
-void AppLaunchSplashScreenHandler::ShowNetworkConfigureUI() {
+void AppLaunchSplashScreenHandler::ShowNetworkConfigureUI(
+ NetworkStateInformer::State network_state,
+ const std::string& network_name) {
network_config_shown_ = true;
-
- NetworkStateInformer::State state = network_state_informer_->state();
-
- // We should not block users when the network was not required by the
- // controller.
- if (!is_network_required_) {
- state = NetworkStateInformer::ONLINE;
- }
-
- const std::string network_path = network_state_informer_->network_path();
- const std::string network_name = GetNetworkName(network_path);
-
error_screen_->SetUIState(NetworkError::UI_STATE_KIOSK_MODE);
+ error_screen_->SetIsPersistentError(true);
error_screen_->AllowGuestSignin(false);
error_screen_->AllowOfflineLogin(false);
-
- switch (state) {
+ switch (network_state) {
case NetworkStateInformer::CAPTIVE_PORTAL: {
error_screen_->SetErrorState(NetworkError::ERROR_STATE_PORTAL,
network_name);
@@ -175,27 +141,23 @@ void AppLaunchSplashScreenHandler::ShowNetworkConfigureUI() {
network_name);
break;
}
- case NetworkStateInformer::OFFLINE: {
- error_screen_->SetErrorState(NetworkError::ERROR_STATE_OFFLINE,
- network_name);
- break;
- }
case NetworkStateInformer::ONLINE: {
error_screen_->SetErrorState(NetworkError::ERROR_STATE_KIOSK_ONLINE,
network_name);
break;
}
- default:
+ case NetworkStateInformer::OFFLINE:
+ case NetworkStateInformer::CONNECTING:
+ case NetworkStateInformer::UNKNOWN:
error_screen_->SetErrorState(NetworkError::ERROR_STATE_OFFLINE,
network_name);
- NOTREACHED();
break;
}
if (GetCurrentScreen() != ErrorScreenView::kScreenId) {
error_screen_->SetParentScreen(kScreenId);
+ error_screen_->Show(/*context=*/nullptr);
}
- error_screen_->Show(nullptr);
}
void AppLaunchSplashScreenHandler::ShowErrorMessage(
@@ -204,25 +166,6 @@ void AppLaunchSplashScreenHandler::ShowErrorMessage(
KioskAppLaunchError::GetErrorMessage(error));
}
-bool AppLaunchSplashScreenHandler::IsNetworkReady() {
- return network_state_informer_->state() == NetworkStateInformer::ONLINE;
-}
-
-void AppLaunchSplashScreenHandler::UpdateState(
- NetworkError::ErrorReason reason) {
- if (!delegate_) {
- return;
- }
- bool new_online_state =
- network_state_informer_->state() == NetworkStateInformer::ONLINE;
- delegate_->OnNetworkStateChanged(new_online_state);
-
- // Redraw network configure UI when the network state changes.
- if (network_config_shown_) {
- ShowNetworkConfigureUI();
- }
-}
-
void AppLaunchSplashScreenHandler::SetLaunchText(const std::string& text) {
CallExternalAPI("updateMessage", text);
}
@@ -264,6 +207,12 @@ void AppLaunchSplashScreenHandler::ContinueAppLaunch() {
network_config_shown_ = false;
delegate_->OnNetworkConfigFinished();
+
+ // Reset ErrorScreen state to default. We don't update other parameters such
+ // as SetUIState/SetErrorState as those should be updated by the next caller
+ // of the ErrorScreen.
+ error_screen_->SetParentScreen(OOBE_SCREEN_UNKNOWN);
+ error_screen_->SetIsPersistentError(false);
}
void AppLaunchSplashScreenHandler::DoToggleNetworkConfig(bool visible) {
diff --git a/chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h
index 7d0327d14b8..bd858c9165a 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h
@@ -28,10 +28,6 @@ class AppLaunchSplashScreenView {
// Invoked when the network config did prepare network and is closed.
virtual void OnNetworkConfigFinished() {}
-
- // Invoked when network state is changed. `online` is true if the device
- // is connected to the Internet.
- virtual void OnNetworkStateChanged(bool online) {}
};
enum class AppLaunchState {
@@ -48,7 +44,7 @@ class AppLaunchSplashScreenView {
inline constexpr static StaticOobeScreenId kScreenId{"app-launch-splash",
"AppLaunchSplashScreen"};
- virtual ~AppLaunchSplashScreenView() {}
+ virtual ~AppLaunchSplashScreenView() = default;
// Sets screen controller this view belongs to.
virtual void SetDelegate(Delegate* delegate) = 0;
@@ -66,26 +62,19 @@ class AppLaunchSplashScreenView {
virtual void ToggleNetworkConfig(bool visible) = 0;
// Shows the network error and configure UI.
- virtual void ShowNetworkConfigureUI() = 0;
+ virtual void ShowNetworkConfigureUI(NetworkStateInformer::State state,
+ const std::string& network_name) = 0;
// Show a notification bar with error message.
virtual void ShowErrorMessage(KioskAppLaunchError::Error error) = 0;
- // Returns true if the default network has Internet access.
- virtual bool IsNetworkReady() = 0;
-
// Continues app launch after error screen is shown.
virtual void ContinueAppLaunch() = 0;
-
- // Tells the splash screen view that network is required.
- virtual void SetNetworkRequired() = 0;
};
// A class that handles the WebUI hooks for the app launch splash screen.
-class AppLaunchSplashScreenHandler
- : public BaseScreenHandler,
- public AppLaunchSplashScreenView,
- public NetworkStateInformer::NetworkStateInformerObserver {
+class AppLaunchSplashScreenHandler : public BaseScreenHandler,
+ public AppLaunchSplashScreenView {
public:
using TView = AppLaunchSplashScreenView;
@@ -110,14 +99,10 @@ class AppLaunchSplashScreenHandler
void ToggleNetworkConfig(bool visible) override;
void UpdateAppLaunchState(AppLaunchState state) override;
void SetDelegate(Delegate* controller) override;
- void ShowNetworkConfigureUI() override;
+ void ShowNetworkConfigureUI(NetworkStateInformer::State state,
+ const std::string& network_name) override;
void ShowErrorMessage(KioskAppLaunchError::Error error) override;
- bool IsNetworkReady() override;
void ContinueAppLaunch() override;
- void SetNetworkRequired() override;
-
- // NetworkStateInformer::NetworkStateInformerObserver implementation:
- void UpdateState(NetworkError::ErrorReason reason) override;
private:
void SetLaunchText(const std::string& text);
@@ -127,10 +112,8 @@ class AppLaunchSplashScreenHandler
raw_ptr<Delegate, ExperimentalAsh> delegate_ = nullptr;
bool is_shown_ = false;
- bool is_network_required_ = false;
AppLaunchState state_ = AppLaunchState::kPreparingProfile;
- scoped_refptr<NetworkStateInformer> network_state_informer_;
raw_ptr<ErrorScreen, DanglingUntriaged | ExperimentalAsh> error_screen_;
// Whether network configure UI is being shown.
diff --git a/chromium/chrome/browser/ui/webui/ash/login/choobe_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/choobe_screen_handler.cc
index f2f6cccc732..3db7a379f3d 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/choobe_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/choobe_screen_handler.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/ui/webui/ash/login/theme_selection_screen_handler.h"
#include "chrome/grit/generated_resources.h"
#include "components/login/localized_values_builder.h"
+#include "ui/chromeos/devicetype_utils.h"
namespace ash {
@@ -23,7 +24,8 @@ ChoobeScreenHandler::~ChoobeScreenHandler() = default;
void ChoobeScreenHandler::DeclareLocalizedValues(
::login::LocalizedValuesBuilder* builder) {
builder->Add("choobeScreenTitle", IDS_OOBE_CHOOBE_TITLE);
- builder->Add("choobeScreenDescription", IDS_OOBE_CHOOBE_DESCRIPTION);
+ builder->AddF("choobeScreenDescription", IDS_OOBE_CHOOBE_DESCRIPTION,
+ ui::GetChromeOSDeviceTypeResourceId());
builder->Add("choobeScreenSkip", IDS_OOBE_CHOOBE_SKIP_BUTTON);
builder->Add("choobeReturnButton", IDS_OOBE_CHOOBE_RETURN_BUTTON);
diff --git a/chromium/chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.cc
index fbeb4162e17..c8506cb8727 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.cc
@@ -8,7 +8,7 @@
#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/values.h"
-#include "chrome/browser/ash/account_manager/account_apps_availability.h"
+#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/login/oobe_screen.h"
#include "chrome/browser/ash/login/screens/consolidated_consent_screen.h"
#include "chrome/grit/chromium_strings.h"
@@ -70,7 +70,7 @@ void ConsolidatedConsentScreenHandler::DeclareLocalizedValues(
builder->Add("consolidatedConsentFooter", IDS_CONSOLIDATED_CONSENT_FOOTER);
builder->Add("consolidatedConsentFooterChild",
IDS_CONSOLIDATED_CONSENT_FOOTER_CHILD);
- if (AccountAppsAvailability::IsArcAccountRestrictionsEnabled() &&
+ if (crosapi::browser_util::IsLacrosEnabled() &&
features::IsOsSyncConsentRevampEnabled()) {
builder->Add("consolidatedConsentUsageOptInLearnMore",
IDS_CONSOLIDATED_CONSENT_USAGE_OPT_IN_LEARN_MORE_OWNER_LACROS);
diff --git a/chromium/chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.cc
new file mode 100644
index 00000000000..c83a940245a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.cc
@@ -0,0 +1,94 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h"
+
+#include "base/logging.h"
+
+#include "chrome/browser/ash/login/oobe_screen.h"
+#include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+
+namespace ash {
+
+namespace {
+
+// These values must be kept in sync with UIState in JS code.
+constexpr const char kCheckingForUpdate[] = "checking";
+constexpr const char kUpdateInProgress[] = "update";
+constexpr const char kRestartInProgress[] = "restart";
+constexpr const char kManualReboot[] = "reboot";
+constexpr const char kCellularPermission[] = "cellular";
+
+} // namespace
+
+ConsumerUpdateScreenHandler::ConsumerUpdateScreenHandler()
+ : BaseScreenHandler(kScreenId) {}
+
+ConsumerUpdateScreenHandler::~ConsumerUpdateScreenHandler() = default;
+
+void ConsumerUpdateScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("consumerUpdateScreenAcceptButton",
+ IDS_CONSUMER_UPDATE_ACCEPT_BUTTON);
+ builder->Add("consumerUpdateScreenSkipButton",
+ IDS_CONSUMER_UPDATE_SKIP_BUTTON);
+ builder->Add("consumerUpdateScreenCellularTitle",
+ IDS_CONSUMER_UPDATE_CELLULAR_TITLE);
+ builder->Add("consumerUpdateScreenInProgressTitle",
+ IDS_CONSUMER_UPDATE_PROGRESS_TITLE);
+ builder->Add("consumerUpdateScreenInProgressSubtitle",
+ IDS_CONSUMER_UPDATE_PROGRESS_SUBTITLE);
+ builder->Add("consumerUpdateScreenInProgressAdditionalSubtitle",
+ IDS_CONSUMER_UPDATE_PROGRESS_ADDITIONAL_SUBTITLE);
+}
+
+void ConsumerUpdateScreenHandler::Show() {
+ ShowInWebUI();
+}
+
+void ConsumerUpdateScreenHandler::SetUpdateState(
+ ConsumerUpdateScreenView::UIState value) {
+ switch (value) {
+ case ConsumerUpdateScreenView::UIState::kCheckingForUpdate:
+ CallExternalAPI("setUpdateState", kCheckingForUpdate);
+ break;
+ case ConsumerUpdateScreenView::UIState::kUpdateInProgress:
+ CallExternalAPI("setUpdateState", kUpdateInProgress);
+ break;
+ case ConsumerUpdateScreenView::UIState::kRestartInProgress:
+ CallExternalAPI("setUpdateState", kRestartInProgress);
+ break;
+ case ConsumerUpdateScreenView::UIState::kManualReboot:
+ CallExternalAPI("setUpdateState", kManualReboot);
+ break;
+ case ConsumerUpdateScreenView::UIState::kCellularPermission:
+ CallExternalAPI("setUpdateState", kCellularPermission);
+ break;
+ }
+}
+
+void ConsumerUpdateScreenHandler::SetUpdateStatus(
+ int percent,
+ const std::u16string& percent_message,
+ const std::u16string& timeleft_message) {
+ CallExternalAPI("setUpdateStatus", percent, percent_message,
+ timeleft_message);
+}
+
+void ConsumerUpdateScreenHandler::ShowLowBatteryWarningMessage(bool value) {
+ CallExternalAPI("showLowBatteryWarningMessage", value);
+}
+
+void ConsumerUpdateScreenHandler::SetAutoTransition(bool value) {
+ CallExternalAPI("setAutoTransition", value);
+}
+
+void ConsumerUpdateScreenHandler::SetIsUpdateMandatory(bool value) {
+ CallExternalAPI("setIsUpdateMandatory", value);
+}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h
new file mode 100644
index 00000000000..e0eb82b3692
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h
@@ -0,0 +1,81 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_CONSUMER_UPDATE_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_CONSUMER_UPDATE_SCREEN_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/ash/accessibility/accessibility_manager.h"
+#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
+
+namespace ash {
+
+class ConsumerUpdateScreen;
+
+// Interface for dependency injection between ConsumerUpdateScreen and its
+// WebUI representation.
+class ConsumerUpdateScreenView
+ : public base::SupportsWeakPtr<ConsumerUpdateScreenView> {
+ public:
+ inline constexpr static StaticOobeScreenId kScreenId{"consumer-update",
+ "ConsumerUpdateScreen"};
+
+ enum class UIState {
+ kCheckingForUpdate = 0,
+ kUpdateInProgress = 1,
+ kRestartInProgress = 2,
+ kManualReboot = 3,
+ kCellularPermission = 4,
+ };
+
+ virtual ~ConsumerUpdateScreenView() = default;
+
+ // Shows the contents of the screen.
+ virtual void Show() = 0;
+
+ virtual void SetUpdateState(UIState value) = 0;
+ virtual void SetUpdateStatus(int percent,
+ const std::u16string& percent_message,
+ const std::u16string& timeleft_message) = 0;
+ virtual void ShowLowBatteryWarningMessage(bool value) = 0;
+ virtual void SetAutoTransition(bool value) = 0;
+ virtual void SetIsUpdateMandatory(bool value) = 0;
+};
+
+class ConsumerUpdateScreenHandler : public BaseScreenHandler,
+ public ConsumerUpdateScreenView {
+ public:
+ using TView = ConsumerUpdateScreenView;
+
+ ConsumerUpdateScreenHandler();
+
+ ConsumerUpdateScreenHandler(const ConsumerUpdateScreenHandler&) = delete;
+ ConsumerUpdateScreenHandler& operator=(const ConsumerUpdateScreenHandler&) =
+ delete;
+
+ ~ConsumerUpdateScreenHandler() override;
+
+ // ConsumerUpdateScreenView:
+ void Show() override;
+
+ void SetUpdateState(ConsumerUpdateScreenView::UIState value) override;
+ void SetUpdateStatus(int percent,
+ const std::u16string& percent_message,
+ const std::u16string& timeleft_message) override;
+ void ShowLowBatteryWarningMessage(bool value) override;
+ void SetAutoTransition(bool value) override;
+ void SetIsUpdateMandatory(bool value) override;
+
+ void OnAccessibilityStatusChanged(
+ const AccessibilityStatusEventDetails& details);
+
+ // BaseScreenHandler:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_CONSUMER_UPDATE_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc
index bfcbf31e3c8..7f8db06ca22 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc
@@ -13,6 +13,7 @@
#include "base/values.h"
#include "chrome/browser/ash/login/configuration_keys.h"
#include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
+#include "chrome/browser/ash/login/oobe_screen.h"
#include "chrome/browser/ash/login/ui/login_display_host.h"
#include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
#include "chrome/browser/ui/ash/ash_util.h"
@@ -95,8 +96,9 @@ void CoreOobeHandler::ShowScreenWithData(
const OobeScreenId& screen,
absl::optional<base::Value::Dict> data) {
const bool is_safe_priority_call =
- IsPriorityScreen(screen.name) &&
- ui_init_state_ == UiState::kPriorityScreensLoaded;
+ ui_init_state_ == UiState::kPriorityScreensLoaded &&
+ PriorityScreenChecker::IsPriorityScreen(screen);
+
CHECK(ui_init_state_ == UiState::kFullyInitialized || is_safe_priority_call);
base::Value::Dict screen_params;
@@ -169,16 +171,6 @@ void CoreOobeHandler::SetBluetoothDeviceInfo(
CallJS("cr.ui.Oobe.setBluetoothDeviceInfo", bluetooth_name);
}
-bool CoreOobeHandler::IsPriorityScreen(const std::string& screen_name) {
- // List of screens that are supported for prioritization. Currently, only the
- // Welcome Screen ('connect') is supported.
- const std::vector<std::string> supported_screens{"connect"};
-
- const auto iter = std::find(supported_screens.begin(),
- supported_screens.end(), screen_name);
- return iter != supported_screens.end();
-}
-
void CoreOobeHandler::HandleInitializeCoreHandler() {
VLOG(3) << "CoreOobeHandler::HandleInitializeCoreHandler";
CHECK(ui_init_state_ == UiState::kUninitialized);
diff --git a/chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.h b/chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.h
index 6631a55fec9..5d08eaecb6b 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/core_oobe_handler.h
@@ -13,6 +13,8 @@
#include "chrome/browser/ash/login/oobe_configuration.h"
#include "chrome/browser/ash/login/oobe_screen.h"
#include "chrome/browser/ui/webui/ash/login/base_webui_handler.h"
+#include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
#include "ui/events/event_source.h"
namespace ui {
@@ -21,6 +23,22 @@ class EventSink;
namespace ash {
+class PriorityScreenChecker {
+ public:
+ static bool IsPriorityScreen(OobeScreenId screen_id) {
+ for (const auto& priority_screen : priority_screens_) {
+ if (screen_id == priority_screen) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ constexpr static StaticOobeScreenId priority_screens_[] = {
+ WelcomeView::kScreenId, UserCreationView::kScreenId};
+};
+
class CoreOobeView : public base::SupportsWeakPtr<CoreOobeView> {
public:
// Possible Initialization States of the UI
@@ -55,10 +73,6 @@ class CoreOobeView : public base::SupportsWeakPtr<CoreOobeView> {
virtual void SetVirtualKeyboardShown(bool shown) = 0;
virtual void SetOsVersionLabelText(const std::string& label_text) = 0;
virtual void SetBluetoothDeviceInfo(const std::string& bluetooth_name) = 0;
-
- // Whether the screen being checked belongs to the group of screens that are
- // prioritized during OOBE's initialization.
- virtual bool IsPriorityScreen(const std::string& screen_name) = 0;
};
// The core handler for Javascript messages related to the "oobe" view.
@@ -99,7 +113,6 @@ class CoreOobeHandler : public BaseWebUIHandler,
void SetVirtualKeyboardShown(bool shown) override;
void SetOsVersionLabelText(const std::string& label_text) override;
void SetBluetoothDeviceInfo(const std::string& bluetooth_name) override;
- bool IsPriorityScreen(const std::string& screen_name) override;
// ---- END --- CoreOobeView
// ---- Handlers for JS WebUI messages.
diff --git a/chromium/chrome/browser/ui/webui/ash/login/display_size_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/display_size_screen_handler.cc
index 716ca030498..a0a1bd2c135 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/display_size_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/display_size_screen_handler.cc
@@ -46,6 +46,8 @@ void DisplaySizeScreenHandler::DeclareLocalizedValues(
// CHOOBE resources
builder->Add("choobeDisplaySizeTitle",
IDS_OOBE_CHOOBE_DISPLAY_SIZE_TILE_TITLE);
+ builder->Add("choobeDisplaySizeSubtitle",
+ IDS_OOBE_CHOOBE_DISPLAY_SIZE_TILE_SUBTITLE);
}
void DisplaySizeScreenHandler::Show(base::Value::Dict data) {
diff --git a/chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.cc
index 4073b6f86c7..4237b978fb4 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.cc
@@ -24,13 +24,18 @@ void DrivePinningScreenHandler::DeclareLocalizedValues(
builder->Add("DevicePinningScreenTitle", IDS_OOBE_DRIVE_PINNING_TITLE);
builder->Add("DevicePinningScreenDescription",
IDS_OOBE_DRIVE_PINNING_SUBTITLE);
- builder->Add("DevicePinningScreenSpaceDescription",
- IDS_OOBE_DRIVE_PINNING_ADDITIONAL_SUBTITLE);
-
- builder->Add("DevicePinningScreenAcceptButton",
- IDS_OOBE_DRIVE_PINNING_ACCEPT_BUTTON);
- builder->Add("DevicePinningScreenDeclineButton",
- IDS_OOBE_DRIVE_PINNING_DECLINE_BUTTON);
+ builder->Add("DevicePinningScreenToggleTitle",
+ IDS_OOBE_DRIVE_PINNING_TOGGLE_TITLE);
+ builder->Add("DevicePinningScreenToggleSubtitle",
+ IDS_OOBE_DRIVE_PINNING_TOGGLE_SUBTITLE);
+
+ builder->Add("choobeDrivePinningTitle",
+ IDS_OOBE_CHOOBE_DRIVE_PINNING_TILE_TITLE);
+
+ builder->Add("choobeDevicePinningSubtitleEnabled",
+ IDS_OOBE_CHOOBE_DRIVE_PINNING_SUBTITLE_ENABLED);
+ builder->Add("choobeDevicePinningSubtitleDisabled",
+ IDS_OOBE_CHOOBE_DRIVE_PINNING_SUBTITLE_DISABLED);
}
void DrivePinningScreenHandler::SetRequiredSpaceInfo(
@@ -39,8 +44,8 @@ void DrivePinningScreenHandler::SetRequiredSpaceInfo(
CallExternalAPI("setRequiredSpaceInfo", required_space, free_space);
}
-void DrivePinningScreenHandler::Show() {
- ShowInWebUI();
+void DrivePinningScreenHandler::Show(base::Value::Dict data) {
+ ShowInWebUI(std::move(data));
}
} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.h
index edb08a13a93..e23e4b5c430 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.h
@@ -27,7 +27,7 @@ class DrivePinningScreenView
std::u16string free_space) = 0;
// Shows the contents of the screen.
- virtual void Show() = 0;
+ virtual void Show(base::Value::Dict data) = 0;
};
class DrivePinningScreenHandler : public BaseScreenHandler,
@@ -49,7 +49,7 @@ class DrivePinningScreenHandler : public BaseScreenHandler,
void SetRequiredSpaceInfo(std::u16string required_space,
std::u16string free_space) override;
- void Show() override;
+ void Show(base::Value::Dict data) override;
};
} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc
index 90d1b3a8c2e..475ce8301d2 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc
@@ -61,8 +61,8 @@ constexpr char kOAUTHCodeCookie[] = "oauth_code";
std::string EnrollmentModeToUIMode(policy::EnrollmentConfig::Mode mode) {
switch (mode) {
case policy::EnrollmentConfig::MODE_NONE:
- case policy::EnrollmentConfig::OBSOLETE_MODE_ENROLLED_ROLLBACK:
- case policy::EnrollmentConfig::MODE_OFFLINE_DEMO_DEPRECATED:
+ case policy::EnrollmentConfig::DEPRECATED_MODE_ENROLLED_ROLLBACK:
+ case policy::EnrollmentConfig::DEPRECATED_MODE_OFFLINE_DEMO:
break;
case policy::EnrollmentConfig::MODE_MANUAL:
case policy::EnrollmentConfig::MODE_MANUAL_REENROLLMENT:
@@ -206,13 +206,13 @@ void EnrollmentScreenHandler::ShowUserError(const std::string& email) {
l10n_util::GetStringFUTF8(
IDS_ENTERPRISE_ENROLLMENT_CONSUMER_ACCOUNT_WITH_EDU_PACKAGED_LICENSE_ACCOUNT_CHECK,
base::ASCIIToUTF16(email)),
- true);
+ /*retry=*/true);
} else {
ShowErrorMessage(
l10n_util::GetStringFUTF8(
IDS_ENTERPRISE_ENROLLMENT_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE_ACCOUNT_CHECK,
base::ASCIIToUTF16(email)),
- true);
+ /*retry=*/true);
}
}
@@ -258,14 +258,15 @@ void EnrollmentScreenHandler::ShowAuthError(
case GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE:
case GoogleServiceAuthError::SERVICE_ERROR:
case GoogleServiceAuthError::SCOPE_LIMITED_UNRECOVERABLE_ERROR:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_FATAL_ERROR, false);
+ case GoogleServiceAuthError::CHALLENGE_RESPONSE_REQUIRED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_FATAL_ERROR, /*retry=*/false);
return;
case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_ACCOUNT_ERROR, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_ACCOUNT_ERROR, /*retry=*/true);
return;
case GoogleServiceAuthError::CONNECTION_FAILED:
case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_NETWORK_ERROR, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_NETWORK_ERROR, /*retry=*/true);
return;
case GoogleServiceAuthError::NUM_STATES:
break;
@@ -277,10 +278,12 @@ void EnrollmentScreenHandler::ShowOtherError(
EnrollmentLauncher::OtherError error) {
switch (error) {
case EnrollmentLauncher::OTHER_ERROR_DOMAIN_MISMATCH:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_USER, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_USER,
+ /*retry=*/true);
return;
case EnrollmentLauncher::OTHER_ERROR_FATAL:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_FATAL_ENROLLMENT_ERROR, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_FATAL_ENROLLMENT_ERROR,
+ /*retry=*/true);
return;
}
NOTREACHED();
@@ -297,43 +300,48 @@ void EnrollmentScreenHandler::ShowEnrollmentStatus(
ShowEnrollmentSuccessScreen();
return;
case policy::EnrollmentStatus::NO_STATE_KEYS:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_NO_STATE_KEYS, false);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_NO_STATE_KEYS,
+ /*retry=*/false);
return;
case policy::EnrollmentStatus::REGISTRATION_FAILED:
// Some special cases for generating a nicer message that's more helpful.
switch (status.client_status()) {
case policy::DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
if (policy::EnrollmentRequisitionManager::IsRemoraRequisition()) {
- ShowError(IDS_ENTERPRISE_ENROLLMENT_ACCOUNT_ERROR_MEETS, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ACCOUNT_ERROR_MEETS,
+ /*retry=*/true);
} else {
- ShowError(IDS_ENTERPRISE_ENROLLMENT_ACCOUNT_ERROR, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ACCOUNT_ERROR, /*retry=*/true);
}
break;
case policy::DM_STATUS_SERVICE_MISSING_LICENSES:
if (policy::EnrollmentRequisitionManager::IsRemoraRequisition()) {
ShowError(IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR_MEETS,
- true);
+ /*retry=*/true);
} else {
- ShowError(IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR,
+ /*retry=*/true);
}
break;
case policy::DM_STATUS_SERVICE_DEPROVISIONED:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_DEPROVISIONED_ERROR, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_DEPROVISIONED_ERROR,
+ /*retry=*/true);
break;
case policy::DM_STATUS_SERVICE_DOMAIN_MISMATCH:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_DOMAIN_MISMATCH_ERROR, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_DOMAIN_MISMATCH_ERROR,
+ /*retry=*/true);
break;
case policy::DM_STATUS_SERVICE_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE:
if (features::IsEducationEnrollmentOobeFlowEnabled() &&
config_.license_type == policy::LicenseType::kEducation) {
ShowError(
IDS_ENTERPRISE_ENROLLMENT_CONSUMER_ACCOUNT_WITH_EDU_PACKAGED_LICENSE,
- true);
+ /*retry=*/true);
break;
} else {
ShowError(
IDS_ENTERPRISE_ENROLLMENT_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE,
- true);
+ /*retry=*/true);
break;
}
@@ -341,23 +349,23 @@ void EnrollmentScreenHandler::ShowEnrollmentStatus(
DM_STATUS_SERVICE_ENTERPRISE_ACCOUNT_IS_NOT_ELIGIBLE_TO_ENROLL:
ShowError(
IDS_ENTERPRISE_ENROLLMENT_ENTERPRISE_ACCOUNT_IS_NOT_ELIGIBLE_TO_ENROLL,
- true);
+ /*retry=*/true);
break;
case policy::DM_STATUS_SERVICE_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED:
if (policy::EnrollmentRequisitionManager::IsRemoraRequisition()) {
ShowError(
IDS_ENTERPRISE_ENROLLMENT_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED_MEETS,
- true);
+ /*retry=*/true);
} else {
ShowError(
IDS_ENTERPRISE_ENROLLMENT_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED,
- true);
+ /*retry=*/true);
}
break;
case policy::DM_STATUS_SERVICE_ILLEGAL_ACCOUNT_FOR_PACKAGED_EDU_LICENSE:
ShowError(
IDS_ENTERPRISE_ENROLLMENT_ILLEGAL_ACCOUNT_FOR_PACKAGED_EDU_LICENSE,
- true);
+ /*retry=*/true);
break;
case policy::DM_STATUS_SERVICE_INVALID_PACKAGED_DEVICE_FOR_KIOSK:
ShowError(IDS_ENTERPRISE_ENROLLMENT_INVALID_PACKAGED_DEVICE_FOR_KIOSK,
@@ -368,38 +376,42 @@ void EnrollmentScreenHandler::ShowEnrollmentStatus(
l10n_util::GetStringFUTF8(
IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_FAILED,
policy::FormatDeviceManagementStatus(status.client_status())),
- true);
+ /*retry=*/true);
}
return;
case policy::EnrollmentStatus::ROBOT_AUTH_FETCH_FAILED:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_ROBOT_AUTH_FETCH_FAILED, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ROBOT_AUTH_FETCH_FAILED,
+ /*retry=*/true);
return;
case policy::EnrollmentStatus::ROBOT_REFRESH_FETCH_FAILED:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_ROBOT_REFRESH_FETCH_FAILED, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ROBOT_REFRESH_FETCH_FAILED,
+ /*retry=*/true);
return;
case policy::EnrollmentStatus::ROBOT_REFRESH_STORE_FAILED:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_ROBOT_REFRESH_STORE_FAILED, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ROBOT_REFRESH_STORE_FAILED,
+ /*retry=*/true);
return;
case policy::EnrollmentStatus::REGISTRATION_BAD_MODE:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_BAD_MODE, false);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_BAD_MODE,
+ /*retry=*/false);
return;
case policy::EnrollmentStatus::REGISTRATION_CERT_FETCH_FAILED:
ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_CERT_FETCH_FAILED,
- true);
+ /*retry=*/true);
return;
case policy::EnrollmentStatus::POLICY_FETCH_FAILED:
ShowErrorMessage(
l10n_util::GetStringFUTF8(
IDS_ENTERPRISE_ENROLLMENT_STATUS_POLICY_FETCH_FAILED,
policy::FormatDeviceManagementStatus(status.client_status())),
- true);
+ /*retry=*/true);
return;
case policy::EnrollmentStatus::VALIDATION_FAILED:
ShowErrorMessage(
l10n_util::GetStringFUTF8(
IDS_ENTERPRISE_ENROLLMENT_STATUS_VALIDATION_FAILED,
policy::FormatValidationStatus(status.validation_status())),
- true);
+ /*retry=*/true);
return;
case policy::EnrollmentStatus::LOCK_ERROR:
switch (status.lock_status()) {
@@ -411,20 +423,24 @@ void EnrollmentScreenHandler::ShowEnrollmentStatus(
LOG(FATAL) << "Invalid lock status.";
return;
case InstallAttributes::LOCK_TIMEOUT:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_TIMEOUT, false);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_TIMEOUT,
+ /*retry=*/false);
return;
case InstallAttributes::LOCK_BACKEND_INVALID:
case InstallAttributes::LOCK_ALREADY_LOCKED:
case InstallAttributes::LOCK_SET_ERROR:
case InstallAttributes::LOCK_FINALIZE_ERROR:
case InstallAttributes::LOCK_READBACK_ERROR:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_ERROR, false);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_ERROR,
+ /*retry=*/false);
return;
case InstallAttributes::LOCK_WRONG_DOMAIN:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_USER, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_USER,
+ /*retry=*/true);
return;
case InstallAttributes::LOCK_WRONG_MODE:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_MODE, true);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_MODE,
+ /*retry=*/true);
return;
}
NOTREACHED();
@@ -435,25 +451,27 @@ void EnrollmentScreenHandler::ShowEnrollmentStatus(
IDS_ENTERPRISE_ENROLLMENT_STATUS_STORE_ERROR,
policy::FormatStoreStatus(status.store_status(),
status.validation_status())),
- true);
+ /*retry=*/true);
return;
case policy::EnrollmentStatus::ATTRIBUTE_UPDATE_FAILED:
- ShowErrorForDevice(IDS_ENTERPRISE_ENROLLMENT_ATTRIBUTE_ERROR, false);
+ ShowErrorForDevice(IDS_ENTERPRISE_ENROLLMENT_ATTRIBUTE_ERROR,
+ /*retry=*/false);
return;
case policy::EnrollmentStatus::NO_MACHINE_IDENTIFICATION:
ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_NO_MACHINE_IDENTIFICATION,
- false);
+ /*retry=*/false);
return;
case policy::EnrollmentStatus::ACTIVE_DIRECTORY_POLICY_FETCH_FAILED:
ShowError(IDS_ENTERPRISE_ENROLLMENT_ERROR_ACTIVE_DIRECTORY_POLICY_FETCH,
- false);
+ /*retry=*/false);
return;
case policy::EnrollmentStatus::DM_TOKEN_STORE_FAILED:
ShowError(IDS_ENTERPRISE_ENROLLMENT_ERROR_SAVE_DEVICE_CONFIGURATION,
- false);
+ /*retry=*/false);
return;
case policy::EnrollmentStatus::MAY_NOT_BLOCK_DEV_MODE:
- ShowError(IDS_ENTERPRISE_ENROLLMENT_ERROR_MAY_NOT_BLOCK_DEV_MODE, false);
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ERROR_MAY_NOT_BLOCK_DEV_MODE,
+ /*retry=*/false);
return;
}
NOTREACHED();
@@ -852,10 +870,13 @@ base::Value::Dict EnrollmentScreenHandler::ScreenDataForOAuthEnrollment() {
screen_data.Set("management_domain", config_.management_domain);
screen_data.Set("gaia_buttons_type",
GetGaiaButtonsTypeString(gaia_buttons_type_));
- const std::string app_locale = g_browser_process->GetApplicationLocale();
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
if (!app_locale.empty())
screen_data.Set("hl", app_locale);
-
+ const std::string& email = config_.enrollment_nudge_email;
+ if (!email.empty()) {
+ screen_data.Set("email", email);
+ }
return screen_data;
}
diff --git a/chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h
index eb405ad7839..e3a8c0d725f 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h
@@ -139,7 +139,8 @@ class EnrollmentScreenHandler : public BaseScreenHandler,
bool IsOnEnrollmentScreen();
// Keeps the controller for this view.
- raw_ptr<Controller, ExperimentalAsh> controller_ = nullptr;
+ raw_ptr<Controller, DanglingUntriaged | ExperimentalAsh> controller_ =
+ nullptr;
bool show_on_init_ = false;
diff --git a/chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.cc
index cc7064abc3f..aaaac4936a2 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.cc
@@ -17,12 +17,8 @@ ErrorScreenHandler::ErrorScreenHandler() : BaseScreenHandler(kScreenId) {}
ErrorScreenHandler::~ErrorScreenHandler() = default;
-void ErrorScreenHandler::Show() {
- base::Value::Dict data;
- if (LoginDisplayHost::default_host()) {
- data.Set("hasUserPods", LoginDisplayHost::default_host()->HasUserPods());
- }
- ShowInWebUI(std::move(data));
+void ErrorScreenHandler::ShowScreenWithParam(bool is_closeable) {
+ ShowInWebUI(base::Value::Dict().Set("isCloseable", is_closeable));
}
void ErrorScreenHandler::ShowOobeScreen(OobeScreenId screen) {
@@ -51,10 +47,6 @@ void ErrorScreenHandler::SetShowConnectingIndicator(bool value) {
CallExternalAPI("showConnectingIndicator", value);
}
-void ErrorScreenHandler::SetIsPersistentError(bool is_persistent) {
- CallExternalAPI("setIsPersistentError", is_persistent);
-}
-
void ErrorScreenHandler::SetUIState(NetworkError::UIState ui_state) {
CallExternalAPI("setUIState", static_cast<int>(ui_state));
}
diff --git a/chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.h
index 9e28bc4fc9c..da90629fb7c 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/error_screen_handler.h
@@ -21,7 +21,7 @@ class ErrorScreenView : public base::SupportsWeakPtr<ErrorScreenView> {
virtual ~ErrorScreenView() = default;
// Shows the contents of the screen.
- virtual void Show() = 0;
+ virtual void ShowScreenWithParam(bool is_closeable) = 0;
// Switches to `screen`.
virtual void ShowOobeScreen(OobeScreenId screen) = 0;
@@ -41,9 +41,6 @@ class ErrorScreenView : public base::SupportsWeakPtr<ErrorScreenView> {
// Updates visibility of the label indicating we're reconnecting.
virtual void SetShowConnectingIndicator(bool value) = 0;
- // Makes error persistent (e.g. non-closable).
- virtual void SetIsPersistentError(bool is_persistent) = 0;
-
// Sets current UI state of the screen.
virtual void SetUIState(NetworkError::UIState ui_state) = 0;
};
@@ -62,14 +59,13 @@ class ErrorScreenHandler : public BaseScreenHandler, public ErrorScreenView {
private:
// ErrorScreenView:
- void Show() override;
+ void ShowScreenWithParam(bool is_closeable) override;
void ShowOobeScreen(OobeScreenId screen) override;
void SetErrorStateCode(NetworkError::ErrorState error_state) override;
void SetErrorStateNetwork(const std::string& network_name) override;
void SetGuestSigninAllowed(bool value) override;
void SetOfflineSigninAllowed(bool value) override;
void SetShowConnectingIndicator(bool value) override;
- void SetIsPersistentError(bool is_persistent) override;
void SetUIState(NetworkError::UIState ui_state) override;
// BaseScreenHandler:
diff --git a/chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.cc
index bb62ff0291e..9830c0d5376 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.cc
@@ -20,36 +20,17 @@ void FakeAppLaunchSplashScreenHandler::ShowErrorMessage(
error_message_type_ = error;
}
-bool FakeAppLaunchSplashScreenHandler::IsNetworkReady() {
- return network_ready_;
-}
-
-bool FakeAppLaunchSplashScreenHandler::IsNetworkRequired() const {
- return network_required_;
-}
-
KioskAppLaunchError::Error
FakeAppLaunchSplashScreenHandler::GetErrorMessageType() const {
return error_message_type_;
}
-void FakeAppLaunchSplashScreenHandler::SetNetworkReady(bool ready) {
- network_ready_ = ready;
- if (delegate_) {
- delegate_->OnNetworkStateChanged(true);
- }
-}
-
void FakeAppLaunchSplashScreenHandler::FinishNetworkConfig() {
if (delegate_) {
delegate_->OnNetworkConfigFinished();
}
}
-void FakeAppLaunchSplashScreenHandler::SetNetworkRequired() {
- network_required_ = true;
-}
-
void FakeAppLaunchSplashScreenHandler::UpdateAppLaunchState(
AppLaunchState state) {
state_ = state;
diff --git a/chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.h
index 170a1a36374..8c742a8cf68 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/fake_app_launch_splash_screen_handler.h
@@ -18,14 +18,12 @@ class FakeAppLaunchSplashScreenHandler : public AppLaunchSplashScreenView {
void Hide() override {}
void UpdateAppLaunchState(AppLaunchState state) override;
void ToggleNetworkConfig(bool) override {}
- void ShowNetworkConfigureUI() override {}
+ void ShowNetworkConfigureUI(NetworkStateInformer::State state,
+ const std::string& network_name) override {}
void ShowErrorMessage(KioskAppLaunchError::Error error) override;
- bool IsNetworkReady() override;
void ContinueAppLaunch() override {}
- void SetNetworkRequired() override;
KioskAppLaunchError::Error GetErrorMessageType() const;
- void SetNetworkReady(bool ready);
void FinishNetworkConfig();
AppLaunchState GetAppLaunchState() const;
bool IsNetworkRequired() const;
@@ -36,8 +34,6 @@ class FakeAppLaunchSplashScreenHandler : public AppLaunchSplashScreenView {
KioskAppLaunchError::Error error_message_type_ =
KioskAppLaunchError::Error::kNone;
KioskAppManagerBase::App last_app_data_;
- bool network_ready_ = false;
- bool network_required_ = false;
AppLaunchState state_ = AppLaunchState::kPreparingProfile;
};
diff --git a/chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
index 7ba5098a117..3ba0ae7a9de 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
@@ -63,7 +63,6 @@
#include "chrome/browser/certificate_provider/certificate_provider_service_factory.h"
#include "chrome/browser/certificate_provider/pin_dialog_manager.h"
#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/enterprise/util/managed_browser_utils.h"
#include "chrome/browser/lifetime/browser_shutdown.h"
#include "chrome/browser/net/nss_temp_certs_cache_chromeos.h"
#include "chrome/browser/net/system_network_context_manager.h"
@@ -184,33 +183,6 @@ void RecordAPILogin(bool is_third_party_idp, bool is_api_used) {
// Timeout used to prevent infinite connecting to a flaky network.
constexpr base::TimeDelta kConnectingTimeout = base::Seconds(60);
-GaiaScreenHandler::GaiaScreenMode GetGaiaScreenMode(const std::string& email) {
- int authentication_behavior = 0;
- CrosSettings::Get()->GetInteger(kLoginAuthenticationBehavior,
- &authentication_behavior);
- if (authentication_behavior ==
- em::LoginAuthenticationBehaviorProto::SAML_INTERSTITIAL) {
- if (email.empty())
- return GaiaScreenHandler::GAIA_SCREEN_MODE_SAML_REDIRECT;
-
- user_manager::KnownUser known_user(g_browser_process->local_state());
- // If there's a populated email, we must check first that this user is using
- // SAML in order to decide whether to show the interstitial page.
- const user_manager::User* user =
- user_manager::UserManager::Get()->FindUser(known_user.GetAccountId(
- email, std::string() /* id */, AccountType::UNKNOWN));
-
- // TODO(b/259675128): we shouldn't rely on `user->using_saml()` when
- // deciding which IdP page to show because this flag can be outdated. Admin
- // could have changed the IdP to GAIA since last authentication and we
- // wouldn't know about it.
- if (user && user->using_saml())
- return GaiaScreenHandler::GAIA_SCREEN_MODE_SAML_REDIRECT;
- }
-
- return GaiaScreenHandler::GAIA_SCREEN_MODE_DEFAULT;
-}
-
std::string GetEnterpriseDomainManager() {
policy::BrowserPolicyConnectorAsh* connector =
g_browser_process->platform_part()->browser_policy_connector_ash();
@@ -278,9 +250,7 @@ void GetVersionAndConsent(std::string* out_version, bool* out_consent) {
}
user_manager::UserType CalculateUserType(const AccountId& account_id) {
- if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY) {
- return user_manager::USER_TYPE_ACTIVE_DIRECTORY;
- }
+ CHECK(account_id.GetAccountType() != AccountType::ACTIVE_DIRECTORY);
return user_manager::USER_TYPE_REGULAR;
}
@@ -556,6 +526,12 @@ void GaiaScreenHandler::LoadGaiaWithPartitionAndVersionAndConsent(
params.Set("rart", gaia_reauth_request_token_);
}
+ if (features::IsPasswordlessGaiaEnabledForConsumers() &&
+ !is_gaia_password_required_) {
+ params.Set("pwl",
+ static_cast<int>(PasswordlessSupportLevel::kConsumersOnly));
+ }
+
PrefService* local_state = g_browser_process->local_state();
if (local_state->IsManagedPreference(
prefs::kUrlParameterToAutofillSAMLUsername)) {
@@ -638,6 +614,9 @@ void GaiaScreenHandler::DeclareLocalizedValues(
IDS_ENROLLMENT_NUDGE_ENROLL_BUTTON);
builder->Add("enrollmentNudgeUseAnotherAccountButton",
IDS_ENROLLMENT_NUDGE_USE_ANOTHER_ACCOUNT_BUTTON);
+
+ builder->Add("signinScreenQuickStart",
+ IDS_LOGIN_QUICK_START_SETUP_SIGNIN_SCREEN_ENTRY_POINT);
}
void GaiaScreenHandler::InitAfterJavascriptAllowed() {
@@ -662,7 +641,6 @@ void GaiaScreenHandler::DeclareJSCallbacks() {
AddCallback("samlChallengeMachineKey",
&GaiaScreenHandler::HandleSamlChallengeMachineKey);
AddCallback("loginWebuiReady", &GaiaScreenHandler::HandleGaiaUIReady);
- AddCallback("identifierEntered", &GaiaScreenHandler::HandleIdentifierEntered);
AddCallback("authExtensionLoaded",
&GaiaScreenHandler::HandleAuthExtensionLoaded);
AddCallback("setIsFirstSigninStep",
@@ -677,14 +655,6 @@ void GaiaScreenHandler::DeclareJSCallbacks() {
&GaiaScreenHandler::HandleShowLoadingTimeoutError);
}
-void GaiaScreenHandler::HandleIdentifierEntered(const std::string& user_email) {
- if (MaybeTriggerEnrollmentNudge(user_email)) {
- return;
- }
-
- CheckIfAllowlisted(user_email);
-}
-
void GaiaScreenHandler::HandleAuthExtensionLoaded() {
VLOG(1) << "Auth extension finished loading";
// Recreate the client cert usage observer, in order to track only the certs
@@ -694,6 +664,15 @@ void GaiaScreenHandler::HandleAuthExtensionLoaded() {
}
void GaiaScreenHandler::HandleWebviewLoadAborted(int error_code) {
+ if (error_code == net::ERR_BLOCKED_BY_ADMINISTRATOR) {
+ // Navigating to a blocked site displays a network error screen, but it
+ // doesn't indicate that the network is malfunctioning or that we need to
+ // reload the screen after regaining network connectivity, and it doesn't
+ // alter the network state, so we handle this network error with a frame
+ // state of its own.
+ frame_state_ = FRAME_STATE_BLOCKED;
+ return;
+ }
if (error_code == net::ERR_INVALID_AUTH_CREDENTIALS) {
// Silently ignore this error - it is used as an intermediate state for
// committed interstitials (see https://crbug.com/1049349 for details).
@@ -1357,6 +1336,10 @@ void GaiaScreenHandler::SetReauthRequestToken(
gaia_reauth_request_token_ = reauth_request_token;
}
+void GaiaScreenHandler::ShowEnrollmentNudge(const std::string& email_domain) {
+ CallExternalAPI("showEnrollmentNudge", email_domain);
+}
+
void GaiaScreenHandler::LoadAuthExtension(bool force) {
VLOG(1) << "LoadAuthExtension, force: " << force;
if (!initialized_) {
@@ -1441,16 +1424,14 @@ void GaiaScreenHandler::UpdateStateInternal(NetworkError::ErrorReason reason,
}
connecting_callback_.Cancel();
- const bool is_online = NetworkStateInformer::IsOnline(state, reason);
- const bool is_behind_captive_portal =
- NetworkStateInformer::IsBehindCaptivePortal(state, reason);
const bool is_gaia_loading_timeout =
(reason == NetworkError::ERROR_REASON_LOADING_TIMEOUT);
const bool is_gaia_error =
frame_error() != net::OK && frame_error() != net::ERR_NETWORK_CHANGED;
const bool error_screen_should_overlay = IsGaiaVisible();
const bool from_not_online_to_online_transition =
- is_online && last_network_state_ != NetworkStateInformer::ONLINE;
+ state == NetworkStateInformer::ONLINE &&
+ last_network_state_ != NetworkStateInformer::ONLINE;
last_network_state_ = state;
proxy_auth_dialog_need_reload_ =
(reason == NetworkError::ERROR_REASON_NETWORK_STATE_CHANGED) &&
@@ -1465,7 +1446,7 @@ void GaiaScreenHandler::UpdateStateInternal(NetworkError::ErrorReason reason,
return;
}
- if (is_online || !is_behind_captive_portal) {
+ if (state != NetworkStateInformer::CAPTIVE_PORTAL) {
error_screen_->HideCaptivePortal();
}
@@ -1506,7 +1487,8 @@ void GaiaScreenHandler::UpdateStateInternal(NetworkError::ErrorReason reason,
reload_gaia = true;
}
- if (!is_online || is_gaia_loading_timeout || is_gaia_error) {
+ if (state != NetworkStateInformer::ONLINE || is_gaia_loading_timeout ||
+ is_gaia_error) {
if (GetCurrentScreen() != ErrorScreenView::kScreenId) {
error_screen_->SetParentScreen(GaiaView::kScreenId);
error_screen_->SetHideCallback(base::BindOnce(
@@ -1524,6 +1506,14 @@ void GaiaScreenHandler::UpdateStateInternal(NetworkError::ErrorReason reason,
}
}
+bool GaiaScreenHandler::IsLoadedForTesting() const {
+ return frame_state_ == FRAME_STATE_LOADED;
+}
+
+bool GaiaScreenHandler::IsNavigationBlockedForTesting() const {
+ return frame_state_ == FRAME_STATE_BLOCKED;
+}
+
void GaiaScreenHandler::HideOfflineMessage(NetworkStateInformer::State state,
NetworkError::ErrorReason reason) {
if (!IsGaiaHiddenByError()) {
@@ -1607,42 +1597,6 @@ void GaiaScreenHandler::SAMLConfirmPassword(
std::move(scraped_saml_passwords), std::move(user_context));
}
-bool GaiaScreenHandler::MaybeTriggerEnrollmentNudge(
- const std::string& user_email) {
- const bool is_enterprise_managed = g_browser_process->platform_part()
- ->browser_policy_connector_ash()
- ->IsDeviceEnterpriseManaged();
- if (is_enterprise_managed) {
- // Device either already went through enterprise enrollment flow or goes
- // through it right now. No need for nudging.
- return false;
- }
- const bool is_first_user =
- user_manager::UserManager::Get()->GetUsers().empty();
- if (!is_first_user) {
- // Enrollment nudge targets only initial OOBE flow on unowned devices.
- // Current user is not a first user which means that device is already
- // owned.
- return false;
- }
- const std::string email_domain =
- chrome::enterprise_util::GetDomainFromEmail(user_email);
- if (chrome::enterprise_util::IsKnownConsumerDomain(email_domain)) {
- // User doesn't belong to a managed domain, so enrollment nudging can't
- // apply.
- return false;
- }
-
- // TODO(b/271104781): replace this check with a policy fetch through a special
- // DM server API when it is available.
- if (!ash::features::IsEnrollmentNudgingForTestingEnabled()) {
- return false;
- }
-
- CallExternalAPI("showEnrollmentNudge", email_domain);
- return true;
-}
-
void GaiaScreenHandler::CheckIfAllowlisted(const std::string& user_email) {
// We cannot tell a user type from the identifier, so we delay checking if
// the account should be allowed.
@@ -1660,4 +1614,47 @@ void GaiaScreenHandler::CheckIfAllowlisted(const std::string& user_email) {
}
}
+void GaiaScreenHandler::ToggleLoadingUI(bool is_shown) {
+ CallExternalAPI("toggleLoadingUI", is_shown);
+}
+
+void GaiaScreenHandler::SetQuickStartEnabled() {
+ CallExternalAPI("setQuickStartEnabled");
+}
+
+void GaiaScreenHandler::SetIsGaiaPasswordRequired(bool is_required) {
+ is_gaia_password_required_ = is_required;
+}
+
+// static
+GaiaScreenHandler::GaiaScreenMode GaiaScreenHandler::GetGaiaScreenMode(
+ const std::string& email) {
+ int authentication_behavior = 0;
+ CrosSettings::Get()->GetInteger(kLoginAuthenticationBehavior,
+ &authentication_behavior);
+ if (authentication_behavior ==
+ em::LoginAuthenticationBehaviorProto::SAML_INTERSTITIAL) {
+ if (email.empty()) {
+ return GaiaScreenHandler::GAIA_SCREEN_MODE_SAML_REDIRECT;
+ }
+
+ user_manager::KnownUser known_user(g_browser_process->local_state());
+ // If there's a populated email, we must check first that this user is using
+ // SAML in order to decide whether to show the interstitial page.
+ const user_manager::User* user =
+ user_manager::UserManager::Get()->FindUser(known_user.GetAccountId(
+ email, std::string() /* id */, AccountType::UNKNOWN));
+
+ // TODO(b/259675128): we shouldn't rely on `user->using_saml()` when
+ // deciding which IdP page to show because this flag can be outdated. Admin
+ // could have changed the IdP to GAIA since last authentication and we
+ // wouldn't know about it.
+ if (user && user->using_saml()) {
+ return GaiaScreenHandler::GAIA_SCREEN_MODE_SAML_REDIRECT;
+ }
+ }
+
+ return GaiaScreenHandler::GAIA_SCREEN_MODE_DEFAULT;
+}
+
} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.h
index c17f578aaf8..d94297d55cd 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/gaia_screen_handler.h
@@ -62,6 +62,16 @@ class GaiaView : public base::SupportsWeakPtr<GaiaView> {
kMaxValue = kOnlineSignin
};
+ enum class PasswordlessSupportLevel {
+ // Passwordless logins are not supported or password logins are enforced.
+ kNone = 0,
+ // Passwordless logins are supported for consumers only, but not for
+ // enterprise users.
+ kConsumersOnly,
+ // Passwordless logins are supported for all users.
+ kAll,
+ };
+
inline constexpr static StaticOobeScreenId kScreenId{"gaia-signin",
"GaiaSigninScreen"};
@@ -85,7 +95,7 @@ class GaiaView : public base::SupportsWeakPtr<GaiaView> {
virtual void SetGaiaPath(GaiaPath gaia_path) = 0;
// Returns the currently set Gaia path
virtual GaiaPath GetGaiaPath() = 0;
- // Show error UI at the end of GAIA flow when user is not allowlisted.
+ // Show error UI at the end of Gaia flow when user is not allowlisted.
virtual void ShowAllowlistCheckFailedError() = 0;
// Reloads authenticator.
virtual void ReloadGaiaAuthenticator() = 0;
@@ -93,6 +103,12 @@ class GaiaView : public base::SupportsWeakPtr<GaiaView> {
// for recovery.
virtual void SetReauthRequestToken(
const std::string& reauth_request_token) = 0;
+ // Shows pop-up saying that enrollment is required for user's managed domain.
+ virtual void ShowEnrollmentNudge(const std::string& email_domain) = 0;
+ // Checks if user's email is allowlisted.
+ virtual void CheckIfAllowlisted(const std::string& user_email) = 0;
+ // Shows a page with loading animation on top of the Gaia screen.
+ virtual void ToggleLoadingUI(bool is_shown) = 0;
// Show sign-in screen for the given credentials. `services` is a list of
// services returned by userInfo call as JSON array. Should be an empty array
@@ -100,6 +116,11 @@ class GaiaView : public base::SupportsWeakPtr<GaiaView> {
virtual void ShowSigninScreenForTest(const std::string& username,
const std::string& password,
const std::string& services) = 0;
+ virtual void SetQuickStartEnabled() = 0;
+ // Sets if Gaia password is required during login. If the password is
+ // required, Gaia passwordless login will be disallowed.
+ virtual void SetIsGaiaPasswordRequired(bool is_required) = 0;
+
// Reset authenticator.
virtual void Reset() = 0;
};
@@ -127,7 +148,8 @@ class GaiaScreenHandler
FRAME_STATE_UNKNOWN = 0,
FRAME_STATE_LOADING,
FRAME_STATE_LOADED,
- FRAME_STATE_ERROR
+ FRAME_STATE_ERROR,
+ FRAME_STATE_BLOCKED
};
GaiaScreenHandler(
@@ -148,10 +170,17 @@ class GaiaScreenHandler
void ShowAllowlistCheckFailedError() override;
void ReloadGaiaAuthenticator() override;
void SetReauthRequestToken(const std::string& reauth_request_token) override;
+ void ShowEnrollmentNudge(const std::string& email_domain) override;
+ void CheckIfAllowlisted(const std::string& user_email) override;
+ void ToggleLoadingUI(bool is_shown) override;
void ShowSigninScreenForTest(const std::string& username,
const std::string& password,
const std::string& services) override;
+
+ void SetQuickStartEnabled() override;
+ void SetIsGaiaPasswordRequired(bool is_required) override;
+
void Reset() override;
// SecurityTokenPinDialogHost:
@@ -169,6 +198,10 @@ class GaiaScreenHandler
// NetworkStateInformer::NetworkStateInformerObserver:
void UpdateState(NetworkError::ErrorReason reason) override;
+ // Returns the initial mode of the Gaia signin screen for a given user email
+ // address. Note this also affects which Gaia endpoint is used.
+ static GaiaScreenMode GetGaiaScreenMode(const std::string& email);
+
void SetNextSamlChallengeKeyHandlerForTesting(
std::unique_ptr<SamlChallengeKeyHandler> handler_for_test);
@@ -182,6 +215,12 @@ class GaiaScreenHandler
offline_timeout_ = offline_timeout;
}
+ // TODO(https://issuetracker.google.com/292489063): Remove these methods to
+ // query the frame state, and instead, allow registering callbacks or futures
+ // to learn of the relevant state transitions e.g. with an Observer class.
+ bool IsLoadedForTesting() const;
+ bool IsNavigationBlockedForTesting() const;
+
private:
void LoadGaia(const login::GaiaContext& context);
@@ -244,8 +283,6 @@ class GaiaScreenHandler
void HandleGaiaUIReady();
- void HandleIdentifierEntered(const std::string& account_identifier);
-
void HandleAuthExtensionLoaded();
// Allows WebUI to control the login shelf's guest and apps buttons visibility
@@ -347,9 +384,6 @@ class GaiaScreenHandler
void SAMLConfirmPassword(::login::StringList scraped_saml_passwords,
std::unique_ptr<UserContext> user_context);
- bool MaybeTriggerEnrollmentNudge(const std::string& user_email);
- void CheckIfAllowlisted(const std::string& user_email);
-
// Current state of Gaia frame.
FrameState frame_state_ = FRAME_STATE_UNKNOWN;
@@ -481,6 +515,8 @@ class GaiaScreenHandler
std::unique_ptr<ErrorScreensHistogramHelper> histogram_helper_;
+ bool is_gaia_password_required_ = false;
+
base::WeakPtrFactory<GaiaScreenHandler> weak_factory_{this};
};
diff --git a/chromium/chrome/browser/ui/webui/ash/login/l10n_util_test_util.cc b/chromium/chrome/browser/ui/webui/ash/login/l10n_util_test_util.cc
index b3abd768ab9..6d13defebfa 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/l10n_util_test_util.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/l10n_util_test_util.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "chrome/browser/ui/webui/ash/login/l10n_util_test_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include <vector>
@@ -25,8 +26,8 @@ void MockInputMethodManagerWithInputMethods::AddInputMethod(
std::vector<std::string> languages;
languages.push_back(language_code);
descriptors_.push_back(input_method::InputMethodDescriptor(
- id, std::string(), std::string(), layout, languages, true, GURL(),
- GURL()));
+ id, std::string(), std::string(), layout, languages, true, GURL(), GURL(),
+ /*handwriting_language=*/absl::nullopt));
}
} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/login/local_password_setup_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/local_password_setup_handler.cc
new file mode 100644
index 00000000000..8560b2ff3cc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/login/local_password_setup_handler.cc
@@ -0,0 +1,34 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/login/local_password_setup_handler.h"
+
+#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "ui/chromeos/devicetype_utils.h"
+
+namespace ash {
+
+LocalPasswordSetupHandler::LocalPasswordSetupHandler()
+ : BaseScreenHandler(kScreenId) {}
+
+LocalPasswordSetupHandler::~LocalPasswordSetupHandler() = default;
+
+void LocalPasswordSetupHandler::Show() {
+ ShowInWebUI();
+}
+
+void LocalPasswordSetupHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->AddF("localPasswordSetupTitle", IDS_LOGIN_LOCAL_PASSWORD_SETUP_TITLE,
+ ui::GetChromeOSDeviceName());
+ builder->Add("passwordInputPlaceholderText",
+ IDS_LOGIN_MANUAL_PASSWORD_INPUT_LABEL);
+ builder->Add("confirmPasswordInputPlaceholderText",
+ IDS_LOGIN_CONFIRM_PASSWORD_LABEL);
+ builder->Add("passwordMismatchError", IDS_LOGIN_MANUAL_PASSWORD_MISMATCH);
+}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/login/local_password_setup_handler.h b/chromium/chrome/browser/ui/webui/ash/login/local_password_setup_handler.h
new file mode 100644
index 00000000000..a31fb18bb93
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/login/local_password_setup_handler.h
@@ -0,0 +1,51 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_LOCAL_PASSWORD_SETUP_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_LOCAL_PASSWORD_SETUP_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ash/login/oobe_screen.h"
+#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
+
+namespace ash {
+
+class LocalPasswordSetupView
+ : public base::SupportsWeakPtr<LocalPasswordSetupView> {
+ public:
+ inline constexpr static StaticOobeScreenId kScreenId{
+ "local-password-setup", "LocalPasswordSetupScreen"};
+
+ LocalPasswordSetupView() = default;
+
+ LocalPasswordSetupView(const LocalPasswordSetupView&) = delete;
+ LocalPasswordSetupView& operator=(const LocalPasswordSetupView&) = delete;
+
+ virtual void Show() = 0;
+};
+
+// A class that handles WebUI hooks in Gaia screen.
+class LocalPasswordSetupHandler : public BaseScreenHandler,
+ public LocalPasswordSetupView {
+ public:
+ using TView = LocalPasswordSetupView;
+
+ LocalPasswordSetupHandler();
+
+ LocalPasswordSetupHandler(const LocalPasswordSetupHandler&) = delete;
+ LocalPasswordSetupHandler& operator=(const LocalPasswordSetupHandler&) =
+ delete;
+
+ ~LocalPasswordSetupHandler() override;
+
+ // LocalPasswordSetupView:
+ void Show() override;
+
+ // BaseScreenHandler:
+ void DeclareLocalizedValues(::login::LocalizedValuesBuilder* builder) final;
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_LOCAL_PASSWORD_SETUP_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc
index 40f7c932c7f..5f5234ec4ec 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc
@@ -6,6 +6,7 @@
#include <string>
+#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "chrome/browser/ash/login/screens/locale_switch_screen.h"
#include "chrome/browser/ash/login/ui/login_display_host.h"
@@ -20,6 +21,7 @@ LocaleSwitchScreenHandler::LocaleSwitchScreenHandler()
LocaleSwitchScreenHandler::~LocaleSwitchScreenHandler() = default;
void LocaleSwitchScreenHandler::UpdateStrings() {
+ TRACE_EVENT0("login", "LocaleSwitchScreenHandler::UpdateStrings");
GetOobeUI()->GetCoreOobe()->ReloadContent();
}
diff --git a/chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.cc
index d76989e62c7..b6cbb69dfef 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.cc
@@ -60,6 +60,10 @@ void NetworkScreenHandler::DeclareLocalizedValues(
builder->Add("proxySettingsListItemName",
IDS_NETWORK_PROXY_SETTINGS_LIST_ITEM_NAME);
builder->Add("addWiFiListItemName", IDS_NETWORK_ADD_WI_FI_LIST_ITEM_NAME);
+
+ builder->Add("networkScreenQuickStart",
+ IDS_LOGIN_QUICK_START_SETUP_NETWORK_SCREEN_ENTRY_POINT);
+
ui::network_element::AddLocalizedValuesToBuilder(builder);
cellular_setup::AddLocalizedValuesToBuilder(builder);
}
@@ -68,4 +72,8 @@ void NetworkScreenHandler::GetAdditionalParameters(base::Value::Dict* dict) {
cellular_setup::AddNonStringLoadTimeDataToDict(dict);
}
+void NetworkScreenHandler::SetQuickStartEnabled() {
+ CallExternalAPI("setQuickStartEnabled");
+}
+
} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.h
index 4566c5cadc2..c6d620f3bce 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/network_screen_handler.h
@@ -29,6 +29,8 @@ class NetworkScreenView : public base::SupportsWeakPtr<NetworkScreenView> {
// Hides error messages showing no error state.
virtual void ClearErrors() = 0;
+
+ virtual void SetQuickStartEnabled() = 0;
};
// WebUI implementation of NetworkScreenView. It is used to interact with
@@ -45,6 +47,8 @@ class NetworkScreenHandler : public NetworkScreenView,
~NetworkScreenHandler() override;
+ void SetQuickStartEnabled() override;
+
private:
// NetworkScreenView:
void Show() override;
diff --git a/chromium/chrome/browser/ui/webui/ash/login/network_state_informer.cc b/chromium/chrome/browser/ui/webui/ash/login/network_state_informer.cc
index bcb452dbcb5..e50c9d22fe4 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/network_state_informer.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/network_state_informer.cc
@@ -124,33 +124,6 @@ std::string NetworkStateInformer::GetNetworkName(
}
// static
-bool NetworkStateInformer::IsOnline(State state,
- NetworkError::ErrorReason reason) {
- switch (reason) {
- case NetworkError::ERROR_REASON_PORTAL_DETECTED:
- case NetworkError::ERROR_REASON_LOADING_TIMEOUT:
- return false;
- case NetworkError::ERROR_REASON_PROXY_AUTH_CANCELLED:
- case NetworkError::ERROR_REASON_PROXY_AUTH_SUPPLIED:
- case NetworkError::ERROR_REASON_PROXY_CONNECTION_FAILED:
- case NetworkError::ERROR_REASON_PROXY_CONFIG_CHANGED:
- case NetworkError::ERROR_REASON_NETWORK_STATE_CHANGED:
- case NetworkError::ERROR_REASON_UPDATE:
- case NetworkError::ERROR_REASON_FRAME_ERROR:
- case NetworkError::ERROR_REASON_NONE:
- return state == NetworkStateInformer::ONLINE;
- }
-}
-
-// static
-bool NetworkStateInformer::IsBehindCaptivePortal(
- State state,
- NetworkError::ErrorReason reason) {
- return state == NetworkStateInformer::CAPTIVE_PORTAL ||
- reason == NetworkError::ERROR_REASON_PORTAL_DETECTED;
-}
-
-// static
bool NetworkStateInformer::IsProxyError(State state,
NetworkError::ErrorReason reason) {
return state == NetworkStateInformer::PROXY_AUTH_REQUIRED ||
diff --git a/chromium/chrome/browser/ui/webui/ash/login/network_state_informer.h b/chromium/chrome/browser/ui/webui/ash/login/network_state_informer.h
index 20235e8dcc5..9914a293845 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/network_state_informer.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/network_state_informer.h
@@ -68,9 +68,6 @@ class NetworkStateInformer : public NetworkStateHandlerObserver,
std::string network_path() const { return network_path_; }
static std::string GetNetworkName(const std::string& service_path);
- static bool IsOnline(State state, NetworkError::ErrorReason reason);
- static bool IsBehindCaptivePortal(State state,
- NetworkError::ErrorReason reason);
static bool IsProxyError(State state, NetworkError::ErrorReason reason);
private:
diff --git a/chromium/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chromium/chrome/browser/ui/webui/ash/login/oobe_ui.cc
index cce92e4506a..a41e077e9a7 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/oobe_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -25,6 +25,8 @@
#include "base/system/sys_info.h"
#include "base/values.h"
#include "build/branding_buildflags.h"
+#include "chrome/browser/ash/boot_times_recorder_tab_helper.h"
+#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/login/enrollment/auto_enrollment_check_screen_view.h"
#include "chrome/browser/ash/login/enrollment/enrollment_screen_view.h"
#include "chrome/browser/ash/login/quick_unlock/pin_backend.h"
@@ -42,6 +44,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/webui/about_ui.h"
+#include "chrome/browser/ui/webui/ash/login/add_child_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/app_downloading_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/arc_vm_data_migration_screen_handler.h"
@@ -50,6 +53,7 @@
#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/choobe_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
#include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_setup_screen_handler.h"
@@ -77,6 +81,7 @@
#include "chrome/browser/ui/webui/ash/login/kiosk_enable_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/lacros_data_backward_migration_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/lacros_data_migration_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/local_password_setup_handler.h"
#include "chrome/browser/ui/webui/ash/login/local_state_error_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/management_transition_screen_handler.h"
@@ -91,6 +96,7 @@
#include "chrome/browser/ui/webui/ash/login/os_trial_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/packaged_license_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/parental_handoff_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/password_selection_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/pin_setup_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/recommend_apps_screen_handler.h"
@@ -130,6 +136,7 @@
#include "chrome/grit/oobe_conditional_resources.h"
#include "chrome/grit/oobe_unconditional_resources.h"
#include "chrome/grit/oobe_unconditional_resources_map.h"
+#include "chromeos/ash/components/assistant/buildflags.h"
#include "chromeos/ash/services/auth_factor_config/in_process_instances.h"
#include "chromeos/ash/services/cellular_setup/public/mojom/esim_manager.mojom.h"
#include "chromeos/ash/services/multidevice_setup/multidevice_setup_service.h"
@@ -142,6 +149,7 @@
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
+#include "ui/accessibility/accessibility_features.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/webui/web_ui_util.h"
@@ -293,12 +301,23 @@ void CreateAndAddOobeUIDataSource(Profile* profile,
source->AddBoolean("isOobeLazyLoadingEnabled",
features::IsOobeLazyLoadingEnabled());
// TODO (b/268463435) Cleanup OobeJelly
+ source->AddBoolean("isJellyEnabled", features::IsOobeJellyEnabled());
source->AddBoolean("isOobeJellyEnabled", features::IsOobeJellyEnabled());
+ source->AddBoolean("isOobeJellyModalEnabled",
+ features::IsOobeJellyModalEnabled());
// TODO (b/269117729) Cleanup OobeSimon
source->AddBoolean("isOobeSimonEnabled", features::IsOobeSimonEnabled());
+ source->AddBoolean(
+ "isChromeVoxHintImprovementsEnabled",
+ ::features::
+ IsExperimentalAccessibilityChromeVoxOobeDialogImprovementsEnabled());
+ source->AddBoolean("isOobeAssistantEnabled",
+ !features::IsOobeSkipAssistantEnabled());
source->AddBoolean("isOobeGaiaInfoScreenEnabled",
features::IsOobeGaiaInfoScreenEnabled());
source->AddBoolean("isChoobeEnabled", features::IsOobeChoobeEnabled());
+ source->AddBoolean("isSoftwareUpdateEnabled",
+ features::IsOobeSoftwareUpdateEnabled());
source->AddBoolean(
"isArcVmDataMigrationEnabled",
base::FeatureList::IsEnabled(arc::kEnableArcVmDataMigration));
@@ -307,7 +326,7 @@ void CreateAndAddOobeUIDataSource(Profile* profile,
features::IsOobeTouchpadScrollEnabled());
source->AddBoolean("isDrivePinningEnabled",
- features::IsOobeDrivePinningEnabled());
+ drive::util::IsOobeDrivePinningScreenEnabled());
// Whether the timings in oobe_trace.js will be output to the console.
source->AddBoolean(
@@ -320,6 +339,12 @@ void CreateAndAddOobeUIDataSource(Profile* profile,
source->AddBoolean("isOobeSoftwareUpdateEnabled",
features::IsOobeSoftwareUpdateEnabled());
+ source->AddBoolean("isPasswordSelectionEnabledInOobe",
+ features::IsPasswordSelectionEnabledInOobe());
+
+ source->AddBoolean("isOobeConsumersLocalPasswordsEnabled",
+ features::AreLocalPasswordsEnabledForConsumers());
+
// Configure shared resources
AddProductLogoResources(source);
if (ash::features::IsOobeSimonEnabled()) {
@@ -444,6 +469,10 @@ void OobeUI::ConfigureOobeDisplay() {
AddScreenHandler(std::make_unique<FingerprintSetupScreenHandler>());
+ if (features::AreLocalPasswordsEnabledForConsumers()) {
+ AddScreenHandler(std::make_unique<LocalPasswordSetupHandler>());
+ }
+
AddScreenHandler(std::make_unique<GestureNavigationScreenHandler>());
AddScreenHandler(std::make_unique<MarketingOptInScreenHandler>());
@@ -506,10 +535,18 @@ void OobeUI::ConfigureOobeDisplay() {
AddScreenHandler(std::make_unique<ThemeSelectionScreenHandler>());
+ if (features::IsPasswordSelectionEnabledInOobe()) {
+ AddScreenHandler(std::make_unique<PasswordSelectionScreenHandler>());
+ }
+
if (features::IsOobeChoobeEnabled()) {
AddScreenHandler(std::make_unique<ChoobeScreenHandler>());
}
+ if (features::IsOobeSoftwareUpdateEnabled()) {
+ AddScreenHandler(std::make_unique<ConsumerUpdateScreenHandler>());
+ }
+
if (features::IsOobeTouchpadScrollEnabled()) {
AddScreenHandler(std::make_unique<TouchpadScrollScreenHandler>());
}
@@ -518,7 +555,9 @@ void OobeUI::ConfigureOobeDisplay() {
AddScreenHandler(std::make_unique<DisplaySizeScreenHandler>());
}
- if (features::IsOobeDrivePinningEnabled()) {
+ AddScreenHandler(std::make_unique<AddChildScreenHandler>());
+
+ if (drive::util::IsOobeDrivePinningScreenEnabled()) {
AddScreenHandler(std::make_unique<DrivePinningScreenHandler>());
}
@@ -526,7 +565,7 @@ void OobeUI::ConfigureOobeDisplay() {
AddScreenHandler(std::make_unique<CryptohomeRecoveryScreenHandler>());
- Profile* profile = Profile::FromWebUI(web_ui());
+ Profile* const profile = Profile::FromWebUI(web_ui());
// Set up the chrome://theme/ source, for Chrome logo.
content::URLDataSource::Add(profile, std::make_unique<ThemeSource>(profile));
@@ -535,10 +574,13 @@ void OobeUI::ConfigureOobeDisplay() {
profile,
std::make_unique<AboutUIHTMLSource>(chrome::kChromeUITermsHost, profile));
- // TabHelper is required for OOBE webui to make webview working on it.
content::WebContents* contents = web_ui()->GetWebContents();
+
+ // TabHelper is required for OOBE webui to make webview working on it.
extensions::TabHelper::CreateForWebContents(contents);
+ BootTimesRecorderTabHelper::MaybeCreateForWebContents(contents);
+
if (ShouldUpScaleOobe())
UpScaleOobe();
@@ -601,11 +643,6 @@ void OobeUI::BindInterface(
void OobeUI::BindInterface(
mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
- if (!features::IsOobeJellyEnabled()) {
- mojo::ReportBadMessage(
- "Jelly not enabled: OOBE should not listen to color changes.");
- return;
- }
color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
web_ui()->GetWebContents(), std::move(receiver));
}
@@ -720,6 +757,9 @@ base::Value::Dict OobeUI::GetLocalizedStrings() {
if (features::IsOobeJellyEnabled()) {
oobeClasses += "jelly-enabled ";
}
+ if (features::IsOobeJellyModalEnabled()) {
+ oobeClasses += "jelly-modal-enabled ";
+ }
// TODO (b/269117729) Cleanup OobeSimon
if (features::IsOobeSimonEnabled()) {
oobeClasses += "simon-enabled ";
diff --git a/chromium/chrome/browser/ui/webui/ash/login/password_selection_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/password_selection_screen_handler.cc
new file mode 100644
index 00000000000..0832ad3eb7b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/login/password_selection_screen_handler.cc
@@ -0,0 +1,36 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/login/password_selection_screen_handler.h"
+
+#include "base/values.h"
+#include "chrome/browser/ash/login/oobe_screen.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "ui/chromeos/devicetype_utils.h"
+
+namespace ash {
+
+PasswordSelectionScreenHandler::PasswordSelectionScreenHandler()
+ : BaseScreenHandler(kScreenId) {}
+
+PasswordSelectionScreenHandler::~PasswordSelectionScreenHandler() = default;
+
+void PasswordSelectionScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->AddF("passwordSelectionTitle", IDS_PASSWORD_SELECTION_TITLE,
+ ui::GetChromeOSDeviceName());
+ builder->AddF("localPasswordSelectionLabel",
+ IDS_PASSWORD_SELECTION_LOCAL_PASSWORD_LABEL,
+ ui::GetChromeOSDeviceName());
+ builder->Add("gaiaPasswordSelectionLabel",
+ IDS_PASSWORD_SELECTION_GAIA_PASSWORD_LABEL);
+}
+
+void PasswordSelectionScreenHandler::Show() {
+ ShowInWebUI();
+}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/login/password_selection_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/password_selection_screen_handler.h
new file mode 100644
index 00000000000..5fe8f44ddae
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/login/password_selection_screen_handler.h
@@ -0,0 +1,54 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_PASSWORD_SELECTION_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_PASSWORD_SELECTION_SCREEN_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
+
+namespace ash {
+
+class PasswordSelectionScreen;
+
+// Interface for dependency injection between PasswordSelectionScreen and
+// its WebUI representation.
+class PasswordSelectionScreenView
+ : public base::SupportsWeakPtr<PasswordSelectionScreenView> {
+ public:
+ inline constexpr static StaticOobeScreenId kScreenId{
+ "password-selection", "PasswordSelectionScreen"};
+
+ virtual ~PasswordSelectionScreenView() = default;
+
+ // Shows the contents of the screen.
+ virtual void Show() = 0;
+};
+
+class PasswordSelectionScreenHandler : public PasswordSelectionScreenView,
+ public BaseScreenHandler {
+ public:
+ using TView = PasswordSelectionScreenView;
+
+ PasswordSelectionScreenHandler();
+
+ PasswordSelectionScreenHandler(const PasswordSelectionScreenHandler&) =
+ delete;
+ PasswordSelectionScreenHandler& operator=(
+ const PasswordSelectionScreenHandler&) = delete;
+
+ ~PasswordSelectionScreenHandler() override;
+
+ private:
+ // PasswordSelectionScreenView
+ void Show() override;
+
+ // BaseScreenHandler:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_PASSWORD_SELECTION_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/login/signin_userlist_unittest.cc b/chromium/chrome/browser/ui/webui/ash/login/signin_userlist_unittest.cc
index 796de80513e..16d27e85bbb 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/signin_userlist_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/signin_userlist_unittest.cc
@@ -13,7 +13,6 @@
#include "chrome/browser/ash/login/screens/user_selection_screen.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/login/users/multi_profile_user_controller.h"
-#include "chrome/browser/ash/login/users/multi_profile_user_controller_delegate.h"
#include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
@@ -37,8 +36,7 @@ std::string GenerateUserEmail(int number) {
} // namespace
-class SigninPrepareUserListTest : public testing::Test,
- public MultiProfileUserControllerDelegate {
+class SigninPrepareUserListTest : public testing::Test {
public:
SigninPrepareUserListTest()
: fake_user_manager_(new FakeChromeUserManager()),
@@ -57,7 +55,7 @@ class SigninPrepareUserListTest : public testing::Test,
TestingBrowserProcess::GetGlobal());
ASSERT_TRUE(profile_manager_->SetUp());
controller_ = std::make_unique<MultiProfileUserController>(
- this, TestingBrowserProcess::GetGlobal()->local_state());
+ TestingBrowserProcess::GetGlobal()->local_state(), fake_user_manager_);
fake_user_manager_->set_multi_profile_user_controller(controller_.get());
for (size_t i = 0; i < std::size(kUsersPublic); ++i)
@@ -76,20 +74,19 @@ class SigninPrepareUserListTest : public testing::Test,
}
void TearDown() override {
+ fake_user_manager_->set_multi_profile_user_controller(nullptr);
controller_.reset();
profile_manager_.reset();
testing::Test::TearDown();
}
- // MultiProfileUserControllerDelegate:
- void OnUserNotAllowed(const std::string& user_email) override {}
-
FakeChromeUserManager* user_manager() { return fake_user_manager_; }
private:
content::BrowserTaskEnvironment task_environment_;
ScopedCrosSettingsTestHelper cros_settings_test_helper_;
- raw_ptr<FakeChromeUserManager, ExperimentalAsh> fake_user_manager_;
+ raw_ptr<FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh>
+ fake_user_manager_;
user_manager::ScopedUserManager user_manager_enabler_;
std::unique_ptr<TestingProfileManager> profile_manager_;
std::map<std::string, proximity_auth::mojom::AuthType> user_auth_type_map;
diff --git a/chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.cc
index 3b1ccc80fb8..9f67a3bfa9f 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.cc
@@ -199,9 +199,9 @@ void SyncConsentScreenHandler::DeclareLocalizedValues(
IDS_LOGIN_OS_SYNC_CONSENT_SCREEN_TOOLTIP_ADDITIONAL_TEXT, builder);
}
-void SyncConsentScreenHandler::Show(bool is_arc_restricted) {
+void SyncConsentScreenHandler::Show(bool is_lacros_enabled) {
base::Value::Dict data;
- data.Set("isArcRestricted", is_arc_restricted);
+ data.Set("isLacrosEnabled", is_lacros_enabled);
ShowInWebUI(std::move(data));
}
diff --git a/chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.h
index bfae2ec6f66..a74a65d8548 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.h
@@ -25,7 +25,7 @@ class SyncConsentScreenView
virtual ~SyncConsentScreenView() = default;
// Shows the contents of the screen.
- virtual void Show(bool is_arc_restricted) = 0;
+ virtual void Show(bool is_lacros_enabled) = 0;
// The screen is initially shown in a loading state.
// When SyncScreenBehavior becomes Shown, this method should be called to
diff --git a/chromium/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc
index f128885a24a..6d2c478e92c 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc
@@ -66,7 +66,6 @@ void OobeTestAPIHandler::DeclareJSCallbacks() {
void OobeTestAPIHandler::GetAdditionalParameters(base::Value::Dict* dict) {
login::NetworkStateHelper helper_;
dict->Set("testapi_shouldSkipNetworkFirstShow",
- features::IsOobeNetworkScreenSkipEnabled() &&
!switches::IsOOBENetworkScreenSkippingDisabledForTesting() &&
helper_.IsConnectedToEthernet());
diff --git a/chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.cc
index 10fffd0c392..a74c6274905 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.cc
@@ -24,11 +24,19 @@ void UserCreationScreenHandler::DeclareLocalizedValues(
builder->AddF("userCreationTitle", IDS_OOBE_USER_CREATION_TITLE,
ui::GetChromeOSDeviceName());
builder->Add("userCreationSubtitle", IDS_OOBE_USER_CREATION_SUBTITLE);
+ builder->AddF("userCreationUpdatedTitle", IDS_OOBE_USER_CREATION_NEW_TITLE,
+ ui::GetChromeOSDeviceName());
+ builder->Add("userCreationUpdatedSubtitle",
+ IDS_OOBE_USER_CREATION_NEW_SUBTITLE);
builder->AddF("userCreationAddPersonTitle",
IDS_OOBE_USER_CREATION_ADD_PERSON_TITLE,
ui::GetChromeOSDeviceName());
builder->Add("userCreationAddPersonSubtitle",
IDS_OOBE_USER_CREATION_ADD_PERSON_SUBTITLE);
+ builder->Add("userCreationAddPersonUpdatedTitle",
+ IDS_OOBE_USER_CREATION_ADD_PERSON_NEW_TITLE);
+ builder->Add("userCreationAddPersonUpdatedSubtitle",
+ IDS_OOBE_USER_CREATION_ADD_PERSON_NEW_SUBTITLE);
builder->Add("createForSelfLabel", IDS_OOBE_USER_CREATION_SELF_BUTTON_LABEL);
builder->Add("createForSelfDescription",
IDS_OOBE_USER_CREATION_SELF_BUTTON_DESCRIPTION);
@@ -36,29 +44,72 @@ void UserCreationScreenHandler::DeclareLocalizedValues(
IDS_OOBE_USER_CREATION_CHILD_BUTTON_LABEL);
builder->Add("createForChildDescription",
IDS_OOBE_USER_CREATION_CHILD_BUTTON_DESCRIPTION);
- builder->AddF("childSignInTitle", IDS_OOBE_USER_CREATION_CHILD_SIGNIN_TITLE,
- ui::GetChromeOSDeviceName());
- builder->Add("childSignInSubtitle",
- IDS_OOBE_USER_CREATION_CHILD_SIGNIN_SUBTITLE);
- builder->Add("createAccountForChildLabel",
- IDS_OOBE_USER_CREATION_CHILD_ACCOUNT_CREATION_BUTTON_LABEL);
- builder->Add("signInForChildLabel",
- IDS_OOBE_USER_CREATION_CHILD_SIGN_IN_BUTTON_LABEL);
- builder->AddF("childSignInParentNotificationText",
- IDS_OOBE_USER_CREATION_CHILD_SIGN_IN_PARENT_NOTIFICATION_TEXT,
- ui::GetChromeOSDeviceName());
- builder->Add("childSignInLearnMore",
- IDS_OOBE_USER_CREATION_CHILD_SIGNIN_LEARN_MORE);
- builder->Add("childSignInLearnMoreDialogTitle",
- IDS_OOBE_USER_CREATION_CHILD_SIGN_IN_LEARN_MORE_DIALOG_TITLE);
- builder->Add("childSignInLearnMoreDialogText",
- IDS_OOBE_USER_CREATION_CHILD_SIGN_IN_LEARN_MORE_DIALOG_TEXT);
+
+ builder->Add("userCreationPersonalButtonTitle",
+ IDS_OOBE_USER_CREATION_PERSONEL_USE_BUTTON_LABEL);
+ builder->Add("userCreationPersonalButtonDescription",
+ IDS_OOBE_USER_CREATION_PERSONEL_USE_BUTTON_DESCRIPTION);
+ builder->Add("userCreationChildButtonTitle",
+ IDS_OOBE_USER_CREATION_CHILD_USE_BUTTON_LABEL);
+ builder->Add("userCreationChildButtonDescription",
+ IDS_OOBE_USER_CREATION_CHILD_USE_BUTTON_DESCRIPTION);
+ builder->Add("userCreationEnrollButtonTitle",
+ IDS_OOBE_USER_CREATION_ENROLL_USE_BUTTON_LABEL);
+ builder->Add("userCreationEnrollButtonDescription",
+ IDS_OOBE_USER_CREATION_ENROLL_USE_BUTTON_DESCRIPTION);
+ builder->Add("userCreationEnrollLearnMore",
+ IDS_OOBE_USER_CREATION_ENROLL_LEARN_MORE);
+ builder->Add("userCreationLearnMoreAria",
+ IDS_OOBE_USER_CREATION_ENROLL_LEARN_MORE_ARIA);
+
+ // Enrollment Triage Strings
+ builder->Add("userCreationEnrollTriageTitle",
+ IDS_OOBE_USER_CREATION_ENROLL_TRIAGE_TITLE);
+ builder->Add("userCreationEnrollTriageSubtitle",
+ IDS_OOBE_USER_CREATION_ENROLL_SUBTITLE);
+ builder->Add("userCreationEnrollTriageDescriptionTitle",
+ IDS_OOBE_USER_CREATION_ENROLL_ADDITIONAL_DESCRIPTION_TITLE);
+ builder->Add("userCreationEnrollTriageDescription",
+ IDS_OOBE_USER_CREATION_ENROLL_ADDITIONAL_DESCRIPTION);
+ builder->Add("userCreationEnrollTriageAcceptEnrollButtonLabel",
+ IDS_OOBE_USER_CREATION_ENROLL_TRIAGE_ACCEPT_ENROLL_BUTTON_LABEL);
+ builder->Add(
+ "userCreationEnrollTriageDeclineEnrollButtonLabel",
+ IDS_OOBE_USER_CREATION_ENROLL_TRIAGE_DECLINE_ENROLL_BUTTON_LABEL);
+
+ // Child Setup Strings
+ builder->Add("userCreationChildSetupTitle",
+ IDS_OOBE_USER_CREATION_CHILD_SETUP_TITLE);
+ builder->Add("userCreationChildSetupChildAccountButtonText",
+ IDS_OOBE_USER_CREATION_CHILD_SETUP_CHILD_ACCOUNT_BUTTON_LABEL);
+ builder->Add(
+ "userCreationChildSetupChildAccountButtonLabel",
+ IDS_OOBE_USER_CREATION_CHILD_SETUP_CHILD_ACCOUNT_BUTTON_DESCRIPTION);
+ builder->Add("userCreationChildSetupSchoolAccountButtonText",
+ IDS_OOBE_USER_CREATION_CHILD_SETUP_SCHOOL_ACCOUNT_BUTTON_LABEL);
+ builder->Add(
+ "userCreationChildSetupSchoolAccountButtonLabel",
+ IDS_OOBE_USER_CREATION_CHILD_SETUP_SCHOOL_ACCOUNT_BUTTON_DESCRIPTION);
+
+ // Enroll Triage Learn more
+ builder->Add("userCreationEnrollLearnMoreTitle",
+ IDS_OOBE_USER_CREATION_ENROLL_LEARN_MORE_TITLE);
+ builder->Add("userCreationEnrollLearnMoreText",
+ IDS_OOBE_USER_CREATION_ENROLL_LEARN_MORE_TEXT);
}
void UserCreationScreenHandler::Show() {
ShowInWebUI();
}
+void UserCreationScreenHandler::SetTriageStep() {
+ CallExternalAPI("setTriageStep");
+}
+
+void UserCreationScreenHandler::SetChildSetupStep() {
+ CallExternalAPI("setChildSetupStep");
+}
+
void UserCreationScreenHandler::SetIsBackButtonVisible(bool value) {
CallExternalAPI("setIsBackButtonVisible", value);
}
diff --git a/chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h b/chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h
index e0cf4e4371d..4ceb6d2fefe 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h
@@ -25,6 +25,8 @@ class UserCreationView : public base::SupportsWeakPtr<UserCreationView> {
virtual void Show() = 0;
virtual void SetIsBackButtonVisible(bool value) = 0;
+ virtual void SetTriageStep() = 0;
+ virtual void SetChildSetupStep() = 0;
};
class UserCreationScreenHandler : public UserCreationView,
@@ -43,6 +45,8 @@ class UserCreationScreenHandler : public UserCreationView,
private:
void Show() override;
void SetIsBackButtonVisible(bool value) override;
+ void SetTriageStep() override;
+ void SetChildSetupStep() override;
// BaseScreenHandler:
void DeclareLocalizedValues(
diff --git a/chromium/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc b/chromium/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc
index af961d6d7b4..a1dcdb97798 100644
--- a/chromium/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc
@@ -188,11 +188,19 @@ void WelcomeScreenHandler::DeclareLocalizedValues(
// Strings for ChromeVox hint.
builder->Add("activateChromeVox", IDS_OOBE_ACTIVATE_CHROMEVOX);
builder->Add("continueWithoutChromeVox", IDS_OOBE_CONTINUE_WITHOUT_CHROMEVOX);
+ builder->Add("chromevoxHintClose", IDS_OOBE_CHROMEVOX_HINT_CLOSE);
+ builder->Add("chromevoxHintTitle", IDS_OOBE_CHROMEVOX_HINT_TITLE);
builder->Add("chromeVoxHintText", IDS_OOBE_CHROMEVOX_HINT_TEXT);
+ builder->Add("chromeVoxHintTextExpanded",
+ IDS_OOBE_CHROMEVOX_HINT_TEXT_EXPANDED);
builder->Add("chromeVoxHintAnnouncementTextLaptop",
IDS_OOBE_CHROMEVOX_HINT_ANNOUNCEMENT_TEXT_LAPTOP);
builder->Add("chromeVoxHintAnnouncementTextTablet",
IDS_OOBE_CHROMEVOX_HINT_ANNOUNCEMENT_TEXT_TABLET);
+ builder->Add("chromeVoxHintAnnouncementTextLaptopExpanded",
+ IDS_OOBE_CHROMEVOX_HINT_ANNOUNCEMENT_TEXT_LAPTOP_EXPANDED);
+ builder->Add("chromeVoxHintAnnouncementTextTabletExpanded",
+ IDS_OOBE_CHROMEVOX_HINT_ANNOUNCEMENT_TEXT_TABLET_EXPANDED);
// Strings for the device requisition prompt.
builder->Add("deviceRequisitionPromptCancel",
diff --git a/chromium/chrome/browser/ui/webui/ash/mako/OWNERS b/chromium/chrome/browser/ui/webui/ash/mako/OWNERS
new file mode 100644
index 00000000000..f5c70a445d9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/mako/OWNERS
@@ -0,0 +1,3 @@
+curtismcmullan@chromium.org
+jopalmer@chromium.org
+mehrab@chromium.org \ No newline at end of file
diff --git a/chromium/chrome/browser/ui/webui/ash/mako/mako_source.cc b/chromium/chrome/browser/ui/webui/ash/mako/mako_source.cc
new file mode 100644
index 00000000000..1f7825520ed
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/mako/mako_source.cc
@@ -0,0 +1,81 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/mako/mako_source.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/task/thread_pool.h"
+#include "chrome/browser/ui/webui/ash/mako/url_constants.h"
+#include "net/base/mime_util.h"
+
+namespace {
+constexpr char kDefaultMime[] = "text/html";
+constexpr base::FilePath::CharType kMakoRoot[] =
+ FILE_PATH_LITERAL("/usr/share/chromeos-assets/mako");
+constexpr base::FilePath::CharType kOrcaHTML[] = FILE_PATH_LITERAL("orca.html");
+
+void ReadFile(const base::FilePath& relative_path,
+ content::URLDataSource::GotDataCallback callback) {
+ CHECK(!relative_path.ReferencesParent());
+
+ base::FilePath path = base::FilePath(kMakoRoot).Append(relative_path);
+ std::string content;
+ bool result = base::ReadFileToString(path, &content);
+
+ DCHECK(result) << path;
+ std::move(callback).Run(
+ base::MakeRefCounted<base::RefCountedString>(std::move(content)));
+}
+
+} // namespace
+
+namespace ash {
+
+MakoSource::MakoSource() noexcept = default;
+
+std::string MakoSource::GetSource() {
+ return ash::kChromeUIMakoURL;
+}
+
+void MakoSource::StartDataRequest(
+ const GURL& url,
+ const content::WebContents::Getter& wc_getter,
+ content::URLDataSource::GotDataCallback callback) {
+ base::FilePath path(url.path().substr(1));
+ if (path.empty()) {
+ path = base::FilePath(kOrcaHTML);
+ }
+
+ base::ThreadPool::PostTask(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+ base::BindOnce(&ReadFile, path, std::move(callback)));
+}
+
+std::string MakoSource::GetMimeType(const GURL& url) {
+ std::string mime_type(kDefaultMime);
+ std::string extension = base::FilePath(url.path_piece()).Extension();
+ if (!extension.empty()) {
+ net::GetWellKnownMimeTypeFromExtension(extension.substr(1), &mime_type);
+ }
+ return mime_type;
+}
+
+std::string MakoSource::GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName directive) {
+ switch (directive) {
+ case network::mojom::CSPDirectiveName::TrustedTypes:
+ // Intentional space at end - things are appended to this.
+ return "trusted-types goog#html polymer_resin lit-html "
+ "polymer-template-event-attribute-policy polymer-html-literal; ";
+ case network::mojom::CSPDirectiveName::StyleSrc:
+ return "style-src 'unsafe-inline'; ";
+ case network::mojom::CSPDirectiveName::ImgSrc:
+ return "img-src data:; ";
+ default:
+ return content::URLDataSource::GetContentSecurityPolicy(directive);
+ }
+}
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/mako/mako_source.h b/chromium/chrome/browser/ui/webui/ash/mako/mako_source.h
new file mode 100644
index 00000000000..b53c3d12426
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/mako/mako_source.h
@@ -0,0 +1,31 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_MAKO_MAKO_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_MAKO_MAKO_SOURCE_H_
+
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+
+namespace ash {
+
+// Provides the web (html / js / css) content for mako
+// This content is provided by ChromeOS in the rootfs at
+// /usr/share/chromeos-assets/mako
+class MakoSource : public content::URLDataSource {
+ public:
+ MakoSource() noexcept;
+
+ // content::URLDataSource:
+ std::string GetSource() override;
+ void StartDataRequest(
+ const GURL& url,
+ const content::WebContents::Getter& wc_getter,
+ content::URLDataSource::GotDataCallback callback) override;
+ std::string GetMimeType(const GURL& url) override;
+ std::string GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName directive) override;
+};
+} // namespace ash
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_MAKO_MAKO_SOURCE_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/mako/mako_ui.cc b/chromium/chrome/browser/ui/webui/ash/mako/mako_ui.cc
new file mode 100644
index 00000000000..3d8b73f22f2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/mako/mako_ui.cc
@@ -0,0 +1,141 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/mako/mako_ui.h"
+
+#include "ash/constants/ash_switches.h"
+#include "base/command_line.h"
+#include "base/hash/sha1.h"
+#include "chrome/browser/ash/input_method/editor_mediator.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/views/bubble/webui_bubble_dialog_view.h"
+#include "chrome/browser/ui/webui/ash/mako/mako_source.h"
+#include "chrome/browser/ui/webui/ash/mako/url_constants.h"
+#include "chromeos/constants/chromeos_features.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/url_constants.h"
+#include "ui/base/ime/ash/ime_bridge.h"
+#include "ui/webui/untrusted_bubble_web_ui_controller.h"
+
+namespace {
+constexpr gfx::Size kExtensionWindowSize(420, 480);
+constexpr int kPaddingAroundCursor = 8;
+
+class MakoDialogView : public WebUIBubbleDialogView {
+ public:
+ explicit MakoDialogView(
+ std::unique_ptr<BubbleContentsWrapper> contents_wrapper)
+ : WebUIBubbleDialogView(nullptr, contents_wrapper.get()),
+ contents_wrapper_(std::move(contents_wrapper)) {
+ set_has_parent(false);
+ set_corner_radius(20);
+ }
+
+ private:
+ std::unique_ptr<BubbleContentsWrapper> contents_wrapper_;
+};
+
+} // namespace
+
+namespace ash {
+
+MakoUntrustedUIConfig::MakoUntrustedUIConfig()
+ : WebUIConfig(content::kChromeUIUntrustedScheme, ash::kChromeUIMakoHost) {}
+
+MakoUntrustedUIConfig::~MakoUntrustedUIConfig() = default;
+
+std::unique_ptr<content::WebUIController>
+MakoUntrustedUIConfig::CreateWebUIController(content::WebUI* web_ui,
+ const GURL& url) {
+ return std::make_unique<MakoUntrustedUI>(web_ui);
+}
+
+bool MakoUntrustedUIConfig::IsWebUIEnabled(
+ content::BrowserContext* browser_context) {
+ return chromeos::features::IsOrcaEnabled();
+}
+
+MakoUntrustedUI::MakoUntrustedUI(content::WebUI* web_ui)
+ : ui::UntrustedBubbleWebUIController(web_ui) {
+ CHECK(chromeos::features::IsOrcaEnabled());
+
+ const std::string debug_key_hash = base::SHA1HashString(
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ ash::switches::kOrcaKey));
+ // See go/orca-key for the key
+ // Commandline looks like:
+ // out/Default/chrome --user-data-dir=/tmp/auuf123 --orca-key="INSERT KEY
+ // HERE" --enable-features=Orca
+ const std::string hash =
+ "\x7a\xf3\xa1\x57\x28\x48\xc4\x14\x27\x13\x53\x5a\x09\xf3\x0e\xfc\xee\xa6"
+ "\xbb\xa4";
+ // If key fails to match, crash chrome.
+ CHECK_EQ(debug_key_hash, hash);
+
+ content::URLDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+ std::make_unique<MakoSource>());
+}
+MakoUntrustedUI::~MakoUntrustedUI() = default;
+
+void MakoUntrustedUI::BindInterface(
+ mojo::PendingReceiver<input_method::mojom::EditorInstance> receiver) {
+ input_method::EditorMediator::Get()->BindEditorInstance(std::move(receiver));
+}
+
+WEB_UI_CONTROLLER_TYPE_IMPL(MakoUntrustedUI)
+
+MakoPageHandler::MakoPageHandler() {
+ // TODO(b/289859230): Construct MakoUntrustedUI and show it to the user. Save
+ // a ref to the constructed view to allow for closing it at a later time.
+ NOTIMPLEMENTED_LOG_ONCE();
+}
+
+MakoPageHandler::~MakoPageHandler() = default;
+
+void MakoPageHandler::CloseUI() {
+ // TODO(b/289859230): Use the ref saved from construction to close the webui.
+ NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void MakoUntrustedUI::Show(Profile* profile) {
+ ui::InputMethod* input_method =
+ IMEBridge::Get()->GetInputContextHandler()->GetInputMethod();
+ ui::TextInputClient* input_client =
+ input_method ? input_method->GetTextInputClient() : nullptr;
+
+ // Does not show mako if there is no input client.
+ if (!(input_client)) {
+ return;
+ }
+
+ gfx::Size window_size = kExtensionWindowSize;
+ gfx::Rect caret_bounds =
+ input_client ? input_client->GetCaretBounds() : gfx::Rect();
+
+ auto anchor_rect =
+ gfx::Rect(caret_bounds.x() + window_size.width(),
+ caret_bounds.y() - kPaddingAroundCursor, 0,
+ caret_bounds.height() + kPaddingAroundCursor * 2);
+
+ // TODO(b/289969807): 3961 is emoji picker identifier for task manager - we
+ // should have a better one for mako
+ auto contents_wrapper =
+ std::make_unique<BubbleContentsWrapperT<MakoUntrustedUI>>(
+ GURL(kChromeUIOrcaURL), profile, 3961);
+ contents_wrapper->ReloadWebContents();
+
+ auto bubble_view =
+ std::make_unique<MakoDialogView>(std::move(contents_wrapper));
+ auto weak_ptr = bubble_view->GetWeakPtr();
+ views::BubbleDialogDelegateView::CreateBubble(std::move(bubble_view));
+ weak_ptr->SetAnchorRect(anchor_rect);
+ weak_ptr->GetBubbleFrameView()->SetPreferredArrowAdjustment(
+ views::BubbleFrameView::PreferredArrowAdjustment::kOffset);
+ weak_ptr->set_adjust_if_offscreen(true);
+ weak_ptr->ShowUI();
+}
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/mako/mako_ui.h b/chromium/chrome/browser/ui/webui/ash/mako/mako_ui.h
new file mode 100644
index 00000000000..10fb4eba4a0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/mako/mako_ui.h
@@ -0,0 +1,57 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_MAKO_MAKO_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_MAKO_MAKO_UI_H_
+
+#include "chrome/browser/ash/input_method/mojom/editor.mojom.h"
+#include "content/public/browser/webui_config.h"
+#include "ui/webui/untrusted_bubble_web_ui_controller.h"
+
+class Profile;
+
+namespace ash {
+
+// WebUIConfig for chrome://mako
+class MakoUntrustedUIConfig : public content::WebUIConfig {
+ public:
+ MakoUntrustedUIConfig();
+ ~MakoUntrustedUIConfig() override;
+
+ std::unique_ptr<content::WebUIController> CreateWebUIController(
+ content::WebUI* web_ui,
+ const GURL& url) override;
+
+ bool IsWebUIEnabled(content::BrowserContext* browser_context) override;
+};
+
+// The WebUI for chrome://mako
+class MakoUntrustedUI : public ui::UntrustedBubbleWebUIController {
+ public:
+ static void Show(Profile* profile);
+ explicit MakoUntrustedUI(content::WebUI* web_ui);
+ ~MakoUntrustedUI() override;
+
+ void BindInterface(
+ mojo::PendingReceiver<input_method::mojom::EditorInstance> receiver);
+
+ WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+// Used by consumers to control the lifecycle of MakoUntrustedUI.
+class MakoPageHandler {
+ public:
+ // Constructing an instance of this class will trigger the construction,
+ // bootstrapping and showing of the MakoUntrustedUI WebUi bubble.
+ MakoPageHandler();
+ ~MakoPageHandler();
+
+ // Consumers can use this method to close any currently visible
+ // MakoUntrustedUI. Consumers cannot reshow the UI with this instance after
+ // calling this method, a new instance must be created to reshow the UI.
+ void CloseUI();
+};
+
+} // namespace ash
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_MAKO_MAKO_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/mako/url_constants.cc b/chromium/chrome/browser/ui/webui/ash/mako/url_constants.cc
new file mode 100644
index 00000000000..6fbf0f30618
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/mako/url_constants.cc
@@ -0,0 +1,13 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/mako/url_constants.h"
+
+namespace ash {
+
+const char kChromeUIMakoHost[] = "mako";
+const char kChromeUIOrcaURL[] = "chrome-untrusted://mako/orca.html";
+const char kChromeUIMakoURL[] = "chrome-untrusted://mako/";
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/mako/url_constants.h b/chromium/chrome/browser/ui/webui/ash/mako/url_constants.h
new file mode 100644
index 00000000000..91a1237c79d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/mako/url_constants.h
@@ -0,0 +1,15 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_MAKO_URL_CONSTANTS_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_MAKO_URL_CONSTANTS_H_
+
+namespace ash {
+
+extern const char kChromeUIMakoHost[];
+extern const char kChromeUIMakoURL[];
+extern const char kChromeUIOrcaURL[];
+
+} // namespace ash
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_MAKO_URL_CONSTANTS_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc b/chromium/chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc
index 61696789fd6..1c8b5501083 100644
--- a/chromium/chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc
@@ -264,7 +264,8 @@ class ManageMirrorSyncDialogTest : public InProcessBrowserTest {
base::test::ScopedFeatureList feature_list_;
base::ScopedTempDir temp_dir_;
base::FilePath my_files_dir_;
- raw_ptr<content::WebContents, ExperimentalAsh> dialog_contents_;
+ raw_ptr<content::WebContents, DanglingUntriaged | ExperimentalAsh>
+ dialog_contents_;
drive::DriveIntegrationServiceFactory::FactoryCallback
create_drive_integration_service_;
diff --git a/chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.cc b/chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.cc
index 786b42b1115..af88737119b 100644
--- a/chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.cc
@@ -8,6 +8,7 @@
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_backdrop.h"
#include "ash/public/cpp/window_properties.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
@@ -127,6 +128,10 @@ MultiDeviceSetupDialogUI::MultiDeviceSetupDialogUI(content::WebUI* web_ui)
base::make_span(kMultideviceSetupResources,
kMultideviceSetupResourcesSize),
IDR_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_DIALOG_HTML);
+ // Enabling trusted types via trusted_types_util must be done after
+ // webui::SetupWebUIDataSource to override the trusted type CSP with correct
+ // policies for JS WebUIs.
+ ash::EnableTrustedTypesCSP(source);
web_ui->AddMessageHandler(std::make_unique<MultideviceSetupHandler>());
web_ui->AddMessageHandler(std::make_unique<MetricsHandler>());
diff --git a/chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_handler.cc b/chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_handler.cc
index 199426ce1fd..139fec8a46d 100644
--- a/chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_handler.cc
@@ -4,13 +4,13 @@
#include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_handler.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
#include "chrome/browser/ui/webui/ash/user_image_source.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/common/webui_url_constants.h"
#include "components/user_manager/user.h"
#include "ui/base/webui/web_ui_util.h"
diff --git a/chromium/chrome/browser/ui/webui/ash/network_ui.cc b/chromium/chrome/browser/ui/webui/ash/network_ui.cc
index 4acc36c6987..db201d9dbc7 100644
--- a/chromium/chrome/browser/ui/webui/ash/network_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/network_ui.cc
@@ -13,6 +13,7 @@
#include "ash/public/cpp/connectivity_services.h"
#include "ash/public/cpp/esim_manager.h"
#include "ash/public/cpp/network_config_service.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "ash/webui/network_ui/network_diagnostics_resource_provider.h"
#include "ash/webui/network_ui/network_health_resource_provider.h"
#include "ash/webui/network_ui/traffic_counters_resource_provider.h"
@@ -129,7 +130,7 @@ void SetDeviceProperties(base::Value::Dict* dictionary) {
bool IsGuestModeActive() {
return user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
- user_manager::UserManager::Get()->IsLoggedInAsPublicAccount();
+ user_manager::UserManager::Get()->IsLoggedInAsManagedGuestSession();
}
// Get the euicc path for reset euicc operation. Return absl::nullopt if the
@@ -999,8 +1000,6 @@ NetworkUI::NetworkUI(content::WebUI* web_ui)
web_ui->GetWebContents()->GetBrowserContext(),
chrome::kChromeUINetworkHost);
- webui::EnableTrustedTypesCSP(html);
-
html->AddLocalizedStrings(localized_strings);
html->AddBoolean("isGuestModeActive", IsGuestModeActive());
html->AddBoolean("isHotspotEnabled", features::IsHotspotEnabled());
@@ -1017,6 +1016,10 @@ NetworkUI::NetworkUI(content::WebUI* web_ui)
webui::SetupWebUIDataSource(
html, base::make_span(kNetworkUiResources, kNetworkUiResourcesSize),
IDR_NETWORK_UI_NETWORK_HTML);
+ // Enabling trusted types via trusted_types_util must be done after
+ // webui::SetupWebUIDataSource to override the trusted type CSP with correct
+ // policies for JS WebUIs.
+ ash::EnableTrustedTypesCSP(html);
}
NetworkUI::~NetworkUI() = default;
diff --git a/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.cc b/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.cc
index 9d14def20ab..4c7d453bf48 100644
--- a/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.cc
@@ -73,7 +73,7 @@ namespace ash::office_fallback {
// static
bool OfficeFallbackDialog::Show(
const std::vector<storage::FileSystemURL>& file_urls,
- const FallbackReason fallback_reason,
+ FallbackReason fallback_reason,
const std::string& action_id,
DialogChoiceCallback callback) {
// Allow no more than one office fallback dialog at a time. In the case of
@@ -119,9 +119,9 @@ bool OfficeFallbackDialog::Show(
// The pointer is managed by an instance of `views::WebDialogView` and removed
// in `SystemWebDialogDelegate::OnDialogClosed`.
- OfficeFallbackDialog* dialog =
- new OfficeFallbackDialog(file_urls, title_text, reason_message,
- instructions_message, std::move(callback));
+ OfficeFallbackDialog* dialog = new OfficeFallbackDialog(
+ file_urls, fallback_reason, title_text, reason_message,
+ instructions_message, std::move(callback));
dialog->ShowSystemDialog();
return true;
@@ -131,15 +131,17 @@ void OfficeFallbackDialog::OnDialogClosed(const std::string& choice) {
// Save callback as local variable before member variables are deleted during
// dialog close.
DialogChoiceCallback callback = std::move(callback_);
+ FallbackReason fallback_reason = fallback_reason_;
// Delete class.
SystemWebDialogDelegate::OnDialogClosed(choice);
// Run callback after dialog closed.
if (callback)
- std::move(callback).Run(choice);
+ std::move(callback).Run(choice, fallback_reason);
}
OfficeFallbackDialog::OfficeFallbackDialog(
const std::vector<storage::FileSystemURL>& file_urls,
+ FallbackReason fallback_reason,
const std::string& title_text,
const std::string& reason_message,
const std::string& instructions_message,
@@ -147,6 +149,7 @@ OfficeFallbackDialog::OfficeFallbackDialog(
: SystemWebDialogDelegate(GURL(chrome::kChromeUIOfficeFallbackURL),
std::u16string() /* title */),
file_urls_(file_urls),
+ fallback_reason_(fallback_reason),
title_text_(title_text),
reason_message_(reason_message),
instructions_message_(instructions_message),
diff --git a/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.h b/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.h
index 6192d92f613..5f13e7ae395 100644
--- a/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.h
+++ b/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.h
@@ -12,15 +12,16 @@
namespace ash::office_fallback {
-using DialogChoiceCallback =
- base::OnceCallback<void(const std::string& choice)>;
-
// The reason why the user's file can't open.
enum class FallbackReason {
kOffline,
kDriveUnavailable,
};
+using DialogChoiceCallback =
+ base::OnceCallback<void(const std::string& choice,
+ FallbackReason fallback_reason)>;
+
// Defines the web dialog used to allow users to choose what to do when failing
// to open office files.
class OfficeFallbackDialog : public SystemWebDialogDelegate {
@@ -31,7 +32,7 @@ class OfficeFallbackDialog : public SystemWebDialogDelegate {
// Creates and shows the dialog. Returns true if a new dialog has been
// effectively created.
static bool Show(const std::vector<storage::FileSystemURL>& file_urls,
- const FallbackReason fallback_reason,
+ FallbackReason fallback_reason,
const std::string& action_id,
DialogChoiceCallback callback);
@@ -42,6 +43,7 @@ class OfficeFallbackDialog : public SystemWebDialogDelegate {
protected:
OfficeFallbackDialog(const std::vector<storage::FileSystemURL>& file_urls,
+ FallbackReason fallback_reason,
const std::string& title_text,
const std::string& reason_message,
const std::string& instructions_message,
@@ -52,6 +54,7 @@ class OfficeFallbackDialog : public SystemWebDialogDelegate {
private:
const std::vector<storage::FileSystemURL> file_urls_;
+ const FallbackReason fallback_reason_;
const std::string title_text_;
const std::string reason_message_;
const std::string instructions_message_;
diff --git a/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.cc b/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.cc
index ed849494963..b5d985a7958 100644
--- a/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.cc
@@ -7,8 +7,8 @@
#include <utility>
#include "base/functional/bind.h"
+#include "chrome/browser/chromeos/upload_office_to_cloud/upload_office_to_cloud.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
@@ -23,7 +23,7 @@ namespace ash::office_fallback {
bool OfficeFallbackUIConfig::IsWebUIEnabled(
content::BrowserContext* browser_context) {
- return cloud_upload::IsEligibleAndEnabledUploadOfficeToCloud(
+ return chromeos::IsEligibleAndEnabledUploadOfficeToCloud(
Profile::FromBrowserContext(browser_context));
}
diff --git a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.cc b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.cc
index 0693ae30c52..7bd44af3ad5 100644
--- a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.cc
@@ -98,6 +98,14 @@ bool ParentAccessDialog::ShouldCloseDialogOnEscape() const {
return true;
}
+bool ParentAccessDialog::ShouldShowDialogTitle() const {
+ return false;
+}
+// The close button is implemented in the WebUI itself.
+bool ParentAccessDialog::ShouldShowCloseButton() const {
+ return false;
+}
+
parent_access_ui::mojom::ParentAccessParamsPtr
ParentAccessDialog::CloneParentAccessParams() {
return parent_access_params_->Clone();
diff --git a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h
index 6ad616efa40..e70ae2834f8 100644
--- a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h
+++ b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h
@@ -53,6 +53,8 @@ class ParentAccessDialog : public ParentAccessUIHandlerDelegate,
ui::ModalType GetDialogModalType() const override;
void GetDialogSize(gfx::Size* size) const override;
bool ShouldCloseDialogOnEscape() const override;
+ bool ShouldShowDialogTitle() const override;
+ bool ShouldShowCloseButton() const override;
// ParentAccessUIHandlerDelegate:
parent_access_ui::mojom::ParentAccessParamsPtr CloneParentAccessParams()
diff --git a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog_browsertest.cc b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog_browsertest.cc
index a866f3bccb2..d4729653f8d 100644
--- a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog_browsertest.cc
@@ -100,7 +100,8 @@ IN_PROC_BROWSER_TEST_P(ParentAccessDialogBrowserTest, ShowDialog) {
// Verify that it is correctly configured.
EXPECT_EQ(dialog->GetDialogContentURL().spec(),
chrome::kChromeUIParentAccessURL);
- EXPECT_TRUE(dialog->ShouldShowCloseButton());
+ EXPECT_FALSE(dialog->ShouldShowDialogTitle());
+ EXPECT_FALSE(dialog->ShouldShowCloseButton());
EXPECT_EQ(dialog->GetDialogModalType(), ui::ModalType::MODAL_TYPE_SYSTEM);
// Send ESCAPE keypress. EventGenerator requires the root window, which has
diff --git a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
index af36bc85574..a68686e2ad9 100644
--- a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
@@ -8,6 +8,8 @@
#include <string>
#include <utility>
+#include "ash/constants/ash_features.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
@@ -24,6 +26,7 @@
#include "content/public/browser/web_ui_data_source.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
+#include "ui/webui/color_change_listener/color_change_handler.h"
namespace ash {
@@ -57,6 +60,12 @@ void ParentAccessUI::BindInterface(
std::move(receiver), identity_manager, ParentAccessDialog::GetInstance());
}
+void ParentAccessUI::BindInterface(
+ mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
+ color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
+ web_ui()->GetWebContents(), std::move(receiver));
+}
+
parent_access_ui::mojom::ParentAccessUIHandler*
ParentAccessUI::GetHandlerForTest() {
return mojo_api_handler_.get();
@@ -65,7 +74,7 @@ ParentAccessUI::GetHandlerForTest() {
void ParentAccessUI::SetUpResources() {
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
Profile::FromWebUI(web_ui()), chrome::kChromeUIParentAccessHost);
- webui::EnableTrustedTypesCSP(source);
+ ash::EnableTrustedTypesCSP(source);
source->EnableReplaceI18nInJS();
@@ -73,6 +82,8 @@ void ParentAccessUI::SetUpResources() {
source->AddResourcePath("parent_access_controller.js",
IDR_PARENT_ACCESS_CONTROLLER_JS);
source->AddResourcePath("parent_access_app.js", IDR_PARENT_ACCESS_APP_JS);
+ source->AddResourcePath("parent_access_template.js",
+ IDR_PARENT_ACCESS_TEMPLATE_JS);
source->AddResourcePath("parent_access_ui.js", IDR_PARENT_ACCESS_UI_JS);
source->AddResourcePath("parent_access_ui_handler.js",
IDR_PARENT_ACCESS_UI_HANDLER_JS);
@@ -93,6 +104,9 @@ void ParentAccessUI::SetUpResources() {
IDR_PARENT_ACCESS_BEFORE_JS);
source->AddResourcePath("parent_access_disabled.js",
IDR_PARENT_ACCESS_DISABLED_JS);
+ source->AddResourcePath("parent_access_error.js", IDR_PARENT_ACCESS_ERROR_JS);
+ source->AddResourcePath("parent_access_offline.js",
+ IDR_PARENT_ACCESS_OFFLINE_JS);
source->AddResourcePath("parent_access_ui.mojom-webui.js",
IDR_PARENT_ACCESS_UI_MOJOM_WEBUI_JS);
source->AddResourcePath("webview_manager.js",
@@ -108,10 +122,13 @@ void ParentAccessUI::SetUpResources() {
IDR_PARENT_ACCESS_REQUEST_APPROVAL_DARK_SVG);
source->UseStringsJs();
+ source->AddBoolean("isParentAccessJellyEnabled",
+ features::IsParentAccessJellyEnabled());
source->SetDefaultResource(IDR_PARENT_ACCESS_HTML);
static constexpr webui::LocalizedString kLocalizedStrings[] = {
{"pageTitle", IDS_PARENT_ACCESS_PAGE_TITLE},
+ {"closeButtonTitle", IDS_PARENT_ACCESS_CLOSE_BUTTON_TITLE},
{"approveButtonText", IDS_PARENT_ACCESS_AFTER_APPROVE_BUTTON},
{"denyButtonText", IDS_PARENT_ACCESS_AFTER_DENY_BUTTON},
{"askInPersonButtonText", IDS_PARENT_ACCESS_ASK_IN_PERSON_BUTTON},
diff --git a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h
index e8d00de4bb8..7ec213c6751 100644
--- a/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h
+++ b/chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h
@@ -14,6 +14,11 @@
#include "content/public/common/url_constants.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "ui/web_dialogs/web_dialog_ui.h"
+#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
+
+namespace ui {
+class ColorChangeHandler;
+}
namespace ash {
@@ -47,11 +52,19 @@ class ParentAccessUI : public ui::MojoWebDialogUI {
mojo::PendingReceiver<parent_access_ui::mojom::ParentAccessUIHandler>
receiver);
+ // Instantiates the implementor of the mojom::PageHandler mojo interface
+ // passing the pending receiver that will be internally bound.
+ void BindInterface(
+ mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+ receiver);
+
parent_access_ui::mojom::ParentAccessUIHandler* GetHandlerForTest();
private:
void SetUpResources();
+ std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
+
std::unique_ptr<parent_access_ui::mojom::ParentAccessUIHandler>
mojo_api_handler_;
diff --git a/chromium/chrome/browser/ui/webui/ash/scalable_iph/DIR_METADATA b/chromium/chrome/browser/ui/webui/ash/scalable_iph/DIR_METADATA
new file mode 100644
index 00000000000..bf1496cc392
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/scalable_iph/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chromeos/ash/components/scalable_iph/COMMON_METADATA"
diff --git a/chromium/chrome/browser/ui/webui/ash/scalable_iph/OWNERS b/chromium/chrome/browser/ui/webui/ash/scalable_iph/OWNERS
new file mode 100644
index 00000000000..08c1c2b22f5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/scalable_iph/OWNERS
@@ -0,0 +1 @@
+file://chromeos/ash/components/scalable_iph/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.cc b/chromium/chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.cc
new file mode 100644
index 00000000000..9a2b6aa688c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.cc
@@ -0,0 +1,69 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.h"
+
+#include "ash/constants/ash_features.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_refptr.h"
+#include "chrome/browser/scalable_iph/scalable_iph_factory.h"
+#include "chromeos/ash/components/scalable_iph/scalable_iph.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+
+namespace ash {
+
+namespace {
+
+constexpr char kLoggingPath[] = "log.txt";
+constexpr char kDebugMessageScalableIphNotAvailable[] =
+ "ScalableIph keyed service is not created for this profile.";
+
+bool ShouldHandleRequest(const std::string& path) {
+ return path == kLoggingPath;
+}
+
+} // namespace
+
+bool ScalableIphDebugUIConfig::IsWebUIEnabled(
+ content::BrowserContext* browser_context) {
+ return ash::features::IsScalableIphDebugEnabled();
+}
+
+ScalableIphDebugUI::ScalableIphDebugUI(content::WebUI* web_ui)
+ : ui::UntrustedWebUIController(web_ui) {
+ content::WebUIDataSource* web_ui_data_source =
+ content::WebUIDataSource::CreateAndAdd(
+ web_ui->GetWebContents()->GetBrowserContext(),
+ scalable_iph::kScalableIphDebugURL);
+ web_ui_data_source->SetRequestFilter(
+ base::BindRepeating(&ShouldHandleRequest),
+ base::BindRepeating(&ScalableIphDebugUI::HandleRequest,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+ScalableIphDebugUI::~ScalableIphDebugUI() = default;
+
+void ScalableIphDebugUI::HandleRequest(
+ const std::string& path,
+ content::WebUIDataSource::GotDataCallback callback) {
+ CHECK_EQ(path, kLoggingPath);
+
+ scalable_iph::ScalableIph* scalable_iph =
+ ScalableIphFactory::GetForBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext());
+ if (!scalable_iph) {
+ // `ScalableIph` might not be available even if the feature flag is on, e.g.
+ // pre-conditions don't get satisfied, querying a service before its
+ // initialization, etc.
+ std::move(callback).Run(base::MakeRefCounted<base::RefCountedString>(
+ kDebugMessageScalableIphNotAvailable));
+ return;
+ }
+
+ std::move(callback).Run(base::MakeRefCounted<base::RefCountedString>(
+ scalable_iph->logger()->GenerateLog()));
+}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.h b/chromium/chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.h
new file mode 100644
index 00000000000..84f7e13bdd2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/scalable_iph/scalable_iph_debug_ui.h
@@ -0,0 +1,44 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SCALABLE_IPH_SCALABLE_IPH_DEBUG_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SCALABLE_IPH_SCALABLE_IPH_DEBUG_UI_H_
+
+#include "chromeos/ash/components/scalable_iph/scalable_iph_constants.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/webui_config.h"
+#include "content/public/common/url_constants.h"
+#include "ui/webui/untrusted_web_ui_controller.h"
+
+namespace ash {
+
+class ScalableIphDebugUI;
+
+class ScalableIphDebugUIConfig
+ : public content::DefaultWebUIConfig<ScalableIphDebugUI> {
+ public:
+ ScalableIphDebugUIConfig()
+ : content::DefaultWebUIConfig<ScalableIphDebugUI>(
+ content::kChromeUIUntrustedScheme,
+ scalable_iph::kScalableIphDebugHost) {}
+
+ // content::WebUIConfig:
+ bool IsWebUIEnabled(content::BrowserContext* browser_context) override;
+};
+
+class ScalableIphDebugUI : public ui::UntrustedWebUIController {
+ public:
+ explicit ScalableIphDebugUI(content::WebUI* web_ui);
+ ~ScalableIphDebugUI() override;
+
+ private:
+ void HandleRequest(const std::string& path,
+ content::WebUIDataSource::GotDataCallback callback);
+
+ base::WeakPtrFactory<ScalableIphDebugUI> weak_ptr_factory_{this};
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SCALABLE_IPH_SCALABLE_IPH_DEBUG_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.cc b/chromium/chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.cc
new file mode 100644
index 00000000000..7030b6f84ee
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.cc
@@ -0,0 +1,30 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/sensor_info_resources.h"
+#include "chrome/grit/sensor_info_resources_map.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace ash {
+SensorInfoUI::SensorInfoUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // Set up the chrome://sensor-info source.
+ content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+ web_ui->GetWebContents()->GetBrowserContext(),
+ chrome::kChromeUISensorInfoHost);
+ // Add required resources.
+ webui::SetupWebUIDataSource(
+ source, base::make_span(kSensorInfoResources, kSensorInfoResourcesSize),
+ IDR_SENSOR_INFO_SENSOR_INFO_HTML);
+}
+
+SensorInfoUI::~SensorInfoUI() = default;
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.h b/chromium/chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.h
new file mode 100644
index 00000000000..14a98308449
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/sensor_info/sensor_info_ui.h
@@ -0,0 +1,37 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SENSOR_INFO_SENSOR_INFO_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SENSOR_INFO_SENSOR_INFO_UI_H_
+
+#include "chrome/common/webui_url_constants.h"
+
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/webui_config.h"
+#include "content/public/common/url_constants.h"
+
+namespace ash {
+
+class SensorInfoUI;
+
+// WebUIConfig for chrome://sensor-info.
+class SensorInfoUIConfig : public content::DefaultWebUIConfig<SensorInfoUI> {
+ public:
+ SensorInfoUIConfig()
+ : DefaultWebUIConfig(content::kChromeUIScheme,
+ chrome::kChromeUISensorInfoHost) {}
+};
+
+// The WebUI controller for chrome://sensor-info.
+class SensorInfoUI : public content::WebUIController {
+ public:
+ explicit SensorInfoUI(content::WebUI* web_ui);
+ SensorInfoUI(const SensorInfoUI&) = delete;
+ SensorInfoUI& operator=(const SensorInfoUI&) = delete;
+ ~SensorInfoUI() override;
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SENSOR_INFO_SENSOR_INFO_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/settings/DIR_METADATA b/chromium/chrome/browser/ui/webui/ash/settings/DIR_METADATA
new file mode 100644
index 00000000000..a3b9d18a21d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/settings/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chrome/browser/resources/ash/settings/COMMON_METADATA"
diff --git a/chromium/chrome/browser/ui/webui/ash/settings/OWNERS b/chromium/chrome/browser/ui/webui/ash/settings/OWNERS
new file mode 100644
index 00000000000..e4b1c84ef22
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/settings/OWNERS
@@ -0,0 +1,10 @@
+file://chrome/browser/resources/ash/settings/OWNERS
+
+per-file account_manager_*=file://chromeos/ash/components/account_manager/OWNERS
+per-file apps_section*=file://chrome/browser/ui/webui/app_management/OWNERS
+per-file device_*=file://chrome/browser/resources/ash/settings/device_page/OWNERS
+per-file files_section*=file://ui/file_manager/OWNERS
+per-file kerberos_*=file://chrome/browser/ash/kerberos/OWNERS
+per-file languages_section*=file://chrome/browser/resources/ash/settings/os_languages_page/OWNERS
+per-file multidevice_handler*=file://chromeos/ash/components/multidevice/OWNERS
+per-file printing_*,cups_*=file://chrome/browser/resources/ash/settings/os_printing_page/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/app_management/DIR_METADATA b/chromium/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA
index a571af325c2..a571af325c2 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/app_management/DIR_METADATA
+++ b/chromium/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/app_management/OWNERS b/chromium/chrome/browser/ui/webui/ash/settings/app_management/OWNERS
index 75ba0e34ce6..75ba0e34ce6 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/app_management/OWNERS
+++ b/chromium/chrome/browser/ui/webui/ash/settings/app_management/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/app_management/app_management_uma.h b/chromium/chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h
index ef9f8c1ed03..b63116bbed5 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/app_management/app_management_uma.h
+++ b/chromium/chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h
@@ -2,11 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
-namespace ash {
-namespace settings {
+namespace ash::settings {
// These are used in histograms, do not remove/renumber entries. If you're
// adding to this enum with the intention that it will be logged, update the
@@ -32,7 +31,6 @@ enum class AppManagementEntryPoint {
kMaxValue = kPrivacyIndicatorsNotificationSettings,
};
-} // namespace settings
-} // namespace ash
+} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/calculator/size_calculator.cc b/chromium/chrome/browser/ui/webui/ash/settings/calculator/size_calculator.cc
index e6e69e8d676..9a0594de314 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/calculator/size_calculator.cc
+++ b/chromium/chrome/browser/ui/webui/ash/settings/calculator/size_calculator.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/calculator/size_calculator.h"
+#include "chrome/browser/ui/webui/ash/settings/calculator/size_calculator.h"
#include <cstdint>
#include <numeric>
@@ -234,26 +234,30 @@ DriveOfflineSizeCalculator::DriveOfflineSizeCalculator(Profile* profile)
DriveOfflineSizeCalculator::~DriveOfflineSizeCalculator() = default;
void DriveOfflineSizeCalculator::PerformCalculation() {
- if (!drive::util::IsDriveFsBulkPinningEnabled(profile_)) {
+ if (!drive::util::IsDriveFsBulkPinningEnabled(profile_) &&
+ !base::FeatureList::IsEnabled(
+ ash::features::kFilesGoogleDriveSettingsPage)) {
NotifySizeCalculated(0);
return;
}
- drive::DriveIntegrationService* integration_service =
- drive::DriveIntegrationServiceFactory::FindForProfile(profile_);
-
- if (!integration_service) {
- NotifySizeCalculated(-1);
+ drive::DriveIntegrationService* const service =
+ drive::util::GetIntegrationServiceByProfile(profile_);
+ if (!service) {
+ NotifySizeCalculated(0);
return;
}
- integration_service->GetTotalPinnedSize(
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::BindOnce(&drive::util::ComputeDriveFsContentCacheSize,
+ service->GetDriveFsContentCachePath()),
base::BindOnce(&DriveOfflineSizeCalculator::OnGetOfflineItemsSize,
weak_ptr_factory_.GetWeakPtr()));
}
void DriveOfflineSizeCalculator::OnGetOfflineItemsSize(int64_t offline_bytes) {
- NotifySizeCalculated(offline_bytes);
+ NotifySizeCalculated(offline_bytes > 0 ? offline_bytes : 0);
}
MyFilesSizeCalculator::MyFilesSizeCalculator(Profile* profile)
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/calculator/size_calculator.h b/chromium/chrome/browser/ui/webui/ash/settings/calculator/size_calculator.h
index 859d8524f84..fadb868ac76 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/calculator/size_calculator.h
+++ b/chromium/chrome/browser/ui/webui/ash/settings/calculator/size_calculator.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_CALCULATOR_SIZE_CALCULATOR_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_CALCULATOR_SIZE_CALCULATOR_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_CALCULATOR_SIZE_CALCULATOR_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_CALCULATOR_SIZE_CALCULATOR_H_
#include <array>
#include <bitset>
@@ -366,4 +366,4 @@ class OtherUsersSizeCalculator : public SizeCalculator {
} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_CALCULATOR_SIZE_CALCULATOR_H_
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_CALCULATOR_SIZE_CALCULATOR_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/calculator/size_calculator_test_api.h b/chromium/chrome/browser/ui/webui/ash/settings/calculator/size_calculator_test_api.h
index 3f4a8abea52..c475709317f 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/calculator/size_calculator_test_api.h
+++ b/chromium/chrome/browser/ui/webui/ash/settings/calculator/size_calculator_test_api.h
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_CALCULATOR_SIZE_CALCULATOR_TEST_API_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_CALCULATOR_SIZE_CALCULATOR_TEST_API_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_CALCULATOR_SIZE_CALCULATOR_TEST_API_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_CALCULATOR_SIZE_CALCULATOR_TEST_API_H_
#include <utility>
-#include "chrome/browser/ui/webui/settings/ash/calculator/size_calculator.h"
+#include "chrome/browser/ui/webui/ash/settings/calculator/size_calculator.h"
#include "chrome/browser/ui/webui/settings/ash/device_storage_handler.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -190,4 +190,4 @@ class OtherUsersSizeTestAPI {
} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_CALCULATOR_SIZE_CALCULATOR_TEST_API_H_
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_CALCULATOR_SIZE_CALCULATOR_TEST_API_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/constants/constants_util.cc b/chromium/chrome/browser/ui/webui/ash/settings/constants/constants_util.cc
index 863c0deae39..665a9979f5d 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/constants/constants_util.cc
+++ b/chromium/chrome/browser/ui/webui/ash/settings/constants/constants_util.cc
@@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/constants/constants_util.h"
+#include "chrome/browser/ui/webui/ash/settings/constants/constants_util.h"
+#include "ash/constants/ash_features.h"
#include "base/no_destructor.h"
namespace ash::settings {
@@ -28,18 +29,50 @@ std::vector<T> All() {
// (1) We use a numbering scheme which purposely skips some values for the
// Subpage and Setting enums.
// (2) Some values are deprecated and removed.
- if (chromeos::settings::mojom::IsKnownEnumValue(current))
+ if (chromeos::settings::mojom::IsKnownEnumValue(current)) {
all.push_back(current);
+ }
}
return all;
}
+void IncludeRevampSectionsOnly(std::vector<mojom::Section>& sections) {
+ auto isOldSection = [](mojom::Section section) {
+ // TODO(b/292678609) Gradually add checks here to filter out old Sections
+ // from the set of available Sections. An old Section can be filtered out
+ // once it has been fully incorporated into the new revamp Section.
+ return section == mojom::Section::kDateAndTime ||
+ section == mojom::Section::kLanguagesAndInput ||
+ section == mojom::Section::kReset ||
+ section == mojom::Section::kSearchAndAssistant;
+ };
+ sections.erase(std::remove_if(sections.begin(), sections.end(), isOldSection),
+ sections.end());
+}
+
+void RemoveRevampSections(std::vector<mojom::Section>& sections) {
+ auto isRevampSection = [](mojom::Section section) {
+ return section == mojom::Section::kSystemPreferences;
+ };
+ sections.erase(
+ std::remove_if(sections.begin(), sections.end(), isRevampSection),
+ sections.end());
+}
+
} // namespace
const std::vector<mojom::Section>& AllSections() {
- static const base::NoDestructor<std::vector<mojom::Section>> all_sections(
- All<mojom::Section>());
+ static const base::NoDestructor<std::vector<mojom::Section>> all_sections([] {
+ std::vector<mojom::Section> sections = All<mojom::Section>();
+ if (ash::features::IsOsSettingsRevampWayfindingEnabled()) {
+ IncludeRevampSectionsOnly(sections);
+ } else {
+ RemoveRevampSections(sections);
+ }
+ return sections;
+ }());
+
return *all_sections;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/constants/constants_util.h b/chromium/chrome/browser/ui/webui/ash/settings/constants/constants_util.h
index a694b716cbc..4cc210f86e7 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/constants/constants_util.h
+++ b/chromium/chrome/browser/ui/webui/ash/settings/constants/constants_util.h
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_CONSTANTS_CONSTANTS_UTIL_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_CONSTANTS_CONSTANTS_UTIL_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_CONSTANTS_CONSTANTS_UTIL_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_CONSTANTS_CONSTANTS_UTIL_H_
#include <vector>
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
+#include "ash/webui/settings/public/constants/setting.mojom.h"
namespace ash::settings {
@@ -18,4 +18,4 @@ const std::vector<chromeos::settings::mojom::Setting>& AllSettings();
} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_CONSTANTS_CONSTANTS_UTIL_H_
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_CONSTANTS_CONSTANTS_UTIL_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/BUILD.gn b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/BUILD.gn
index 6f2027b16af..79ad22e97d5 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/BUILD.gn
@@ -7,7 +7,7 @@ import("//mojo/public/tools/bindings/mojom.gni")
assert(is_chromeos_ash)
-mojom("mojo_bindings") {
+mojom("mojom") {
sources = [
"search.mojom",
"search_result_icon.mojom",
@@ -15,7 +15,7 @@ mojom("mojo_bindings") {
]
public_deps = [
- "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
+ "//ash/webui/settings/public/constants:mojom",
"//mojo/public/mojom/base",
]
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/OWNERS b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/OWNERS
index 08850f42120..08850f42120 100644
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/OWNERS
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/search.mojom b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom
index 1c5c3890c3a..bd9ad739585 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/search.mojom
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom
@@ -4,9 +4,9 @@
module ash.settings.mojom;
-import "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom";
-import "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom";
-import "chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom";
+import "ash/webui/settings/public/constants/routes.mojom";
+import "ash/webui/settings/public/constants/setting.mojom";
+import "chrome/browser/ui/webui/ash/settings/search/mojom/search_result_icon.mojom";
import "mojo/public/mojom/base/string16.mojom";
// Describes the type of settings result.
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/search_result_icon.mojom
index 161f6a78e6a..734cd3072dd 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/search_result_icon.mojom
@@ -58,6 +58,7 @@ enum SearchResultIcon {
kStylus,
kSwitchAccess,
kSync,
+ kSystemPreferences,
kWallpaper,
kWifi
};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/user_action_recorder.mojom
index f1d67c11b32..084ce4fab2b 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/mojom/user_action_recorder.mojom
@@ -4,7 +4,7 @@
module ash.settings.mojom;
-import "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom";
+import "ash/webui/settings/public/constants/setting.mojom";
// Value for the setting change; can be a bool, int, or string.
union SettingChangeValue {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/search_concept.h b/chromium/chrome/browser/ui/webui/ash/settings/search/search_concept.h
index 4e15a6c6334..ca1fcf6f54e 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/search_concept.h
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/search_concept.h
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_CONCEPT_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_CONCEPT_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_SEARCH_SEARCH_CONCEPT_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_SEARCH_SEARCH_CONCEPT_H_
+#include "ash/webui/settings/public/constants/routes.mojom.h"
+#include "ash/webui/settings/public/constants/setting.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/search_result_icon.mojom.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_identifier.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
namespace ash::settings {
@@ -66,4 +66,4 @@ struct SearchConcept {
} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_CONCEPT_H_
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_SEARCH_SEARCH_CONCEPT_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/search_handler.cc b/chromium/chrome/browser/ui/webui/ash/settings/search/search_handler.cc
index 424f1e16486..9db414f881e 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/search_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/search_handler.cc
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/search/search_handler.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_handler.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/search_result_icon.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_concept.h"
#include "chrome/browser/ui/webui/settings/ash/hierarchy.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_sections.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_concept.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy.h"
#include "ui/base/l10n/l10n_util.h"
@@ -92,8 +92,9 @@ void SearchHandler::Observe(
}
void SearchHandler::OnRegistryUpdated() {
- for (auto& observer : observers_)
+ for (auto& observer : observers_) {
observer->OnSearchResultsChanged();
+ }
}
std::vector<mojom::SearchResultPtr> SearchHandler::GenerateSearchResultsArray(
@@ -104,8 +105,9 @@ std::vector<mojom::SearchResultPtr> SearchHandler::GenerateSearchResultsArray(
std::vector<mojom::SearchResultPtr> search_results;
for (const auto& result : local_search_service_results) {
mojom::SearchResultPtr result_ptr = ResultToSearchResult(result);
- if (result_ptr)
+ if (result_ptr) {
search_results.push_back(std::move(result_ptr));
+ }
}
std::sort(search_results.begin(), search_results.end(), CompareSearchResults);
@@ -203,8 +205,9 @@ SearchHandler::AddSectionResultIfPossible(
mojom::Section section,
std::vector<mojom::SearchResultPtr>* results) const {
// If |results| already includes |section|, do not add it again.
- if (ContainsSectionResult(*results, section))
+ if (ContainsSectionResult(*results, section)) {
return curr_position;
+ }
mojom::SearchResultPtr section_result =
hierarchy_->GetSectionMetadata(section).ToSearchResult(
@@ -212,8 +215,9 @@ SearchHandler::AddSectionResultIfPossible(
// Don't add a result for a parent section if it has the exact same text as
// the child result, since this results in a broken-looking UI.
- if (section_result->text == child_result->text)
+ if (section_result->text == child_result->text) {
return curr_position;
+ }
return results->insert(curr_position + 1, std::move(section_result));
}
@@ -226,8 +230,9 @@ SearchHandler::AddSubpageResultIfPossible(
double relevance_score,
std::vector<mojom::SearchResultPtr>* results) const {
// If |results| already includes |subpage|, do not add it again.
- if (ContainsSubpageResult(*results, subpage))
+ if (ContainsSubpageResult(*results, subpage)) {
return curr_position;
+ }
mojom::SearchResultPtr subpage_result =
hierarchy_->GetSubpageMetadata(subpage).ToSearchResult(
@@ -235,8 +240,9 @@ SearchHandler::AddSubpageResultIfPossible(
// Don't add a result for a parent subpage if it has the exact same text as
// the child result, since this results in a broken-looking UI.
- if (subpage_result->text == child_result->text)
+ if (subpage_result->text == child_result->text) {
return curr_position;
+ }
return results->insert(
curr_position + 1,
@@ -251,14 +257,16 @@ mojom::SearchResultPtr SearchHandler::ResultToSearchResult(
// If the concept was not registered, no metadata is available. This can occur
// if the search tag was dynamically unregistered during the asynchronous
// Find() call.
- if (!search_concept)
+ if (!search_concept) {
return nullptr;
+ }
// |result| is expected to have one position, whose ID is a stringified int.
DCHECK_EQ(1u, result.positions.size());
int content_id;
- if (!base::StringToInt(result.positions[0].content_id, &content_id))
+ if (!base::StringToInt(result.positions[0].content_id, &content_id)) {
return nullptr;
+ }
std::string url;
mojom::SearchResultIdentifierPtr result_id;
@@ -317,18 +325,22 @@ bool SearchHandler::CompareSearchResults(const mojom::SearchResultPtr& first,
// positive value indicates that |second| is ranked higher than |first|.
int32_t default_rank_diff = static_cast<int32_t>(first->default_rank) -
static_cast<int32_t>(second->default_rank);
- if (default_rank_diff < 0)
+ if (default_rank_diff < 0) {
return true;
- if (default_rank_diff > 0)
+ }
+ if (default_rank_diff > 0) {
return false;
+ }
// At this point, the default ranks are equal, so compare relevance scores. A
// higher relevance score indicates a better text match, so the reverse is
// true this time.
- if (first->relevance_score > second->relevance_score)
+ if (first->relevance_score > second->relevance_score) {
return true;
- if (first->relevance_score < second->relevance_score)
+ }
+ if (first->relevance_score < second->relevance_score) {
return false;
+ }
// Default rank and relevance scores are equal, so prefer the result which is
// higher on the hierarchy. kSection is declared before kSubpage which is
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/search_handler.h b/chromium/chrome/browser/ui/webui/ash/settings/search/search_handler.h
index 1a21d6d95cc..a3964a6ca4e 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/search_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/search_handler.h
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_SEARCH_SEARCH_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_SEARCH_SEARCH_HANDLER_H_
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy.h"
#include "chromeos/ash/components/local_search_service/public/mojom/index.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -117,4 +117,4 @@ class SearchHandler : public mojom::SearchHandler,
} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_HANDLER_H_
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_SEARCH_SEARCH_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/search_handler_unittest.cc b/chromium/chrome/browser/ui/webui/ash/settings/search/search_handler_unittest.cc
index 867bd31870c..ee974282ad9 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/search_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/search_handler_unittest.cc
@@ -2,16 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/search/search_handler.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_handler.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/fake_hierarchy.h"
#include "chrome/browser/ui/webui/settings/ash/fake_os_settings_sections.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom-test-utils.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy.h"
#include "mojo/public/cpp/bindings/remote.h"
@@ -134,6 +135,16 @@ class SearchHandlerTest : public testing::Test {
updater.RemoveSearchTags(search_tags);
}
+ std::vector<mojom::SearchResultPtr> Search(
+ const std::u16string& query,
+ uint32_t max_num_results,
+ mojom::ParentResultBehavior parent_result_behavior) {
+ base::test::TestFuture<std::vector<mojom::SearchResultPtr>> future;
+ handler_remote_->Search(query, max_num_results, parent_result_behavior,
+ future.GetCallback());
+ return future.Take();
+ }
+
base::test::TaskEnvironment task_environment_;
std::unique_ptr<local_search_service::LocalSearchServiceProxy>
local_search_service_proxy_ =
@@ -157,37 +168,33 @@ TEST_F(SearchHandlerTest, AddAndRemove) {
std::vector<mojom::SearchResultPtr> search_results;
// 3 results should be available for a "Print" query.
- mojom::SearchHandlerAsyncWaiter(handler_remote_.get())
- .Search(u"Print",
- /*max_num_results=*/3u,
- mojom::ParentResultBehavior::kDoNotIncludeParentResults,
- &search_results);
+ search_results =
+ Search(u"Print",
+ /*max_num_results=*/3u,
+ mojom::ParentResultBehavior::kDoNotIncludeParentResults);
EXPECT_EQ(search_results.size(), 3u);
// Limit results to 1 max and ensure that only 1 result is returned.
- mojom::SearchHandlerAsyncWaiter(handler_remote_.get())
- .Search(u"Print",
- /*max_num_results=*/1u,
- mojom::ParentResultBehavior::kDoNotIncludeParentResults,
- &search_results);
+ search_results =
+ Search(u"Print",
+ /*max_num_results=*/1u,
+ mojom::ParentResultBehavior::kDoNotIncludeParentResults);
EXPECT_EQ(search_results.size(), 1u);
// Search for a query which should return no results.
- mojom::SearchHandlerAsyncWaiter(handler_remote_.get())
- .Search(u"QueryWithNoResults",
- /*max_num_results=*/3u,
- mojom::ParentResultBehavior::kDoNotIncludeParentResults,
- &search_results);
+ search_results =
+ Search(u"QueryWithNoResults",
+ /*max_num_results=*/3u,
+ mojom::ParentResultBehavior::kDoNotIncludeParentResults);
EXPECT_TRUE(search_results.empty());
// Remove printing search tags to registry and verify that no results are
// returned for "Printing".
RemoveSearchTags(GetPrintingSearchConcepts());
- mojom::SearchHandlerAsyncWaiter(handler_remote_.get())
- .Search(u"Print",
- /*max_num_results=*/3u,
- mojom::ParentResultBehavior::kDoNotIncludeParentResults,
- &search_results);
+ search_results =
+ Search(u"Print",
+ /*max_num_results=*/3u,
+ mojom::ParentResultBehavior::kDoNotIncludeParentResults);
EXPECT_TRUE(search_results.empty());
EXPECT_EQ(2u, observer_.num_calls());
}
@@ -195,12 +202,10 @@ TEST_F(SearchHandlerTest, AddAndRemove) {
TEST_F(SearchHandlerTest, UrlModification) {
// Add printing search tags to registry and search for "Saved".
AddSearchTags(GetPrintingSearchConcepts());
- std::vector<mojom::SearchResultPtr> search_results;
- mojom::SearchHandlerAsyncWaiter(handler_remote_.get())
- .Search(u"Saved",
- /*max_num_results=*/3u,
- mojom::ParentResultBehavior::kDoNotIncludeParentResults,
- &search_results);
+ std::vector<mojom::SearchResultPtr> search_results =
+ Search(u"Saved",
+ /*max_num_results=*/3u,
+ mojom::ParentResultBehavior::kDoNotIncludeParentResults);
// Only the "saved printers" item should be returned.
EXPECT_EQ(search_results.size(), 1u);
@@ -214,16 +219,14 @@ TEST_F(SearchHandlerTest, UrlModification) {
TEST_F(SearchHandlerTest, AltTagMatch) {
// Add printing search tags to registry.
AddSearchTags(GetPrintingSearchConcepts());
- std::vector<mojom::SearchResultPtr> search_results;
// Search for "CUPS". The IDS_OS_SETTINGS_TAG_PRINTING result has an alternate
// tag "CUPS" (referring to the Unix printing protocol), so we should receive
// one match.
- mojom::SearchHandlerAsyncWaiter(handler_remote_.get())
- .Search(u"CUPS",
- /*max_num_results=*/3u,
- mojom::ParentResultBehavior::kDoNotIncludeParentResults,
- &search_results);
+ std::vector<mojom::SearchResultPtr> search_results =
+ Search(u"CUPS",
+ /*max_num_results=*/3u,
+ mojom::ParentResultBehavior::kDoNotIncludeParentResults);
EXPECT_EQ(search_results.size(), 1u);
// Verify the result text and canonical restult text.
@@ -236,16 +239,13 @@ TEST_F(SearchHandlerTest, AltTagMatch) {
TEST_F(SearchHandlerTest, AllowParentResult) {
// Add printing search tags to registry.
AddSearchTags(GetPrintingSearchConcepts());
- std::vector<mojom::SearchResultPtr> search_results;
// Search for "Saved", which should only apply to the "saved printers" item.
// Pass the kAllowParentResults flag, which should also cause its parent
// subpage item to be returned.
- mojom::SearchHandlerAsyncWaiter(handler_remote_.get())
- .Search(u"Saved",
- /*max_num_results=*/3u,
- mojom::ParentResultBehavior::kAllowParentResults,
- &search_results);
+ std::vector<mojom::SearchResultPtr> search_results = Search(
+ u"Saved",
+ /*max_num_results=*/3u, mojom::ParentResultBehavior::kAllowParentResults);
EXPECT_EQ(search_results.size(), 2u);
EXPECT_FALSE(search_results[1]->was_generated_from_text_match);
}
@@ -253,16 +253,13 @@ TEST_F(SearchHandlerTest, AllowParentResult) {
TEST_F(SearchHandlerTest, DefaultRank) {
// Add printing search tags to registry.
AddSearchTags(GetPrintingSearchConcepts());
- std::vector<mojom::SearchResultPtr> search_results;
// Search for "Print". Only the IDS_OS_SETTINGS_TAG_PRINTING result
// contains the word "Printing", but the other results have the similar word
// "Printer". Thus, "Printing" has a higher relevance score.
- mojom::SearchHandlerAsyncWaiter(handler_remote_.get())
- .Search(u"Print",
- /*max_num_results=*/3u,
- mojom::ParentResultBehavior::kAllowParentResults,
- &search_results);
+ std::vector<mojom::SearchResultPtr> search_results = Search(
+ u"Print",
+ /*max_num_results=*/3u, mojom::ParentResultBehavior::kAllowParentResults);
EXPECT_EQ(search_results.size(), 3u);
// Since the IDS_OS_SETTINGS_TAG_PRINTING result has a default rank of kLow,
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.cc b/chromium/chrome/browser/ui/webui/ash/settings/search/search_tag_registry.cc
index 5583bc46daa..7f41e16905e 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.cc
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/search_tag_registry.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include <algorithm>
#include <sstream>
@@ -10,7 +10,7 @@
#include "base/feature_list.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_concept.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_concept.h"
#include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy.h"
#include "ui/base/l10n/l10n_util.h"
@@ -25,8 +25,9 @@ std::vector<int> GetMessageIds(const SearchConcept* search_concept) {
// Add alternate IDs, if they exist.
for (size_t i = 0; i < SearchConcept::kMaxAltTagsPerConcept; ++i) {
int curr_alt_tag_message_id = search_concept->alt_tag_ids[i];
- if (curr_alt_tag_message_id == SearchConcept::kAltTagEnd)
+ if (curr_alt_tag_message_id == SearchConcept::kAltTagEnd) {
break;
+ }
alt_tag_message_ids.push_back(curr_alt_tag_message_id);
}
@@ -59,16 +60,20 @@ SearchTagRegistry::ScopedTagUpdater::~ScopedTagUpdater() {
// Only add concepts which are intended to be added and have not yet been
// added; only remove concepts which are intended to be removed and have
// already been added.
- if (is_pending_add && !is_concept_already_added)
+ if (is_pending_add && !is_concept_already_added) {
pending_adds.push_back(search_concept);
- if (!is_pending_add && is_concept_already_added)
+ }
+ if (!is_pending_add && is_concept_already_added) {
pending_removals.push_back(search_concept);
+ }
}
- if (!pending_adds.empty())
+ if (!pending_adds.empty()) {
registry_->AddSearchTags(pending_adds);
- if (!pending_removals.empty())
+ }
+ if (!pending_removals.empty()) {
registry_->RemoveSearchTags(pending_removals);
+ }
}
void SearchTagRegistry::ScopedTagUpdater::AddSearchTags(
@@ -125,9 +130,10 @@ void SearchTagRegistry::AddSearchTags(
// Add each concept to the map. Note that it is safe to take the address of
// each concept because all concepts are allocated via static
// base::NoDestructor objects in the Get*SearchConcepts() helper functions.
- for (const auto* search_concept : search_tags)
+ for (const auto* search_concept : search_tags) {
result_id_to_metadata_list_map_[ToResultId(*search_concept)] =
search_concept;
+ }
index_remote_->AddOrUpdate(
ConceptVectorToDataVector(search_tags),
@@ -152,8 +158,9 @@ void SearchTagRegistry::RemoveSearchTags(
const SearchConcept* SearchTagRegistry::GetTagMetadata(
const std::string& result_id) const {
const auto it = result_id_to_metadata_list_map_.find(result_id);
- if (it == result_id_to_metadata_list_map_.end())
+ if (it == result_id_to_metadata_list_map_.end()) {
return nullptr;
+ }
return it->second;
}
@@ -200,8 +207,9 @@ SearchTagRegistry::ConceptVectorToDataVector(
}
void SearchTagRegistry::NotifyRegistryUpdated() {
- for (auto& observer : observer_list_)
+ for (auto& observer : observer_list_) {
observer.OnRegistryUpdated();
+ }
}
void SearchTagRegistry::NotifyRegistryAdded() {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h b/chromium/chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h
index c4ecab3ea7e..8c00576d94b 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_TAG_REGISTRY_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_TAG_REGISTRY_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_SEARCH_SEARCH_TAG_REGISTRY_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_SEARCH_SEARCH_TAG_REGISTRY_H_
#include <unordered_map>
#include <utility>
@@ -78,6 +78,7 @@ class SearchTagRegistry {
// returned by the LocalSearchService. If no metadata is available, null
// is returned.
const SearchConcept* GetTagMetadata(const std::string& result_id) const;
+
private:
FRIEND_TEST_ALL_PREFIXES(SearchTagRegistryTest, AddAndRemove);
@@ -106,4 +107,4 @@ class SearchTagRegistry {
} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_TAG_REGISTRY_H_
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_SEARCH_SEARCH_TAG_REGISTRY_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/search_tag_registry_unittest.cc b/chromium/chrome/browser/ui/webui/ash/settings/search/search_tag_registry_unittest.cc
index 6b0ea8323bf..7797e660b72 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/search_tag_registry_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/settings/search/search_tag_registry_unittest.cc
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "base/no_destructor.h"
#include "base/test/task_environment.h"
#include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_concept.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_concept.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/local_search_service/public/mojom/index.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/BUILD.gn b/chromium/chrome/browser/ui/webui/ash/settings/test_support/BUILD.gn
index 1eb7eebabb2..1eb7eebabb2 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/ash/settings/test_support/BUILD.gn
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.cc b/chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_browser_test_mixin.cc
index cdeb24c4f28..d292545dfb3 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.cc
+++ b/chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_browser_test_mixin.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.h"
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_browser_test_mixin.h"
#include <memory>
#include <string>
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.h b/chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_browser_test_mixin.h
index 45c2d9f6fe7..1638630d8bf 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.h
+++ b/chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_browser_test_mixin.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_OS_SETTINGS_BROWSER_TEST_MIXIN_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_OS_SETTINGS_BROWSER_TEST_MIXIN_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_TEST_SUPPORT_OS_SETTINGS_BROWSER_TEST_MIXIN_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_TEST_SUPPORT_OS_SETTINGS_BROWSER_TEST_MIXIN_H_
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
@@ -114,4 +114,4 @@ class OSSettingsBrowserTestMixin : public InProcessBrowserTestMixin {
} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_OS_SETTINGS_BROWSER_TEST_MIXIN_H_
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_TEST_SUPPORT_OS_SETTINGS_BROWSER_TEST_MIXIN_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.cc b/chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.cc
index 378734f18cd..7e67d231910 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.cc
+++ b/chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.cc
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.h"
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
namespace ash::settings {
-OSSettingsLockScreenBrowserTestBase::OSSettingsLockScreenBrowserTestBase() {
+OSSettingsLockScreenBrowserTestBase::OSSettingsLockScreenBrowserTestBase(
+ PasswordType password_type)
+ : password_type_(password_type) {
// We configure FakeUserDataAuthClient (via `cryptohome_`) here and not
// later because the global PinBackend object reads whether or not
// cryptohome PINs are supported on startup. If we set up
@@ -16,7 +18,14 @@ OSSettingsLockScreenBrowserTestBase::OSSettingsLockScreenBrowserTestBase() {
cryptohome_.set_enable_auth_check(true);
cryptohome_.set_supports_low_entropy_credentials(true);
cryptohome_.MarkUserAsExisting(GetAccountId());
- cryptohome_.AddGaiaPassword(GetAccountId(), kPassword);
+ switch (password_type) {
+ case PasswordType::kGaia:
+ cryptohome_.AddGaiaPassword(GetAccountId(), kPassword);
+ break;
+ case PasswordType::kLocal:
+ cryptohome_.AddLocalPassword(GetAccountId(), kPassword);
+ break;
+ }
}
OSSettingsLockScreenBrowserTestBase::~OSSettingsLockScreenBrowserTestBase() =
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.h b/chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h
index 00c9a89385a..bd96ac00363 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.h
+++ b/chromium/chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h
@@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_OS_SETTINGS_LOCK_SCREEN_BROWSER_TEST_BASE_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_OS_SETTINGS_LOCK_SCREEN_BROWSER_TEST_BASE_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_TEST_SUPPORT_OS_SETTINGS_LOCK_SCREEN_BROWSER_TEST_BASE_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_TEST_SUPPORT_OS_SETTINGS_LOCK_SCREEN_BROWSER_TEST_BASE_H_
-#include "ash/constants/ash_features.h"
#include "chrome/browser/ash/login/test/cryptohome_mixin.h"
#include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
-#include "chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.h"
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_browser_test_mixin.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/data/webui/settings/chromeos/test_api.test-mojom-test-utils.h"
#include "components/account_id/account_id.h"
@@ -23,10 +22,15 @@ namespace ash::settings {
class OSSettingsLockScreenBrowserTestBase
: public MixinBasedInProcessBrowserTest {
public:
+ enum class PasswordType {
+ kGaia,
+ kLocal,
+ };
// The password of the user that is set up by this fixture.
static constexpr char kPassword[] = "the-password";
- OSSettingsLockScreenBrowserTestBase();
+ explicit OSSettingsLockScreenBrowserTestBase(
+ PasswordType = PasswordType::kGaia);
~OSSettingsLockScreenBrowserTestBase() override;
void SetUpOnMainThread() override;
@@ -49,6 +53,7 @@ class OSSettingsLockScreenBrowserTestBase
const AccountId& GetAccountId();
protected:
+ PasswordType password_type_;
CryptohomeMixin cryptohome_{&mixin_host_};
LoggedInUserMixin logged_in_user_mixin_{
&mixin_host_, LoggedInUserMixin::LogInType::kRegular,
@@ -67,4 +72,4 @@ class OSSettingsLockScreenBrowserTestBase
} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_OS_SETTINGS_LOCK_SCREEN_BROWSER_TEST_BASE_H_
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_TEST_SUPPORT_OS_SETTINGS_LOCK_SCREEN_BROWSER_TEST_BASE_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_credentials_dialog.cc b/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_credentials_dialog.cc
index ced0a1e4e93..b2c1d6f0973 100644
--- a/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_credentials_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_credentials_dialog.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "ash/webui/common/trusted_types_util.h"
#include "base/functional/bind.h"
#include "base/json/json_writer.h"
#include "base/values.h"
@@ -116,7 +117,7 @@ SmbCredentialsDialogUI::SmbCredentialsDialogUI(content::WebUI* web_ui)
: ui::WebDialogUI(web_ui) {
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
Profile::FromWebUI(web_ui), chrome::kChromeUISmbCredentialsHost);
- webui::EnableTrustedTypesCSP(source);
+ ash::EnableTrustedTypesCSP(source);
AddSmbCredentialsDialogStrings(source);
diff --git a/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.cc b/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.cc
index 77b1f12d4f7..7c54b884d8d 100644
--- a/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.cc
@@ -54,6 +54,23 @@ void SmbHandler::RegisterMessages() {
"updateCredentials",
base::BindRepeating(&SmbHandler::HandleUpdateCredentials,
base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "hasAnySmbMountedBefore",
+ base::BindRepeating(&SmbHandler::HandleHasAnySmbMountedBefore,
+ base::Unretained(this)));
+}
+
+void SmbHandler::SetSmbServiceForTesting(smb_client::SmbService* smb_service) {
+ CHECK(smb_service);
+ test_smb_service_ = smb_service;
+}
+
+smb_client::SmbService* SmbHandler::GetLocalSmbService() {
+ if (test_smb_service_) {
+ return test_smb_service_;
+ }
+ return GetSmbService(profile_);
}
void SmbHandler::HandleSmbMount(const base::Value::List& args) {
@@ -118,6 +135,24 @@ void SmbHandler::HandleDiscoveryDone() {
}
}
+void SmbHandler::HandleHasAnySmbMountedBefore(const base::Value::List& args) {
+ CHECK_EQ(1U, args.size());
+ std::string callback_id = args[0].GetString();
+ smb_client::SmbService* const service = GetLocalSmbService();
+
+ AllowJavascript();
+
+ if (!service) {
+ // Return the default value false so no changes would take place on the
+ // Settings page.
+ ResolveJavascriptCallback(base::Value(callback_id), base::Value(false));
+ return;
+ }
+
+ ResolveJavascriptCallback(base::Value(callback_id),
+ base::Value(service->IsAnySmbShareConfigured()));
+}
+
void SmbHandler::HandleGatherSharesResponse(
const std::vector<smb_client::SmbUrl>& shares_gathered,
bool done) {
diff --git a/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.h b/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.h
index 2c323b412fd..c10eddea129 100644
--- a/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.h
+++ b/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_handler.h
@@ -30,10 +30,17 @@ class SmbHandler : public content::WebUIMessageHandler {
~SmbHandler() override;
- private:
// content::WebUIMessageHandler
void RegisterMessages() override;
+ void SetSmbServiceForTesting(smb_client::SmbService* smb_service);
+
+ protected:
+ void HandleHasAnySmbMountedBefore(const base::Value::List& args);
+
+ private:
+ friend class TestSmbHandler;
+
// WebUI call to mount an Smb Filesystem.
void HandleSmbMount(const base::Value::List& args);
@@ -55,10 +62,13 @@ class SmbHandler : public content::WebUIMessageHandler {
// Callback handler that indicates discovery is complete.
void HandleDiscoveryDone();
+ smb_client::SmbService* GetLocalSmbService();
+
bool host_discovery_done_ = false;
base::OnceClosure stored_mount_call_;
const raw_ptr<Profile, ExperimentalAsh> profile_;
UpdateCredentialsCallback update_cred_callback_;
+ raw_ptr<smb_client::SmbService> test_smb_service_ = nullptr;
base::WeakPtrFactory<SmbHandler> weak_ptr_factory_{this};
};
diff --git a/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_share_dialog.cc b/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_share_dialog.cc
index fa2da610657..f9c1c53d496 100644
--- a/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_share_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/ash/smb_shares/smb_share_dialog.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/ui/webui/ash/smb_shares/smb_share_dialog.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "base/functional/callback_helpers.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/ash/smb_client/smb_service.h"
@@ -71,7 +72,7 @@ SmbShareDialogUI::SmbShareDialogUI(content::WebUI* web_ui)
: ui::WebDialogUI(web_ui) {
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
Profile::FromWebUI(web_ui), chrome::kChromeUISmbShareHost);
- webui::EnableTrustedTypesCSP(source);
+ ash::EnableTrustedTypesCSP(source);
AddSmbSharesStrings(source);
@@ -88,17 +89,29 @@ SmbShareDialogUI::SmbShareDialogUI(content::WebUI* web_ui)
smb_service && smb_service->IsKerberosEnabledViaPolicy();
source->AddBoolean("isKerberosEnabled", is_kerberos_enabled);
- bool is_guest = user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
- user_manager::UserManager::Get()->IsLoggedInAsPublicAccount();
+ bool is_guest =
+ user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
+ user_manager::UserManager::Get()->IsLoggedInAsManagedGuestSession();
source->AddBoolean("isGuest", is_guest);
bool is_jelly_enabled = chromeos::features::IsJellyEnabled();
source->AddBoolean("isJellyEnabled", is_jelly_enabled);
+ source->AddBoolean("isCrosComponentsEnabled",
+ chromeos::features::IsCrosComponentsEnabled());
source->UseStringsJs();
source->SetDefaultResource(IDR_SMB_SHARES_DIALOG_CONTAINER_HTML);
source->AddResourcePath("smb_share_dialog.js", IDR_SMB_SHARES_DIALOG_JS);
+ source->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::TrustedTypes,
+ "trusted-types parse-html-subset sanitize-inner-html static-types "
+ "ash-deprecated-parse-html-subset "
+ // Required by lit-html.
+ "lit-html "
+ // Required by polymer.
+ "polymer-html-literal polymer-template-event-attribute-policy;");
+
web_ui->AddMessageHandler(std::make_unique<SmbHandler>(
Profile::FromWebUI(web_ui), base::DoNothing()));
}
diff --git a/chromium/chrome/browser/ui/webui/ash/status_area_internals/DEPS b/chromium/chrome/browser/ui/webui/ash/status_area_internals/DEPS
new file mode 100644
index 00000000000..15cd7361a55
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/status_area_internals/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ash"
+]
diff --git a/chromium/chrome/browser/ui/webui/ash/status_area_internals/OWNERS b/chromium/chrome/browser/ui/webui/ash/status_area_internals/OWNERS
new file mode 100644
index 00000000000..8e0965d6ff1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/status_area_internals/OWNERS
@@ -0,0 +1,2 @@
+leandre@chromium.org
+etuck@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.cc b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.cc
new file mode 100644
index 00000000000..d8adfc1ebbb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.cc
@@ -0,0 +1,101 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.h"
+
+#include "ash/ime/ime_controller_impl.h"
+#include "ash/public/cpp/stylus_utils.h"
+#include "ash/root_window_controller.h"
+#include "ash/shell.h"
+#include "ash/system/palette/palette_tray.h"
+#include "ash/system/privacy/privacy_indicators_controller.h"
+#include "ash/system/status_area_widget.h"
+#include "base/functional/bind.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui.h"
+
+namespace ash {
+
+// static
+const char StatusAreaInternalsHandler::kToggleIme[] = "toggleIme";
+const char StatusAreaInternalsHandler::kTogglePalette[] = "togglePalette";
+const char StatusAreaInternalsHandler::kTriggerPrivacyIndicators[] =
+ "triggerPrivacyIndicators";
+
+StatusAreaInternalsHandler::StatusAreaInternalsHandler() = default;
+
+StatusAreaInternalsHandler::~StatusAreaInternalsHandler() = default;
+
+void StatusAreaInternalsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ kToggleIme,
+ base::BindRepeating(&StatusAreaInternalsHandler::ToggleImeTray,
+ weak_pointer_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ kTogglePalette,
+ base::BindRepeating(&StatusAreaInternalsHandler::TogglePaletteTray,
+ weak_pointer_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ kTriggerPrivacyIndicators,
+ base::BindRepeating(&StatusAreaInternalsHandler::TriggerPrivacyIndicators,
+ weak_pointer_factory_.GetWeakPtr()));
+}
+
+void StatusAreaInternalsHandler::SetWebUiForTesting(content::WebUI* web_ui) {
+ DCHECK(web_ui);
+ set_web_ui(web_ui);
+}
+
+void StatusAreaInternalsHandler::ToggleImeTray(const base::Value::List& args) {
+ AllowJavascript();
+
+ // Parse JS args.
+ bool toggled = args[0].GetBool();
+ Shell::Get()->ime_controller()->ShowImeMenuOnShelf(/*show=*/toggled);
+}
+
+void StatusAreaInternalsHandler::TogglePaletteTray(
+ const base::Value::List& args) {
+ AllowJavascript();
+
+ // Parse JS args.
+ bool toggled = args[0].GetBool();
+
+ if (toggled) {
+ stylus_utils::SetHasStylusInputForTesting();
+ } else {
+ stylus_utils::SetNoStylusInputForTesting();
+ }
+
+ for (auto* root_window_controller :
+ Shell::Get()->GetAllRootWindowControllers()) {
+ DCHECK(root_window_controller);
+ DCHECK(root_window_controller->GetStatusAreaWidget());
+
+ root_window_controller->GetStatusAreaWidget()
+ ->palette_tray()
+ ->SetDisplayHasStylusForTesting();
+ }
+}
+
+void StatusAreaInternalsHandler::TriggerPrivacyIndicators(
+ const base::Value::List& args) {
+ AllowJavascript();
+
+ // Parse JS args.
+ auto app_id = args[0].GetString();
+ auto app_name = args[1].GetString();
+ auto is_camera_used = args[2].GetBool();
+ auto is_microphone_used = args[3].GetBool();
+
+ PrivacyIndicatorsController::Get()->UpdatePrivacyIndicators(
+ app_id, base::UTF8ToUTF16(app_name), is_camera_used, is_microphone_used,
+ base::MakeRefCounted<PrivacyIndicatorsNotificationDelegate>(),
+ PrivacyIndicatorsSource::kApps);
+}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.h b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.h
new file mode 100644
index 00000000000..7d49d8710a6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.h
@@ -0,0 +1,48 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_STATUS_AREA_INTERNALS_STATUS_AREA_INTERNALS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_STATUS_AREA_INTERNALS_STATUS_AREA_INTERNALS_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace content {
+class WebUI;
+} // namespace content
+
+namespace ash {
+
+// WebUI message handler for chrome://status-area-internals from the Chrome page
+// to the System UI.
+class StatusAreaInternalsHandler : public content::WebUIMessageHandler {
+ public:
+ StatusAreaInternalsHandler();
+ StatusAreaInternalsHandler(const StatusAreaInternalsHandler&) = delete;
+ StatusAreaInternalsHandler& operator=(const StatusAreaInternalsHandler&) =
+ delete;
+ ~StatusAreaInternalsHandler() override;
+
+ // Handler names
+ static const char kToggleIme[];
+ static const char kTogglePalette[];
+ static const char kTriggerPrivacyIndicators[];
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ void SetWebUiForTesting(content::WebUI* web_ui);
+
+ private:
+ // Callbacks for events coming from the web UI.
+ void ToggleImeTray(const base::Value::List& args);
+ void TogglePaletteTray(const base::Value::List& args);
+ void TriggerPrivacyIndicators(const base::Value::List& args);
+
+ base::WeakPtrFactory<StatusAreaInternalsHandler> weak_pointer_factory_{this};
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_STATUS_AREA_INTERNALS_STATUS_AREA_INTERNALS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler_unittest.cc b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler_unittest.cc
new file mode 100644
index 00000000000..ce76f27567d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler_unittest.cc
@@ -0,0 +1,129 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.h"
+
+#include <memory>
+
+#include "ash/constants/ash_pref_names.h"
+#include "ash/root_window_controller.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/system/ime_menu/ime_menu_tray.h"
+#include "ash/system/notification_center/notification_center_tray.h"
+#include "ash/system/palette/palette_tray.h"
+#include "ash/system/privacy/privacy_indicators_tray_item_view.h"
+#include "ash/system/status_area_widget.h"
+#include "ash/test/ash_test_base.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+
+class StatusAreaInternalsHandlerTest : public AshTestBase {
+ public:
+ StatusAreaInternalsHandlerTest()
+ : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+ StatusAreaInternalsHandlerTest(const StatusAreaInternalsHandlerTest&) =
+ delete;
+ StatusAreaInternalsHandlerTest& operator=(
+ const StatusAreaInternalsHandlerTest&) = delete;
+ ~StatusAreaInternalsHandlerTest() override = default;
+
+ void SetUp() override {
+ handler_ = std::make_unique<StatusAreaInternalsHandler>();
+ handler_->SetWebUiForTesting(&web_ui_);
+ handler_->RegisterMessages();
+
+ AshTestBase::SetUp();
+ }
+
+ void SendMessage(const std::string& handler_name,
+ const base::Value::List& args) {
+ web_ui_.HandleReceivedMessage(handler_name, args);
+ task_environment()->RunUntilIdle();
+ }
+
+ StatusAreaWidget* GetStatusAreaWidget() {
+ return ash::Shell::Get()
+ ->GetPrimaryRootWindowController()
+ ->GetStatusAreaWidget();
+ }
+
+ private:
+ content::TestWebUI web_ui_;
+
+ std::unique_ptr<StatusAreaInternalsHandler> handler_;
+};
+
+// Sending `kToggleIme` message from the web UI should update the visibility of
+// IME tray accordingly.
+TEST_F(StatusAreaInternalsHandlerTest, ToggleImeTray) {
+ auto* ime_tray = GetStatusAreaWidget()->ime_menu_tray();
+ EXPECT_FALSE(ime_tray->GetVisible());
+
+ base::Value::List args;
+ args.Append(true);
+ SendMessage(StatusAreaInternalsHandler::kToggleIme, args);
+
+ EXPECT_TRUE(ime_tray->GetVisible());
+
+ args.clear();
+ args.Append(false);
+ SendMessage(StatusAreaInternalsHandler::kToggleIme, args);
+
+ EXPECT_FALSE(ime_tray->GetVisible());
+}
+
+// Sending `kTogglePalette` message from the web UI should update the visibility
+// of palette tray accordingly.
+TEST_F(StatusAreaInternalsHandlerTest, TogglePaletteTray) {
+ Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
+ prefs::kEnableStylusTools, true);
+
+ auto* palette_tray = GetStatusAreaWidget()->palette_tray();
+ EXPECT_FALSE(palette_tray->GetVisible());
+
+ base::Value::List args;
+ args.Append(true);
+ SendMessage(StatusAreaInternalsHandler::kTogglePalette, args);
+
+ EXPECT_TRUE(palette_tray->GetVisible());
+
+ args.clear();
+ args.Append(false);
+ SendMessage(StatusAreaInternalsHandler::kTogglePalette, args);
+
+ EXPECT_FALSE(palette_tray->GetVisible());
+}
+
+// Sending `kTriggerPrivacyIndicators` message from the web UI should update the
+// visibility of the privacy indicators accordingly.
+TEST_F(StatusAreaInternalsHandlerTest, TriggerPrivacyIndicators) {
+ auto* privacy_indicators_view = GetStatusAreaWidget()
+ ->notification_center_tray()
+ ->privacy_indicators_view();
+ ASSERT_FALSE(privacy_indicators_view->GetVisible());
+
+ base::Value::List args;
+ args.Append("app_id");
+ args.Append("app_name");
+ args.Append(true);
+ args.Append(true);
+ SendMessage(StatusAreaInternalsHandler::kTriggerPrivacyIndicators, args);
+
+ EXPECT_TRUE(privacy_indicators_view->GetVisible());
+
+ args.clear();
+ args.Append("app_id");
+ args.Append("app_name");
+ args.Append(false);
+ args.Append(false);
+ SendMessage(StatusAreaInternalsHandler::kTriggerPrivacyIndicators, args);
+
+ EXPECT_FALSE(privacy_indicators_view->GetVisible());
+}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.cc b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.cc
new file mode 100644
index 00000000000..71024143e25
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.cc
@@ -0,0 +1,46 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.h"
+
+#include <memory>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_handler.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/status_area_internals_resources.h"
+#include "chrome/grit/status_area_internals_resources_map.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace ash {
+
+StatusAreaInternalsUI::StatusAreaInternalsUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // Set up the chrome://status-area-internals source.
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::CreateAndAdd(
+ Profile::FromWebUI(web_ui), chrome::kChromeUIStatusAreaInternalsHost);
+
+ // Add required resources.
+ webui::SetupWebUIDataSource(
+ html_source,
+ base::make_span(kStatusAreaInternalsResources,
+ kStatusAreaInternalsResourcesSize),
+ IDR_STATUS_AREA_INTERNALS_MAIN_HTML);
+
+ web_ui->AddMessageHandler(std::make_unique<StatusAreaInternalsHandler>());
+}
+
+StatusAreaInternalsUI::~StatusAreaInternalsUI() = default;
+
+StatusAreaInternalsUIConfig::StatusAreaInternalsUIConfig()
+ : DefaultWebUIConfig(content::kChromeUIScheme,
+ chrome::kChromeUIStatusAreaInternalsHost) {}
+
+} // namespace ash
diff --git a/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.h b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.h
new file mode 100644
index 00000000000..a8204c55e99
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ash/status_area_internals/status_area_internals_ui.h
@@ -0,0 +1,31 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_STATUS_AREA_INTERNALS_STATUS_AREA_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_STATUS_AREA_INTERNALS_STATUS_AREA_INTERNALS_UI_H_
+
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/webui_config.h"
+
+namespace ash {
+
+// The UI controller for ChromeOS Status Area Internals page.
+class StatusAreaInternalsUI : public content::WebUIController {
+ public:
+ explicit StatusAreaInternalsUI(content::WebUI* web_ui);
+ StatusAreaInternalsUI(const StatusAreaInternalsUI&) = delete;
+ StatusAreaInternalsUI& operator=(const StatusAreaInternalsUI&) = delete;
+ ~StatusAreaInternalsUI() override;
+};
+
+// UI config for the class above.
+class StatusAreaInternalsUIConfig
+ : public content::DefaultWebUIConfig<StatusAreaInternalsUI> {
+ public:
+ StatusAreaInternalsUIConfig();
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_UI_WEBUI_ASH_STATUS_AREA_INTERNALS_STATUS_AREA_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/ash/sync/os_sync_handler_unittest.cc b/chromium/chrome/browser/ui/webui/ash/sync/os_sync_handler_unittest.cc
index 199a43fd864..91adcf3ca26 100644
--- a/chromium/chrome/browser/ui/webui/ash/sync/os_sync_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/sync/os_sync_handler_unittest.cc
@@ -184,15 +184,18 @@ class OsSyncHandlerTest : public ChromeRenderViewHostTestHarness {
enabled);
}
- raw_ptr<syncer::TestSyncService, ExperimentalAsh> sync_service_ = nullptr;
- raw_ptr<syncer::SyncUserSettings, ExperimentalAsh> user_settings_ = nullptr;
+ raw_ptr<syncer::TestSyncService, DanglingUntriaged | ExperimentalAsh>
+ sync_service_ = nullptr;
+ raw_ptr<syncer::SyncUserSettings, DanglingUntriaged | ExperimentalAsh>
+ user_settings_ = nullptr;
std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
identity_test_env_adaptor_;
std::unique_ptr<TestWebUI> web_ui_;
TestWebUIProvider test_web_ui_provider_;
std::unique_ptr<TestChromeWebUIControllerFactory> test_web_ui_factory_;
- raw_ptr<OSSyncHandler, ExperimentalAsh> handler_;
- raw_ptr<MockNewWindowDelegate, ExperimentalAsh> new_window_delegate_primary_;
+ raw_ptr<OSSyncHandler, DanglingUntriaged | ExperimentalAsh> handler_;
+ raw_ptr<MockNewWindowDelegate, DanglingUntriaged | ExperimentalAsh>
+ new_window_delegate_primary_;
std::unique_ptr<TestNewWindowDelegateProvider> new_window_provider_;
};
diff --git a/chromium/chrome/browser/ui/webui/ash/sys_internals/sys_internals_ui.cc b/chromium/chrome/browser/ui/webui/ash/sys_internals/sys_internals_ui.cc
index ea68f3c29b4..7c5754ee157 100644
--- a/chromium/chrome/browser/ui/webui/ash/sys_internals/sys_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ash/sys_internals/sys_internals_ui.cc
@@ -7,6 +7,7 @@
#include <memory>
#include "base/feature_list.h"
+#include "base/metrics/user_metrics.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/sys_internals/sys_internals_message_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
@@ -70,6 +71,7 @@ SysInternalsUI::SysInternalsUI(content::WebUI* web_ui)
html_source->AddResourcePath("test_loader_util.js",
IDR_WEBUI_JS_TEST_LOADER_UTIL_JS);
+ base::RecordAction(base::UserMetricsAction("Open_Sys_Internals"));
}
SysInternalsUI::~SysInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/ash/system_web_dialog_browsertest.cc b/chromium/chrome/browser/ui/webui/ash/system_web_dialog_browsertest.cc
index 572c13c1ac8..a4d23d7ae5b 100644
--- a/chromium/chrome/browser/ui/webui/ash/system_web_dialog_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ash/system_web_dialog_browsertest.cc
@@ -19,6 +19,7 @@
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_ui.h"
#include "content/public/test/browser_test.h"
+#include "content/public/test/test_navigation_observer.h"
#include "third_party/blink/public/common/page/page_zoom.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "ui/aura/client/aura_constants.h"
@@ -112,9 +113,16 @@ IN_PROC_BROWSER_TEST_F(SystemWebDialogTest, FontSize) {
profile_prefs->SetInteger(prefs::kWebKitDefaultFixedFontSize,
kDefaultFixedFontSize + 1);
- // Open a system dialog.
+ // Open a system dialog and ensure it has successfully committed.
+ const GURL expected_url = GURL(chrome::kChromeUIInternetConfigDialogURL);
+ content::TestNavigationObserver navigation_observer(expected_url);
+ navigation_observer.StartWatchingNewWebContents();
MockSystemWebDialog* dialog = new MockSystemWebDialog();
dialog->ShowSystemDialog();
+ navigation_observer.Wait();
+
+ // Ensure web preferences are updated.
+ dialog->GetWebUIForTest()->GetWebContents()->NotifyPreferencesChanged();
// Dialog font sizes are still the default values.
blink::web_pref::WebPreferences dialog_prefs =
diff --git a/chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.cc b/chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.cc
index 78464f4af5e..c737c642dbf 100644
--- a/chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.cc
+++ b/chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.cc
@@ -200,7 +200,10 @@ void SystemWebDialogDelegate::OnDialogShown(content::WebUI* webui) {
// System dialogs don't use the browser's default page zoom. Their contents
// stay at 100% to match the size of app list, shelf, status area, etc.
auto* web_contents = webui_->GetWebContents();
- auto* rfh = web_contents->GetPrimaryMainFrame();
+ // This is safe, because OnDialogShown() is called from
+ // WebUIRenderFrameCreated(), and by then `webui` is already associated with a
+ // RenderFrameHost.
+ auto* rfh = webui->GetRenderFrameHost();
auto* zoom_map = content::HostZoomMap::GetForWebContents(web_contents);
// Temporary means the lifetime of the WebContents.
zoom_map->SetTemporaryZoomLevel(rfh->GetGlobalId(),
diff --git a/chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.h b/chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.h
index 7af53ea9ae0..618ac890753 100644
--- a/chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.h
+++ b/chromium/chrome/browser/ui/webui/ash/system_web_dialog_delegate.h
@@ -123,7 +123,7 @@ class SystemWebDialogDelegate : public ui::WebDialogDelegate {
private:
GURL gurl_;
std::u16string title_;
- raw_ptr<content::WebUI, ExperimentalAsh> webui_ = nullptr;
+ raw_ptr<content::WebUI, DanglingUntriaged | ExperimentalAsh> webui_ = nullptr;
ui::ModalType modal_type_;
gfx::NativeWindow dialog_window_ = gfx::NativeWindow();
};
diff --git a/chromium/chrome/browser/ui/webui/autofill_and_password_manager_internals/password_manager_internals_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/autofill_and_password_manager_internals/password_manager_internals_ui_browsertest.cc
index e7a21ed6cff..48819dcc701 100644
--- a/chromium/chrome/browser/ui/webui/autofill_and_password_manager_internals/password_manager_internals_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/autofill_and_password_manager_internals/password_manager_internals_ui_browsertest.cc
@@ -33,7 +33,8 @@ class PasswordManagerInternalsWebUIBrowserTest : public WebUIBrowserTest {
WindowOpenDisposition disposition);
private:
- raw_ptr<PasswordManagerInternalsUI, DanglingUntriaged> controller_ = nullptr;
+ raw_ptr<PasswordManagerInternalsUI, AcrossTasksDanglingUntriaged>
+ controller_ = nullptr;
};
PasswordManagerInternalsWebUIBrowserTest::
diff --git a/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc b/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
index b84767c084c..e901257c305 100644
--- a/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
@@ -14,7 +14,6 @@
#include "chrome/grit/bluetooth_internals_resources_map.h"
#include "content/public/browser/web_ui_data_source.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
-#include "ui/resources/grit/webui_resources.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/bluetooth/debug_logs_manager_factory.h"
@@ -30,8 +29,6 @@ BluetoothInternalsUI::BluetoothInternalsUI(content::WebUI* web_ui)
network::mojom::CSPDirectiveName::ScriptSrc,
"script-src chrome://resources chrome://webui-test 'self';");
webui::EnableTrustedTypesCSP(html_source);
- html_source->AddResourcePath("test_loader_util.js",
- IDR_WEBUI_JS_TEST_LOADER_UTIL_JS);
// Add required resources.
html_source->AddResourcePaths(base::make_span(
diff --git a/chromium/chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.cc b/chromium/chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.cc
deleted file mode 100644
index d0ba098ebad..00000000000
--- a/chromium/chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/bookmarks/bookmark_model_factory.h"
-#include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/common/pref_names.h"
-#include "components/bookmarks/browser/bookmark_model.h"
-#include "components/bookmarks/common/bookmark_pref_names.h"
-#include "components/bookmarks/managed/managed_bookmark_service.h"
-#include "components/bookmarks/test/bookmark_test_helpers.h"
-#include "components/prefs/pref_service.h"
-#include "components/user_prefs/user_prefs.h"
-
-BookmarksBrowserTest::BookmarksBrowserTest() {}
-
-BookmarksBrowserTest::~BookmarksBrowserTest() {}
-
-void BookmarksBrowserTest::SetupExtensionAPITest() {
- // Add managed bookmarks.
- Profile* profile = browser()->profile();
- bookmarks::BookmarkModel* model =
- BookmarkModelFactory::GetForBrowserContext(profile);
- bookmarks::ManagedBookmarkService* managed =
- ManagedBookmarkServiceFactory::GetForProfile(profile);
- bookmarks::test::WaitForBookmarkModelToLoad(model);
-
- base::Value::List list;
- base::Value::Dict node;
- node.Set("name", "Managed Bookmark");
- node.Set("url", "http://www.chromium.org");
- list.Append(node.Clone());
- node.clear();
- node.Set("name", "Managed Folder");
- node.Set("children", base::Value::List());
- list.Append(std::move(node));
- profile->GetPrefs()->Set(bookmarks::prefs::kManagedBookmarks,
- base::Value(std::move(list)));
- ASSERT_EQ(2u, managed->managed_node()->children().size());
-}
-
-void BookmarksBrowserTest::SetupExtensionAPIEditDisabledTest() {
- Profile* profile = browser()->profile();
-
- // Provide some testing data here, since bookmark editing will be disabled
- // within the extension.
- bookmarks::BookmarkModel* model =
- BookmarkModelFactory::GetForBrowserContext(profile);
- bookmarks::test::WaitForBookmarkModelToLoad(model);
- const bookmarks::BookmarkNode* bar = model->bookmark_bar_node();
- const bookmarks::BookmarkNode* folder = model->AddFolder(bar, 0, u"Folder");
- model->AddURL(bar, 1, u"AAA", GURL("http://aaa.example.com"));
- model->AddURL(folder, 0, u"BBB", GURL("http://bbb.example.com"));
-
- PrefService* prefs = user_prefs::UserPrefs::Get(profile);
- prefs->SetBoolean(bookmarks::prefs::kEditBookmarksEnabled, false);
-}
diff --git a/chromium/chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.h b/chromium/chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.h
deleted file mode 100644
index 7f674a336c0..00000000000
--- a/chromium/chrome/browser/ui/webui/bookmarks/bookmarks_browsertest.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_BOOKMARKS_BOOKMARKS_BROWSERTEST_H_
-#define CHROME_BROWSER_UI_WEBUI_BOOKMARKS_BOOKMARKS_BROWSERTEST_H_
-
-#include "chrome/test/base/web_ui_browser_test.h"
-
-class BookmarksBrowserTest : public WebUIBrowserTest {
- public:
- BookmarksBrowserTest();
-
- BookmarksBrowserTest(const BookmarksBrowserTest&) = delete;
- BookmarksBrowserTest& operator=(const BookmarksBrowserTest&) = delete;
-
- ~BookmarksBrowserTest() override;
-
- void SetupExtensionAPITest();
- void SetupExtensionAPIEditDisabledTest();
-};
-
-#endif // CHROME_BROWSER_UI_WEBUI_BOOKMARKS_BOOKMARKS_BROWSERTEST_H_
diff --git a/chromium/chrome/browser/ui/webui/browser_command/DEPS b/chromium/chrome/browser/ui/webui/browser_command/DEPS
deleted file mode 100644
index 40885822102..00000000000
--- a/chromium/chrome/browser/ui/webui/browser_command/DEPS
+++ /dev/null
@@ -1,4 +0,0 @@
-include_rules = [
- # Browser command relies on the tutorial ID defined here.
- "+chrome/browser/ui/views/user_education/browser_user_education_service.h",
-]
diff --git a/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.cc b/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.cc
index 26fdbf7b6a3..959beafda70 100644
--- a/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.cc
+++ b/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.cc
@@ -17,9 +17,8 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
-#include "chrome/browser/ui/user_education/user_education_service.h"
-#include "chrome/browser/ui/user_education/user_education_service_factory.h"
-#include "chrome/browser/ui/views/user_education/browser_user_education_service.h"
+#include "chrome/browser/user_education/user_education_service.h"
+#include "chrome/browser/user_education/user_education_service_factory.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/webui_url_constants.h"
#include "components/password_manager/core/common/password_manager_features.h"
@@ -27,11 +26,12 @@
#include "components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h"
#include "components/safe_browsing/core/common/safe_browsing_policy_handler.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
-#include "components/search/ntp_features.h"
+#include "components/safe_browsing/core/common/safebrowsing_referral_methods.h"
#include "components/user_education/common/tutorial_identifier.h"
#include "components/user_education/common/tutorial_service.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/page_transition_types.h"
+#include "ui/base/ui_base_features.h"
#include "ui/base/window_open_disposition.h"
#include "ui/base/window_open_disposition_utils.h"
@@ -109,8 +109,7 @@ void BrowserCommandHandler::CanExecuteCommand(
DefaultSearchProviderIsGoogle();
break;
case Command::kStartPasswordManagerTutorial:
- can_execute =
- !!GetTutorialService() && BrowserSupportsNewPasswordManager();
+ can_execute = !!GetTutorialService();
break;
}
std::move(callback).Run(can_execute);
@@ -150,9 +149,7 @@ void BrowserCommandHandler::ExecuteCommandWithDisposition(
base::UserMetricsAction("NewTabPage_Promos_SafetyCheck"));
break;
case Command::kOpenSafeBrowsingEnhancedProtectionSettings:
- NavigateToURL(GURL(chrome::GetSettingsUrl(
- chrome::kSafeBrowsingEnhancedProtectionSubPage)),
- disposition);
+ NavigateToEnhancedProtectionSetting();
base::RecordAction(
base::UserMetricsAction("NewTabPage_Promos_EnhancedProtection"));
break;
@@ -191,7 +188,7 @@ void BrowserCommandHandler::ExecuteCommandWithDisposition(
}
user_education::TutorialService* BrowserCommandHandler::GetTutorialService() {
- auto* service = UserEducationServiceFactory::GetForProfile(profile_);
+ auto* service = UserEducationServiceFactory::GetForBrowserContext(profile_);
return service ? &service->tutorial_service() : nullptr;
}
@@ -206,11 +203,6 @@ bool BrowserCommandHandler::BrowserSupportsTabGroups() {
return browser->tab_strip_model()->SupportsTabGroups();
}
-bool BrowserCommandHandler::BrowserHasTabGroups() {
- Browser* browser = chrome::FindBrowserWithProfile(profile_);
- return !browser->tab_strip_model()->group_model()->ListTabGroups().empty();
-}
-
void BrowserCommandHandler::StartTabGroupTutorial() {
user_education::TutorialService* tutorial_service = GetTutorialService();
@@ -227,21 +219,25 @@ void BrowserCommandHandler::StartTabGroupTutorial() {
return;
}
- user_education::TutorialIdentifier tutorial_id =
- BrowserHasTabGroups() ? kTabGroupWithExistingGroupTutorialId
- : kTabGroupTutorialId;
+ user_education::TutorialIdentifier tutorial_id = kTabGroupTutorialId;
tutorial_service->StartTutorial(tutorial_id, context);
tutorial_service->LogStartedFromWhatsNewPage(
tutorial_id, tutorial_service->IsRunningTutorial());
}
+void BrowserCommandHandler::NavigateToEnhancedProtectionSetting() {
+ chrome::ShowSafeBrowsingEnhancedProtectionWithIph(
+ chrome::FindBrowserWithProfile(profile_),
+ safe_browsing::SafeBrowsingSettingReferralMethod::kPromoSlingerReferral);
+}
+
void BrowserCommandHandler::OpenPasswordManager() {
chrome::ShowPasswordManager(chrome::FindBrowserWithProfile(profile_));
}
bool BrowserCommandHandler::BrowserSupportsCustomizeChromeSidePanel() {
- return base::FeatureList::IsEnabled(ntp_features::kCustomizeChromeSidePanel);
+ return base::FeatureList::IsEnabled(features::kCustomizeChromeSidePanel);
}
bool BrowserCommandHandler::DefaultSearchProviderIsGoogle() {
@@ -281,11 +277,6 @@ void BrowserCommandHandler::OpenNTPAndStartCustomizeChromeTutorial(
NavigateToURL(GURL(chrome::kChromeUINewTabPageURL), disposition);
}
-bool BrowserCommandHandler::BrowserSupportsNewPasswordManager() {
- return base::FeatureList::IsEnabled(
- password_manager::features::kPasswordManagerRedesign);
-}
-
void BrowserCommandHandler::StartPasswordManagerTutorial() {
user_education::TutorialService* tutorial_service = GetTutorialService();
@@ -300,10 +291,6 @@ void BrowserCommandHandler::StartPasswordManagerTutorial() {
return;
}
- if (!BrowserSupportsNewPasswordManager()) {
- return;
- }
-
user_education::TutorialIdentifier tutorial_id = kPasswordManagerTutorialId;
tutorial_service->StartTutorial(tutorial_id, context);
diff --git a/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.h b/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.h
index f218cd4faa9..74ccd742568 100644
--- a/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.h
+++ b/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler.h
@@ -75,15 +75,13 @@ class BrowserCommandHandler : public CommandUpdaterDelegate,
virtual bool BrowserSupportsCustomizeChromeSidePanel();
virtual bool DefaultSearchProviderIsGoogle();
- virtual bool BrowserHasTabGroups();
- virtual bool BrowserSupportsNewPasswordManager();
-
private:
virtual void NavigateToURL(const GURL& url,
WindowOpenDisposition disposition);
virtual void OpenFeedbackForm();
virtual user_education::TutorialService* GetTutorialService();
virtual ui::ElementContext GetUiElementContext();
+ virtual void NavigateToEnhancedProtectionSetting();
virtual void OpenPasswordManager();
void StartTabGroupTutorial();
void OpenNTPAndStartCustomizeChromeTutorial(
diff --git a/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler_unittest.cc b/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler_unittest.cc
index bf415c9ab4d..dfa3af75264 100644
--- a/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/browser_command/browser_command_handler_unittest.cc
@@ -8,18 +8,12 @@
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/browser_features.h"
#include "chrome/browser/command_updater_impl.h"
#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/views/user_education/browser_user_education_service.h"
#include "chrome/browser/ui/webui/browser_command/browser_command_handler.h"
-#include "chrome/common/chrome_features.h"
+#include "chrome/browser/user_education/user_education_service.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/testing_profile.h"
-#include "components/content_settings/core/common/pref_names.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/performance_manager/public/features.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_education/common/help_bubble_factory_registry.h"
@@ -29,7 +23,6 @@
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/interaction/element_tracker.h"
#include "ui/base/window_open_disposition.h"
#include "ui/base/window_open_disposition_utils.h"
#include "ui/webui/resources/js/browser_command/browser_command.mojom.h"
@@ -65,6 +58,11 @@ class TestCommandHandler : public BrowserCommandHandler {
supported_commands) {}
~TestCommandHandler() override = default;
+ void NavigateToEnhancedProtectionSetting() override {
+ // The functionality of opening a URL is removed, as it cannot be executed
+ // in a unittest.
+ }
+
void NavigateToURL(const GURL&, WindowOpenDisposition) override {
// The functionality of opening a URL is removed, as it cannot be executed
// in a unittest.
@@ -107,18 +105,10 @@ class TestCommandHandler : public BrowserCommandHandler {
tab_groups_feature_supported_ = is_supported;
}
- void SetBrowserHasTabGroups(bool has_tab_groups) {
- has_tab_groups_ = has_tab_groups;
- }
-
void SetBrowserSupportsCustomizeChromeSidePanel(bool is_supported) {
customize_chrome_side_panel_feature_supported_ = is_supported;
}
- void SetBrowserSupportsNewPasswordManager(bool is_supported) {
- new_password_manager_feature_supported_ = is_supported;
- }
-
void SetDefaultSearchProviderToGoogle(bool is_google) {
default_search_provider_is_google_ = is_google;
}
@@ -128,16 +118,10 @@ class TestCommandHandler : public BrowserCommandHandler {
return tab_groups_feature_supported_;
}
- bool BrowserHasTabGroups() override { return has_tab_groups_; }
-
bool BrowserSupportsCustomizeChromeSidePanel() override {
return customize_chrome_side_panel_feature_supported_;
}
- bool BrowserSupportsNewPasswordManager() override {
- return new_password_manager_feature_supported_;
- }
-
bool DefaultSearchProviderIsGoogle() override {
return default_search_provider_is_google_;
}
@@ -147,9 +131,7 @@ class TestCommandHandler : public BrowserCommandHandler {
std::unique_ptr<CommandUpdater> command_updater_;
bool tab_groups_feature_supported_ = true;
- bool has_tab_groups_ = false;
bool customize_chrome_side_panel_feature_supported_ = true;
- bool new_password_manager_feature_supported_ = true;
bool default_search_provider_is_google_ = true;
};
@@ -208,6 +190,8 @@ class MockCommandHandler : public TestCommandHandler {
explicit MockCommandHandler(Profile* profile) : TestCommandHandler(profile) {}
~MockCommandHandler() override = default;
+ MOCK_METHOD(void, NavigateToEnhancedProtectionSetting, ());
+
MOCK_METHOD(void, NavigateToURL, (const GURL&, WindowOpenDisposition));
MOCK_METHOD(void, OpenFeedbackForm, ());
@@ -433,15 +417,11 @@ TEST_F(
TEST_F(BrowserCommandHandlerTest, OpenSafeBrowsingEnhancedProtectionCommand) {
// The kOpenSafeBrowsingEnhancedProtectionSettings command opens a new
// settings window with the Safe Browsing settings with the Enhanced
- // Protection section expanded, and the correct disposition.
+ // Protection section expanded, and an In-product help bubble
ClickInfoPtr info = ClickInfo::New();
info->middle_button = true;
info->meta_key = true;
- EXPECT_CALL(
- *command_handler_,
- NavigateToURL(GURL(chrome::GetSettingsUrl(
- chrome::kSafeBrowsingEnhancedProtectionSubPage)),
- DispositionFromClick(*info)));
+ EXPECT_CALL(*command_handler_, NavigateToEnhancedProtectionSetting());
EXPECT_TRUE(ExecuteCommand(
Command::kOpenSafeBrowsingEnhancedProtectionSettings, std::move(info)));
}
@@ -507,10 +487,8 @@ TEST_F(BrowserCommandHandlerTest, StartTabGroupTutorialCommand) {
command_handler_->SetBrowserSupportsTabGroups(true);
EXPECT_TRUE(CanExecuteCommand(Command::kStartTabGroupTutorial));
- // The StartTabGroupTutorial command should start the tab group tutorial. if
- // there are no tab groups in the tabstrip
+ // The StartTabGroupTutorial command should start the tab group tutorial.
{
- command_handler_->SetBrowserHasTabGroups(false);
ClickInfoPtr info = ClickInfo::New();
EXPECT_CALL(service, StartTutorial(kTabGroupTutorialId, kTestContext1,
testing::_, testing::_))
@@ -520,21 +498,6 @@ TEST_F(BrowserCommandHandlerTest, StartTabGroupTutorialCommand) {
EXPECT_TRUE(
ExecuteCommand(Command::kStartTabGroupTutorial, std::move(info)));
}
-
- // The StartTabGroupTutorial command should start the "existing tab groups"
- // tab group tutorial. if there are tab groups in the tabstrip
- {
- command_handler_->SetBrowserHasTabGroups(true);
- ClickInfoPtr info = ClickInfo::New();
- EXPECT_CALL(service, StartTutorial(kTabGroupWithExistingGroupTutorialId,
- kTestContext1, testing::_, testing::_))
- .Times(1);
- EXPECT_CALL(service, IsRunningTutorial).WillOnce(testing::Return(true));
- EXPECT_CALL(service, LogStartedFromWhatsNewPage(
- kTabGroupWithExistingGroupTutorialId, true));
- EXPECT_TRUE(
- ExecuteCommand(Command::kStartTabGroupTutorial, std::move(info)));
- }
}
TEST_F(BrowserCommandHandlerTest, OpenPasswordManagerCommand) {
@@ -627,14 +590,6 @@ TEST_F(BrowserCommandHandlerTest, StartPasswordManagerTutorialCommand) {
MockTutorialService service(&registry, bubble_factory_registry.get());
command_handler_->SetTutorialService(&service);
- // If the browser does not support the new password manager,
- // dont run the command.
- command_handler_->SetBrowserSupportsNewPasswordManager(false);
- EXPECT_FALSE(CanExecuteCommand(Command::kStartPasswordManagerTutorial));
-
- // If the browser supports the new password manager and has a tutorial
- // service it should allow running commands.
- command_handler_->SetBrowserSupportsNewPasswordManager(true);
EXPECT_TRUE(CanExecuteCommand(Command::kStartPasswordManagerTutorial));
ClickInfoPtr info = ClickInfo::New();
diff --git a/chromium/chrome/browser/ui/webui/certificate_viewer_ui.cc b/chromium/chrome/browser/ui/webui/certificate_viewer_ui.cc
index c5e5ab1348b..ed8fad1b17a 100644
--- a/chromium/chrome/browser/ui/webui/certificate_viewer_ui.cc
+++ b/chromium/chrome/browser/ui/webui/certificate_viewer_ui.cc
@@ -41,7 +41,7 @@ void CreateAndAddWebUIDataSource(Profile* profile, const std::string& host) {
{"expiresOn", IDS_CERT_INFO_EXPIRES_ON_LABEL},
{"fingerprints", IDS_CERT_INFO_FINGERPRINTS_GROUP},
{"sha256", IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL},
- {"sha1", IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL},
+ {"spki", IDS_CERT_INFO_SHA256_SPKI_FINGERPRINT_LABEL},
{"hierarchy", IDS_CERT_DETAILS_CERTIFICATE_HIERARCHY_LABEL},
{"certFields", IDS_CERT_DETAILS_CERTIFICATE_FIELDS_LABEL},
{"certFieldVal", IDS_CERT_DETAILS_CERTIFICATE_FIELD_VALUE_LABEL},
diff --git a/chromium/chrome/browser/ui/webui/certificate_viewer_webui.cc b/chromium/chrome/browser/ui/webui/certificate_viewer_webui.cc
index 45098787220..f899a8fecce 100644
--- a/chromium/chrome/browser/ui/webui/certificate_viewer_webui.cc
+++ b/chromium/chrome/browser/ui/webui/certificate_viewer_webui.cc
@@ -304,11 +304,12 @@ std::string CertificateViewerDialog::GetDialogArgs() const {
}
cert_info.SetByDottedPath("general.issue-date", issued_str);
cert_info.SetByDottedPath("general.expiry-date", expires_str);
+ cert_info.SetByDottedPath("general.spki", model.HashSpkiSHA256());
}
- cert_info.SetByDottedPath("general.sha256",
- model.HashCertSHA256WithSeparators());
- cert_info.SetByDottedPath("general.sha1", model.HashCertSHA1WithSeparators());
+ // We always have a cert hash. We don't always have a SPKI hash, if the cert
+ // is not valid.
+ cert_info.SetByDottedPath("general.sha256", model.HashCertSHA256());
// Certificate hierarchy is constructed from bottom up.
base::Value::List children;
@@ -476,16 +477,19 @@ void CertificateViewerDialogHandler::HandleRequestCertificateFields(
.Payload(model.ProcessRawBitsSignatureWrap())
.Build());
}
-
- contents_builder.Child(
- CertNodeBuilder(IDS_CERT_INFO_FINGERPRINTS_GROUP)
- .Child(CertNodeBuilder(IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL)
- .Payload(model.HashCertSHA256WithSeparators())
- .Build())
- .Child(CertNodeBuilder(IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL)
- .Payload(model.HashCertSHA1WithSeparators())
- .Build())
+ CertNodeBuilder fingerprint_builder =
+ CertNodeBuilder(IDS_CERT_INFO_FINGERPRINTS_GROUP);
+ fingerprint_builder.Child(
+ CertNodeBuilder(IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL)
+ .Payload(model.HashCertSHA256())
.Build());
+ if (model.is_valid()) {
+ fingerprint_builder.Child(
+ CertNodeBuilder(IDS_CERT_INFO_SHA256_SPKI_FINGERPRINT_LABEL)
+ .Payload(model.HashSpkiSHA256())
+ .Build());
+ }
+ contents_builder.Child(fingerprint_builder.Build());
base::Value::List root_list;
root_list.Append(CertNodeBuilder(model.GetTitle())
diff --git a/chromium/chrome/browser/ui/webui/certificates_handler.cc b/chromium/chrome/browser/ui/webui/certificates_handler.cc
index da8c5d68eb7..0a8d511fa9d 100644
--- a/chromium/chrome/browser/ui/webui/certificates_handler.cc
+++ b/chromium/chrome/browser/ui/webui/certificates_handler.cc
@@ -154,7 +154,7 @@ struct CertEquals {
// Certificate must be DER encoded, while PFX may be BER encoded.
// Therefore PFX can be distingushed by checking if the file starts with an
// indefinite SEQUENCE, or a definite SEQUENCE { INTEGER, ... }.
-bool CouldBePFX(const std::string& data) {
+bool CouldBePFX(std::string_view data) {
if (data.size() < 4)
return false;
@@ -164,7 +164,7 @@ bool CouldBePFX(const std::string& data) {
// If the SEQUENCE is definite length, it can be parsed through the version
// tag using DER parser, since INTEGER must be definite length, even in BER.
- net::der::Parser parser((net::der::Input(&data)));
+ net::der::Parser parser((net::der::Input(data)));
net::der::Parser sequence_parser;
if (!parser.ReadSequence(&sequence_parser))
return false;
diff --git a/chromium/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_desktop.cc b/chromium/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_desktop.cc
index b2d2743fa0b..1be54e9d8be 100644
--- a/chromium/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_desktop.cc
+++ b/chromium/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs_desktop.cc
@@ -10,8 +10,9 @@
#include "chrome/browser/companion/core/features.h"
#include "chrome/browser/ui/side_panel/companion/companion_utils.h"
#include "chrome/browser/ui/webui/feed/feed_ui_config.h"
+#include "chrome/browser/ui/webui/hats/hats_ui.h"
#include "chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.h"
-#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.h"
+#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.h"
#include "components/lens/buildflags.h"
#include "content/public/browser/webui_config_map.h"
#include "ui/accessibility/accessibility_features.h"
@@ -32,6 +33,7 @@ void RegisterDesktopChromeUntrustedWebUIConfigs() {
}
map.AddUntrustedWebUIConfig(
std::make_unique<ReadAnythingUIUntrustedConfig>());
+ map.AddUntrustedWebUIConfig(std::make_unique<HatsUIConfig>());
#if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES)
map.AddUntrustedWebUIConfig(std::make_unique<lens::LensUntrustedUIConfig>());
diff --git a/chromium/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chromium/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
index 486d6281486..a9c4f6f0862 100644
--- a/chromium/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -21,8 +21,8 @@
#include "chrome/test/base/ui_test_utils.h"
#include "components/enterprise/browser/controller/fake_browser_dm_token_storage.h"
#include "components/history_clusters/core/features.h"
+#include "components/nacl/common/buildflags.h"
#include "components/password_manager/core/common/password_manager_features.h"
-#include "components/search/ntp_features.h"
#include "components/user_notes/user_notes_features.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_handle.h"
@@ -35,7 +35,9 @@
#include "content/public/test/browser_test_utils.h"
#include "media/base/media_switches.h"
#include "printing/buildflags/buildflags.h"
+#include "third_party/abseil-cpp/absl/strings/ascii.h"
#include "ui/accessibility/accessibility_features.h"
+#include "ui/base/ui_base_features.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
@@ -112,7 +114,13 @@ class ChromeURLDataManagerTest : public InProcessBrowserTest {
// Makes sure navigating to the new tab page results in a http status code
// of 200.
-IN_PROC_BROWSER_TEST_F(ChromeURLDataManagerTest, 200) {
+// TODO(crbug.com/1473471) Test Failing on Mac11 tests
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_200 DISABLED_200
+#else
+#define MAYBE_200 200
+#endif
+IN_PROC_BROWSER_TEST_F(ChromeURLDataManagerTest, MAYBE_200) {
NavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
@@ -176,9 +184,7 @@ class ChromeURLDataManagerWebUITrustedTypesTest
enabled_features.push_back(features::kChromeWhatsNewUI);
enabled_features.push_back(history_clusters::kSidePanelJourneys);
enabled_features.push_back(features::kSupportTool);
- enabled_features.push_back(ntp_features::kCustomizeChromeSidePanel);
- enabled_features.push_back(
- password_manager::features::kPasswordManagerRedesign);
+ enabled_features.push_back(features::kCustomizeChromeSidePanel);
enabled_features.push_back(features::kReadAnything);
enabled_features.push_back(user_notes::kUserNotes);
@@ -239,7 +245,8 @@ class ChromeURLDataManagerWebUITrustedTypesTest
const ::testing::TestParamInfo<const char*>& info) {
std::string name(info.param);
std::replace_if(
- name.begin(), name.end(), [](char c) { return !std::isalnum(c); }, '_');
+ name.begin(), name.end(),
+ [](unsigned char c) { return !absl::ascii_isalnum(c); }, '_');
return name;
}
@@ -403,6 +410,9 @@ static constexpr const char* const kChromeUrls[] = {
"chrome://account-migration-welcome",
"chrome://add-supervision/",
"chrome://app-disabled",
+ "chrome://camera-app/views/main.html",
+ "chrome://assistant-optin/",
+ "chrome://bluetooth-pairing",
"chrome://certificate-manager/",
// Crashes because message handler is not registered outside of the dialog
// for confirm password change UI.
@@ -412,9 +422,11 @@ static constexpr const char* const kChromeUrls[] = {
"chrome://crostini-installer",
"chrome://crostini-upgrader",
"chrome://cryptohome",
+ "chrome://diagnostics",
"chrome://drive-internals",
"chrome://emoji-picker",
"chrome://family-link-user-internals",
+ "chrome://file-manager",
"chrome://guest-os-installer",
"chrome://help-app",
"chrome://linux-proxy-config",
@@ -427,6 +439,7 @@ static constexpr const char* const kChromeUrls[] = {
"chrome://office-fallback/",
"chrome://os-feedback",
"chrome-untrusted://os-feedback",
+ "chrome://os-settings",
"chrome://parent-access",
"chrome://password-change",
"chrome://personalization",
@@ -434,6 +447,7 @@ static constexpr const char* const kChromeUrls[] = {
"chrome://print-management",
"chrome-untrusted://projector",
"chrome://proximity-auth/proximity_auth.html",
+ "chrome://scanning",
"chrome://set-time",
"chrome://shimless-rma",
"chrome://shortcut-customization",
@@ -463,11 +477,10 @@ static constexpr const char* const kChromeUrls[] = {
#endif
#if !BUILDFLAG(IS_MAC)
"chrome://sandbox",
-// NaCl isn't supported on ARM64 Windows.
-#if !BUILDFLAG(IS_WIN) || !defined(ARCH_CPU_ARM64)
+#endif // !BUILDFLAG(IS_MAC)
+#if BUILDFLAG(ENABLE_NACL)
"chrome://nacl",
#endif
-#endif // !BUILDFLAG(IS_MAC)
#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS)
// TODO(https://crbug.com/1219651): this test is flaky on mac.
"chrome://bluetooth-internals",
diff --git a/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.cc b/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.cc
index d32af2fcbd2..bf5e62c7b6d 100644
--- a/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "build/chromeos_buildflags.h"
#include "chrome/browser/file_select_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
@@ -18,6 +19,10 @@
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/url_handler.h"
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
using content::BrowserContext;
using content::OpenURLParams;
using content::WebContents;
@@ -30,9 +35,9 @@ ChromeWebContentsHandler::~ChromeWebContentsHandler() {
// Opens a new URL inside |source|. |context| is the browser context that the
// browser should be owned by. |params| contains the URL to open and various
-// attributes such as disposition. On return |out_new_contents| contains the
-// WebContents the URL is opened in. Returns the web contents opened by the
-// browser.
+// attributes such as disposition. Returns the WebContents opened by the browser
+// on success. Otherwise, returns nullptr. In ChromeOS Ash, the URL might be
+// opened in Lacros. In that case, this function returns nullptr.
WebContents* ChromeWebContentsHandler::OpenURLFromTab(
content::BrowserContext* context,
WebContents* source,
@@ -40,6 +45,13 @@ WebContents* ChromeWebContentsHandler::OpenURLFromTab(
if (!context)
return nullptr;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // Try to intercept the request and open the URL with Lacros.
+ if (ash::TryOpenUrl(params.url, params.disposition)) {
+ return nullptr;
+ }
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
Profile* profile = Profile::FromBrowserContext(context);
Browser* browser = chrome::FindTabbedBrowser(profile, false);
diff --git a/chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 1fe6c2242c1..182cb8d7e84 100644
--- a/chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -75,6 +75,7 @@
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
+#include "chromeos/ash/components/scalable_iph/scalable_iph_constants.h"
#include "components/commerce/content/browser/commerce_internals_ui.h"
#include "components/commerce/core/commerce_constants.h"
#include "components/favicon/core/favicon_service.h"
@@ -98,6 +99,7 @@
#include "components/security_interstitials/content/known_interception_disclosure_ui.h"
#include "components/security_interstitials/content/urls.h"
#include "components/signin/public/base/signin_buildflags.h"
+#include "components/signin/public/base/signin_switches.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "components/supervised_user/core/common/buildflags.h"
#include "content/public/browser/web_contents.h"
@@ -167,7 +169,7 @@
#include "chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.h"
#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h"
#include "chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h"
-#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.h"
+#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.h"
#include "chrome/browser/ui/webui/side_panel/reading_list/reading_list_ui.h"
#include "chrome/browser/ui/webui/side_panel/user_notes/user_notes_side_panel_ui.h"
#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
@@ -208,10 +210,6 @@
#include "chrome/browser/ui/webui/chromeos/chrome_url_disabled/chrome_url_disabled_ui.h"
#endif
-#if !BUILDFLAG(IS_CHROMEOS)
-#include "chrome/browser/ui/webui/app_launcher_page_ui.h"
-#endif
-
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ui/webui/webui_js_error/webui_js_error_ui.h"
#endif
@@ -278,7 +276,6 @@
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
#include "chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.h"
-#include "chrome/browser/ui/webui/signin/signin_reauth_ui.h"
#include "chrome/browser/ui/webui/welcome/helpers.h"
#include "chrome/browser/ui/webui/welcome/welcome_ui.h"
#endif
@@ -287,6 +284,10 @@
#include "chrome/browser/ui/webui/signin/inline_login_ui.h"
#endif
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chrome/browser/ui/webui/signin/signin_reauth_ui.h"
+#endif
+
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
#include "chrome/browser/ui/webui/media_router/cast_feedback_ui.h"
#endif
@@ -299,8 +300,8 @@
#include "chrome/browser/ui/webui/ash/chromebox_for_meetings/network_settings_dialog.h"
#endif // BUILDFLAG(PLATFORM_CFM)
-#if BUILDFLAG(ENABLE_WAFFLE_DESKTOP)
-#include "chrome/browser/ui/webui/waffle/waffle_ui.h"
+#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
#endif
using content::WebUI;
@@ -531,15 +532,11 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
#if !BUILDFLAG(IS_ANDROID)
#if !BUILDFLAG(IS_CHROMEOS)
- // AppLauncherPage is not needed on Android or ChromeOS.
+ // AppHome is not needed on Android or ChromeOS.
if (url.host_piece() == chrome::kChromeUIAppLauncherPageHost && profile &&
extensions::ExtensionSystem::Get(profile)->extension_service() &&
!profile->IsGuestSession()) {
- if (base::FeatureList::IsEnabled(features::kDesktopPWAsAppHomePage)) {
- return &NewWebUI<webapps::AppHomeUI>;
- } else {
- return &NewWebUI<AppLauncherPageUI>;
- }
+ return &NewWebUI<webapps::AppHomeUI>;
}
#endif // !BUILDFLAG(IS_CHROMEOS)
if (profile->IsGuestSession() &&
@@ -557,10 +554,9 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
// Bookmarks are part of NTP on Android.
if (url.host_piece() == chrome::kChromeUIBookmarksHost)
return &NewWebUI<BookmarksUI>;
- if (url.host_piece() == password_manager::kChromeUIPasswordManagerHost &&
- base::FeatureList::IsEnabled(
- password_manager::features::kPasswordManagerRedesign))
+ if (url.host_piece() == password_manager::kChromeUIPasswordManagerHost) {
return &NewWebUI<PasswordManagerUI>;
+ }
if (url.host_piece() == chrome::kChromeUICommanderHost)
return &NewWebUI<CommanderUI>;
// Downloads list on Android uses the built-in download manager.
@@ -715,10 +711,10 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
return &NewWebUI<SigninEmailConfirmationUI>;
#endif
-#if BUILDFLAG(ENABLE_WAFFLE_DESKTOP)
- if (url.host_piece() == chrome::kChromeUIWaffleHost &&
- base::FeatureList::IsEnabled(kWaffle)) {
- return &NewWebUI<WaffleUI>;
+#if BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+ if (url.host_piece() == chrome::kChromeUISearchEngineChoiceHost &&
+ base::FeatureList::IsEnabled(switches::kSearchEngineChoice)) {
+ return &NewWebUI<SearchEngineChoiceUI>;
}
#endif
@@ -857,10 +853,6 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
}
if (url.host_piece() == chrome::kChromeUIDiceWebSigninInterceptHost)
return &NewWebUI<DiceWebSigninInterceptUI>;
- if (url.host_piece() == chrome::kChromeUISigninReauthHost &&
- !profile->IsOffTheRecord()) {
- return &NewWebUI<SigninReauthUI>;
- }
#endif
#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_ASH)
@@ -869,6 +861,13 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
return &NewWebUI<InlineLoginUI>;
#endif
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+ if (url.host_piece() == chrome::kChromeUISigninReauthHost &&
+ !profile->IsOffTheRecord()) {
+ return &NewWebUI<SigninReauthUI>;
+ }
+#endif
+
#if BUILDFLAG(PLATFORM_CFM)
if (url.host_piece() == chrome::kCfmNetworkSettingsHost)
return &NewWebUI<ash::cfm::NetworkSettingsDialogUi>;
@@ -1041,12 +1040,6 @@ base::RefCountedMemory* ChromeWebUIControllerFactory::GetFaviconResourceBytes(
return FlagsUI::GetFaviconResourceBytes(scale_factor);
#if !BUILDFLAG(IS_ANDROID)
-#if !BUILDFLAG(IS_CHROMEOS)
- // The Apps launcher page is not available on android or ChromeOS.
- if (page_url.host_piece() == chrome::kChromeUIAppLauncherPageHost)
- return AppLauncherPageUI::GetFaviconResourceBytes(scale_factor);
-#endif // !BUILDFLAG(IS_CHROMEOS)
-
if (page_url.host_piece() == chrome::kChromeUINewTabPageHost)
return NewTabPageUI::GetFaviconResourceBytes(scale_factor);
@@ -1105,12 +1098,16 @@ ChromeWebUIControllerFactory::GetListOfAcceptableURLs() {
// avoid confusion, the two instances should provide a link to each other.
GURL(chrome::kChromeUIAboutURL),
GURL(chrome::kChromeUIAppServiceInternalsURL),
+ GURL(chrome::kChromeUIChromeURLsURL),
GURL(chrome::kChromeUIComponentsUrl),
GURL(chrome::kChromeUICreditsURL),
GURL(chrome::kChromeUIDeviceLogUrl),
+ GURL(chrome::kChromeUIExtensionsInternalsURL),
+ GURL(chrome::kChromeUIExtensionsURL),
GURL(chrome::kChromeUIFlagsURL),
GURL(chrome::kChromeUIGpuURL),
GURL(chrome::kChromeUIHistogramsURL),
+ GURL(chrome::kChromeUIInspectURL),
GURL(chrome::kChromeUIInvalidationsUrl),
GURL(chrome::kChromeUIManagementURL),
GURL(chrome::kChromeUIPrefsInternalsURL),
@@ -1120,6 +1117,7 @@ ChromeWebUIControllerFactory::GetListOfAcceptableURLs() {
GURL(chrome::kChromeUISystemURL),
GURL(chrome::kChromeUITermsURL),
GURL(chrome::kChromeUIVersionURL),
+ GURL(chrome::kChromeUIWebAppInternalsURL),
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Pages that exist only in Ash, i.e. have no immediate counterpart in
@@ -1152,7 +1150,6 @@ ChromeWebUIControllerFactory::GetListOfAcceptableURLs() {
GURL(chrome::kChromeUIDiagnosticsAppURL),
GURL(chrome::kChromeUIDriveInternalsUrl),
GURL(chrome::kChromeUIEmojiPickerURL),
- GURL(chrome::kChromeUIExtensionsInternalsURL),
GURL(chrome::kChromeUIFirmwareUpdaterAppURL),
GURL(chrome::kChromeUIHealthdInternalsURL),
GURL(chrome::kChromeUIHumanPresenceInternalsURL),
@@ -1174,6 +1171,7 @@ ChromeWebUIControllerFactory::GetListOfAcceptableURLs() {
GURL(chrome::kChromeUIPowerUrl),
GURL(chrome::kChromeUIPrintManagementUrl),
GURL(chrome::kChromeUIScanningAppURL),
+ GURL(chrome::kChromeUISensorInfoURL),
GURL(chrome::kChromeUISetTimeURL),
GURL(chrome::kChromeUISlowURL),
GURL(chrome::kChromeUISmbShareURL),
@@ -1182,6 +1180,7 @@ ChromeWebUIControllerFactory::GetListOfAcceptableURLs() {
GURL(chrome::kChromeUIUntrustedTerminalURL),
GURL(chrome::kChromeUIUserImageURL),
GURL(chrome::kChromeUIVmUrl),
+ GURL(scalable_iph::kScalableIphDebugURL),
#if BUILDFLAG(ENABLE_EXTENSIONS)
// IME extension's Japanese options page. Opened via OS_URL_HANDLER SWA
@@ -1202,6 +1201,14 @@ ChromeWebUIControllerFactory::GetListOfAcceptableURLs() {
url::kStandardSchemeSeparator,
extension_misc::kEspeakSpeechSynthesisExtensionId,
extension_misc::kEspeakSpeechSynthesisOptionsPath})),
+ // This file doesn't exist but the options page links to it (b/269703827),
+ // so we have to list it here anyways to prevent opening an Ash window on
+ // e.g. shift-click.
+ // TODO(b/269703827): Revisit when Espeak is fixed.
+ GURL(base::StrCat({extensions::kExtensionScheme,
+ url::kStandardSchemeSeparator,
+ extension_misc::kEspeakSpeechSynthesisExtensionId,
+ "/COPYING"})),
GURL(base::StrCat({extensions::kExtensionScheme,
url::kStandardSchemeSeparator,
extension_misc::kGoogleSpeechSynthesisExtensionId,
diff --git a/chromium/chrome/browser/ui/webui/commerce/shopping_insights_side_panel_ui.cc b/chromium/chrome/browser/ui/webui/commerce/shopping_insights_side_panel_ui.cc
index bb7a8f4b864..5f8e4016a6c 100644
--- a/chromium/chrome/browser/ui/webui/commerce/shopping_insights_side_panel_ui.cc
+++ b/chromium/chrome/browser/ui/webui/commerce/shopping_insights_side_panel_ui.cc
@@ -64,6 +64,7 @@ ShoppingInsightsSidePanelUI::ShoppingInsightsSidePanelUI(content::WebUI* web_ui)
{"trackPriceDone", IDS_SHOPPING_INSIGHTS_SIDE_PANEL_TRACK_PRICE_DONE},
{"trackPriceError", IDS_SHOPPING_INSIGHTS_SIDE_PANEL_TRACK_PRICE_ERROR},
{"yesterday", IDS_PRICE_HISTORY_YESTERDAY_PRICE},
+ {"historyGraphAccessibility", IDS_PRICE_HISTORY_GRAPH_ACCESSIBILITY},
};
for (const auto& str : kLocalizedStrings) {
webui::AddLocalizedString(source, str.name, str.id);
diff --git a/chromium/chrome/browser/ui/webui/commerce/shopping_ui_handler_delegate.cc b/chromium/chrome/browser/ui/webui/commerce/shopping_ui_handler_delegate.cc
index dd306b512de..740949d64f5 100644
--- a/chromium/chrome/browser/ui/webui/commerce/shopping_ui_handler_delegate.cc
+++ b/chromium/chrome/browser/ui/webui/commerce/shopping_ui_handler_delegate.cc
@@ -13,6 +13,8 @@
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/webui/commerce/shopping_insights_side_panel_ui.h"
#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/commerce/core/commerce_feature_list.h"
+#include "components/commerce/core/price_tracking_utils.h"
#include "components/commerce/core/webui/shopping_list_handler.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/page_navigator.h"
@@ -38,7 +40,7 @@ ShoppingUiHandlerDelegate::ShoppingUiHandlerDelegate(
ShoppingUiHandlerDelegate::~ShoppingUiHandlerDelegate() = default;
absl::optional<GURL> ShoppingUiHandlerDelegate::GetCurrentTabUrl() {
- auto* browser = chrome::FindLastActive();
+ auto* browser = chrome::FindTabbedBrowser(profile_, false);
if (!browser) {
return absl::nullopt;
}
@@ -81,9 +83,16 @@ ShoppingUiHandlerDelegate::GetOrAddBookmarkForCurrentUrl() {
GURL url;
std::u16string title;
if (chrome::GetURLAndTitleToBookmark(web_contents, &url, &title)) {
- const bookmarks::BookmarkNode* other_node = bookmark_model_->other_node();
- return bookmark_model_->AddNewURL(other_node, other_node->children().size(),
- title, url);
+ const bookmarks::BookmarkNode* parent = bookmark_model_->other_node();
+
+ // Automatically add the bookmark to the shopping collection if enabled.
+ if (base::FeatureList::IsEnabled(commerce::kShoppingCollection)) {
+ parent =
+ commerce::GetShoppingCollectionBookmarkFolder(bookmark_model_, true);
+ }
+
+ return bookmark_model_->AddNewURL(parent, parent->children().size(), title,
+ url);
}
return nullptr;
}
diff --git a/chromium/chrome/browser/ui/webui/components/components_ui.cc b/chromium/chrome/browser/ui/webui/components/components_ui.cc
index 72f6d410219..b4ff9ae31f1 100644
--- a/chromium/chrome/browser/ui/webui/components/components_ui.cc
+++ b/chromium/chrome/browser/ui/webui/components/components_ui.cc
@@ -73,7 +73,7 @@ void CreateAndAddComponentsUIHTMLSource(Profile* profile) {
"isGuest",
#if BUILDFLAG(IS_CHROMEOS_ASH)
user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
- user_manager::UserManager::Get()->IsLoggedInAsPublicAccount()
+ user_manager::UserManager::Get()->IsLoggedInAsManagedGuestSession()
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
chromeos::BrowserParamsProxy::Get()->SessionType() ==
crosapi::mojom::SessionType::kPublicSession ||
diff --git a/chromium/chrome/browser/ui/webui/cookies_tree_model_util.cc b/chromium/chrome/browser/ui/webui/cookies_tree_model_util.cc
index 658c96c0055..02abbbbbf65 100644
--- a/chromium/chrome/browser/ui/webui/cookies_tree_model_util.cc
+++ b/chromium/chrome/browser/ui/webui/cookies_tree_model_util.cc
@@ -197,8 +197,8 @@ CookiesTreeModelUtil::GetCookieTreeNodeDictionary(const CookieTreeNode& node) {
case CookieTreeNode::DetailedInfo::TYPE_SHARED_WORKER: {
dict.Set(kKeyType, "shared_worker");
- const browsing_data::SharedWorkerHelper::SharedWorkerInfo&
- shared_worker_info = *node.GetDetailedInfo().shared_worker_info;
+ const browsing_data::SharedWorkerInfo& shared_worker_info =
+ *node.GetDetailedInfo().shared_worker_info;
dict.Set(kKeyOrigin, shared_worker_info.worker.spec());
dict.Set(kKeyName, shared_worker_info.name);
diff --git a/chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc b/chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc
index 9383422211e..2b9bc1e2271 100644
--- a/chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc
+++ b/chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc
@@ -10,7 +10,10 @@
#include "base/feature_list.h"
#include "base/scoped_observation.h"
#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_features.h"
#include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
+#include "chrome/browser/preloading/chrome_preloading.h"
+#include "chrome/browser/preloading/prerender/prerender_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/web_applications/preinstalled_web_app_manager.h"
@@ -194,6 +197,27 @@ void MostVisitedHandler::OnMostVisitedTileNavigation(
false));
}
+void MostVisitedHandler::PrerenderMostVisitedTile(
+ most_visited::mojom::MostVisitedTilePtr tile,
+ bool is_hover_trigger) {
+ if (base::FeatureList::IsEnabled(features::kNewTabPageTriggerForPrerender2)) {
+ PrerenderManager::CreateForWebContents(web_contents_);
+ auto* prerender_manager = PrerenderManager::FromWebContents(web_contents_);
+ prerender_handle_ = prerender_manager->StartPrerenderNewTabPage(
+ tile->url, is_hover_trigger
+ ? chrome_preloading_predictor::kMouseHoverOnNewTabPage
+ : chrome_preloading_predictor::kPointerDownOnNewTabPage);
+ }
+}
+
+void MostVisitedHandler::CancelPrerender() {
+ if (base::FeatureList::IsEnabled(features::kNewTabPageTriggerForPrerender2)) {
+ auto* prerender_manager = PrerenderManager::FromWebContents(web_contents_);
+ prerender_manager->StopPrerenderNewTabPage(prerender_handle_);
+ prerender_handle_ = nullptr;
+ }
+}
+
void MostVisitedHandler::OnURLsAvailable(
const std::map<ntp_tiles::SectionType, ntp_tiles::NTPTilesVector>&
sections) {
diff --git a/chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h b/chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h
index c2e463e2e6a..d4c3e08e775 100644
--- a/chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h
+++ b/chromium/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h
@@ -16,6 +16,7 @@
#include "components/ntp_tiles/most_visited_sites.h"
#include "components/ntp_tiles/ntp_tile.h"
#include "components/ntp_tiles/section_type.h"
+#include "content/public/browser/prerender_handle.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "ui/webui/resources/cr_components/most_visited/most_visited.mojom.h"
@@ -62,6 +63,9 @@ class MostVisitedHandler : public most_visited::mojom::MostVisitedPageHandler,
const GURL& new_url,
const std::string& new_title,
UpdateMostVisitedTileCallback callback) override;
+ void PrerenderMostVisitedTile(most_visited::mojom::MostVisitedTilePtr tile,
+ bool is_hover_trigger) override;
+ void CancelPrerender() override;
void OnMostVisitedTilesRendered(
std::vector<most_visited::mojom::MostVisitedTilePtr> tiles,
double time) override;
@@ -91,6 +95,8 @@ class MostVisitedHandler : public most_visited::mojom::MostVisitedPageHandler,
base::Time ntp_navigation_start_time_;
GURL last_blocklisted_;
+ base::WeakPtr<content::PrerenderHandle> prerender_handle_;
+
mojo::Receiver<most_visited::mojom::MostVisitedPageHandler> page_handler_;
mojo::Remote<most_visited::mojom::MostVisitedPage> page_;
diff --git a/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/OWNERS b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/OWNERS
new file mode 100644
index 00000000000..2895b60c9ea
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/OWNERS
@@ -0,0 +1,4 @@
+file://chrome/browser/ui/webui/new_tab_page/OWNERS
+
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS \ No newline at end of file
diff --git a/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.cc b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.cc
new file mode 100644
index 00000000000..0dbcaa70c09
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.cc
@@ -0,0 +1,95 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.h"
+
+#include <array>
+#include <utility>
+
+#include "chrome/browser/new_tab_page/chrome_colors/generated_colors_info.h"
+#include "chrome/browser/new_tab_page/chrome_colors/selected_colors_info.h"
+#include "ui/base/mojom/themes.mojom.h"
+
+namespace {
+
+// Returns chrome color with ID |kCustomizeChromeColorIds[I]|.
+template <size_t I>
+constexpr chrome_colors::ColorInfo GetChromeColor() {
+ constexpr int id = kCustomizeChromeColorIds[I];
+ // We assume that chrome colors are stored sequentially ordered by their ID
+ // starting at ID 1.
+ constexpr auto chrome_color = chrome_colors::kGeneratedColorsInfo[id - 1];
+ static_assert(chrome_color.id == id);
+ return chrome_color;
+}
+
+template <std::size_t... I>
+constexpr auto MakeCustomizeChromeColors(std::index_sequence<I...>) {
+ return std::array<chrome_colors::ColorInfo, sizeof...(I)>{
+ GetChromeColor<I>()...};
+}
+
+} // namespace
+
+const decltype(kCustomizeChromeColors) kCustomizeChromeColors =
+ MakeCustomizeChromeColors(
+ std::make_index_sequence<std::size(kCustomizeChromeColorIds)>{});
+
+const decltype(kDynamicCustomizeChromeColors) kDynamicCustomizeChromeColors =
+ std::array<DynamicColorInfo, 13>{
+ // ID 0 reserved for other colors.
+ // ID 1 reserved for grayscale theme.
+ DynamicColorInfo(/*id=*/2,
+ SkColorSetRGB(140, 171, 228),
+ IDS_NTP_COLORS_BLUE,
+ ui::mojom::BrowserColorVariant::kTonalSpot),
+ DynamicColorInfo(/*id=*/3,
+ SkColorSetRGB(140, 171, 228),
+ IDS_NTP_COLORS_COOL_GREY,
+ ui::mojom::BrowserColorVariant::kNeutral),
+ DynamicColorInfo(/*id=*/4,
+ SkColorSetRGB(136, 136, 136),
+ IDS_NTP_COLORS_GREY,
+ ui::mojom::BrowserColorVariant::kNeutral),
+ DynamicColorInfo(/*id=*/5,
+ SkColorSetRGB(38, 166, 154),
+ IDS_NTP_COLORS_AQUA,
+ ui::mojom::BrowserColorVariant::kTonalSpot),
+ DynamicColorInfo(/*id=*/6,
+ SkColorSetRGB(0, 255, 0),
+ IDS_NTP_COLORS_GREEN,
+ ui::mojom::BrowserColorVariant::kTonalSpot),
+ DynamicColorInfo(/*id=*/7,
+ SkColorSetRGB(135, 186, 129),
+ IDS_NTP_COLORS_VIRIDIAN,
+ ui::mojom::BrowserColorVariant::kNeutral),
+ DynamicColorInfo(/*id=*/8,
+ SkColorSetRGB(250, 223, 115),
+ IDS_NTP_COLORS_CITRON,
+ ui::mojom::BrowserColorVariant::kTonalSpot),
+ DynamicColorInfo(/*id=*/9,
+ SkColorSetRGB(255, 128, 0),
+ IDS_NTP_COLORS_ORANGE,
+ ui::mojom::BrowserColorVariant::kTonalSpot),
+ DynamicColorInfo(/*id=*/10,
+ SkColorSetRGB(252, 219, 201),
+ IDS_NTP_COLORS_APRICOT,
+ ui::mojom::BrowserColorVariant::kNeutral),
+ DynamicColorInfo(/*id=*/11,
+ SkColorSetRGB(243, 178, 190),
+ IDS_NTP_COLORS_ROSE,
+ ui::mojom::BrowserColorVariant::kTonalSpot),
+ DynamicColorInfo(/*id=*/12,
+ SkColorSetRGB(243, 178, 190),
+ IDS_NTP_COLORS_PINK,
+ ui::mojom::BrowserColorVariant::kNeutral),
+ DynamicColorInfo(/*id=*/13,
+ SkColorSetRGB(255, 0, 255),
+ IDS_NTP_COLORS_FUCHSIA,
+ ui::mojom::BrowserColorVariant::kTonalSpot),
+ DynamicColorInfo(/*id=*/14,
+ SkColorSetRGB(229, 213, 252),
+ IDS_NTP_COLORS_VIOLET,
+ ui::mojom::BrowserColorVariant::kTonalSpot),
+ };
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.h b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.h
index 8ec86a15561..8f9ee41210c 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.h
+++ b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.h
@@ -2,12 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_COLORS_H_
-#define CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_COLORS_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_THEME_COLOR_PICKER_CUSTOMIZE_CHROME_COLORS_H_
+#define CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_THEME_COLOR_PICKER_CUSTOMIZE_CHROME_COLORS_H_
#include <array>
#include "chrome/browser/new_tab_page/chrome_colors/selected_colors_info.h"
+#include "ui/base/mojom/themes.mojom.h"
// The customize chrome side panel only uses the chrome colors with the
// following ids, which is a subset of all chrome colors.
@@ -34,4 +35,18 @@ extern const std::array<chrome_colors::ColorInfo,
std::size(kCustomizeChromeColorIds)>
kCustomizeChromeColors;
-#endif // CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_COLORS_H_
+struct DynamicColorInfo {
+ constexpr DynamicColorInfo(int id,
+ SkColor color,
+ int label_id,
+ ui::mojom::BrowserColorVariant variant)
+ : id(id), color(color), label_id(label_id), variant(variant) {}
+ int id;
+ SkColor color;
+ int label_id;
+ ui::mojom::BrowserColorVariant variant;
+};
+
+extern const std::array<DynamicColorInfo, 13> kDynamicCustomizeChromeColors;
+
+#endif // CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_THEME_COLOR_PICKER_CUSTOMIZE_CHROME_COLORS_H_
diff --git a/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.cc b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.cc
new file mode 100644
index 00000000000..dc11d2a8a4d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.cc
@@ -0,0 +1,279 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h"
+
+#include <limits>
+
+#include "base/containers/fixed_flat_map.h"
+#include "chrome/browser/new_tab_page/chrome_colors/generated_colors_info.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
+#include "chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/themes/autogenerated_theme_util.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/mojom/themes.mojom.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/color/color_provider.h"
+#include "ui/color/color_provider_key.h"
+#include "ui/color/dynamic_color/palette_factory.h"
+#include "ui/native_theme/native_theme.h"
+
+namespace {
+
+ui::ColorProviderKey::SchemeVariant GetSchemeVariant(
+ ui::mojom::BrowserColorVariant color_variant) {
+ using BCV = ui::mojom::BrowserColorVariant;
+ using SV = ui::ColorProviderKey::SchemeVariant;
+ static constexpr auto kSchemeVariantMap = base::MakeFixedFlatMap<BCV, SV>({
+ {BCV::kTonalSpot, SV::kTonalSpot},
+ {BCV::kNeutral, SV::kNeutral},
+ {BCV::kVibrant, SV::kVibrant},
+ {BCV::kExpressive, SV::kExpressive},
+ });
+ return kSchemeVariantMap.at(color_variant);
+}
+
+// Create a GM2 chrome color.
+theme_color_picker::mojom::ChromeColorPtr CreateChromeColor(
+ chrome_colors::ColorInfo color_info) {
+ auto theme_colors = GetAutogeneratedThemeColors(color_info.color);
+ auto color = theme_color_picker::mojom::ChromeColor::New();
+ color->name = l10n_util::GetStringUTF8(color_info.label_id);
+ color->seed = color_info.color;
+ color->background = theme_colors.active_tab_color;
+ color->foreground = theme_colors.frame_color;
+ color->variant = ui::mojom::BrowserColorVariant::kSystem;
+ return color;
+}
+
+// Create a GM3 chrome color that takes color scheme into account.
+theme_color_picker::mojom::ChromeColorPtr CreateDynamicChromeColor(
+ DynamicColorInfo color_info,
+ bool is_dark_mode) {
+ auto color = theme_color_picker::mojom::ChromeColor::New();
+ auto color_palette = ui::GeneratePalette(
+ color_info.color, GetSchemeVariant(color_info.variant));
+ color->name = l10n_util::GetStringUTF8(color_info.label_id);
+ color->seed = color_info.color;
+ color->background = is_dark_mode ? color_palette->primary().get(80)
+ : color_palette->primary().get(40);
+ color->foreground = is_dark_mode ? color_palette->primary().get(30)
+ : color_palette->primary().get(90);
+ color->base = is_dark_mode ? color_palette->secondary().get(50)
+ : color_palette->primary().get(80);
+ color->variant = color_info.variant;
+ return color;
+}
+} // namespace
+
+ThemeColorPickerHandler::ThemeColorPickerHandler(
+ mojo::PendingReceiver<theme_color_picker::mojom::ThemeColorPickerHandler>
+ pending_handler,
+ mojo::PendingRemote<theme_color_picker::mojom::ThemeColorPickerClient>
+ pending_client,
+ NtpCustomBackgroundService* ntp_custom_background_service,
+ content::WebContents* web_contents)
+ : ntp_custom_background_service_(ntp_custom_background_service),
+ profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
+ web_contents_(web_contents),
+ theme_service_(ThemeServiceFactory::GetForProfile(profile_)),
+ client_(std::move(pending_client)),
+ receiver_(this, std::move(pending_handler)) {
+ CHECK(ntp_custom_background_service_);
+ CHECK(theme_service_);
+ native_theme_observation_.Observe(ui::NativeTheme::GetInstanceForNativeUi());
+ theme_service_observation_.Observe(theme_service_);
+
+ ntp_custom_background_service_observation_.Observe(
+ ntp_custom_background_service_.get());
+}
+
+ThemeColorPickerHandler::~ThemeColorPickerHandler() {}
+
+// static
+void ThemeColorPickerHandler::RegisterProfilePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterIntegerPref(prefs::kSeedColorChangeCount, 0);
+}
+
+void ThemeColorPickerHandler::SetDefaultColor() {
+ if (features::IsChromeWebuiRefresh2023()) {
+ theme_service_->SetUserColor(SK_ColorTRANSPARENT);
+ theme_service_->UseDeviceTheme(false);
+ MaybeIncrementSeedColorChangeCount();
+ } else {
+ theme_service_->UseDefaultTheme();
+ }
+}
+
+void ThemeColorPickerHandler::SetGreyDefaultColor() {
+ theme_service_->SetIsGrayscale(true);
+ if (features::IsChromeWebuiRefresh2023()) {
+ theme_service_->UseDeviceTheme(false);
+ MaybeIncrementSeedColorChangeCount();
+ }
+}
+
+void ThemeColorPickerHandler::SetSeedColor(
+ SkColor seed_color,
+ ui::mojom::BrowserColorVariant variant) {
+ if (features::IsChromeWebuiRefresh2023()) {
+ theme_service_->SetUserColorAndBrowserColorVariant(seed_color, variant);
+ theme_service_->UseDeviceTheme(false);
+ MaybeIncrementSeedColorChangeCount();
+ } else {
+ theme_service_->BuildAutogeneratedThemeFromColor(seed_color);
+ }
+}
+
+void ThemeColorPickerHandler::SetSeedColorFromHue(float hue) {
+ SetSeedColor(color_utils::HSLToSkColor(
+ {std::clamp(hue / 360, 0.0f, 1.0f), 1, .5}, 255),
+ ui::mojom::BrowserColorVariant::kTonalSpot);
+}
+
+void ThemeColorPickerHandler::GetChromeColors(
+ bool is_dark_mode,
+ bool extended_list,
+ GetChromeColorsCallback callback) {
+ std::vector<theme_color_picker::mojom::ChromeColorPtr> colors;
+ if (extended_list) {
+ for (const auto& color_info : chrome_colors::kGeneratedColorsInfo) {
+ colors.push_back(CreateChromeColor(color_info));
+ }
+ } else if (features::IsChromeWebuiRefresh2023()) {
+ for (const auto& color_info : kDynamicCustomizeChromeColors) {
+ colors.push_back(CreateDynamicChromeColor(color_info, is_dark_mode));
+ }
+ } else {
+ for (const auto& color_info : kCustomizeChromeColors) {
+ colors.push_back(CreateChromeColor(color_info));
+ }
+ }
+ std::move(callback).Run(std::move(colors));
+}
+
+void ThemeColorPickerHandler::RemoveBackgroundImage() {
+ if (ntp_custom_background_service_) {
+ ntp_custom_background_service_->ResetCustomBackgroundInfo();
+ }
+}
+
+void ThemeColorPickerHandler::UpdateTheme() {
+ if (ntp_custom_background_service_) {
+ ntp_custom_background_service_->RefreshBackgroundIfNeeded();
+ }
+ auto theme = theme_color_picker::mojom::Theme::New();
+ auto custom_background =
+ ntp_custom_background_service_
+ ? ntp_custom_background_service_->GetCustomBackground()
+ : absl::nullopt;
+ theme->has_background_image = custom_background.has_value();
+ if (custom_background.has_value() &&
+ custom_background->custom_background_main_color.has_value()) {
+ theme->background_image_main_color =
+ *custom_background->custom_background_main_color;
+ }
+
+ theme->follow_device_theme = features::IsChromeWebuiRefresh2023() &&
+ theme_service_->UsingDeviceTheme();
+
+ auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+ CHECK(native_theme);
+
+ auto user_color = theme->follow_device_theme ? native_theme->user_color()
+ : theme_service_->GetUserColor();
+ // If a user has the GM3 flag enabled but a GM2 theme set they are in a limbo
+ // state between the 2 theme types. We need to get the color of their theme
+ // with GetAutogeneratedThemeColor still until they set a GM3 theme, use the
+ // old way of detecting default, and use the old color tokens to keep an
+ // accurate representation of what the user is seeing.
+ if (features::IsChromeWebuiRefresh2023() && user_color.has_value()) {
+ theme->seed_color = user_color.value();
+ theme->background_color =
+ web_contents_->GetColorProvider().GetColor(ui::kColorSysInversePrimary);
+ theme->color_picker_icon_color =
+ web_contents_->GetColorProvider().GetColor(ui::kColorSysOnSurface);
+ if (theme->seed_color != SK_ColorTRANSPARENT) {
+ theme->foreground_color = theme->background_color;
+ }
+ } else {
+ theme->seed_color = theme_service_->GetAutogeneratedThemeColor();
+ theme->background_color =
+ web_contents_->GetColorProvider().GetColor(kColorNewTabPageBackground);
+ if (!theme_service_->UsingDefaultTheme() &&
+ !theme_service_->UsingSystemTheme()) {
+ theme->foreground_color =
+ web_contents_->GetColorProvider().GetColor(ui::kColorFrameActive);
+ }
+ theme->color_picker_icon_color =
+ web_contents_->GetColorProvider().GetColor(kColorNewTabPageText);
+ }
+ color_utils::HSL hsl;
+ color_utils::SkColorToHSL(theme->seed_color, &hsl);
+ theme->seed_color_hue = hsl.h * 360;
+ theme->is_grey_baseline = theme_service_->GetIsGrayscale();
+ theme->browser_color_variant = theme_service_->GetBrowserColorVariant();
+
+ if (!theme_service_->UsingDefaultTheme() &&
+ !theme_service_->UsingSystemTheme()) {
+ theme->colors_managed_by_policy = theme_service_->UsingPolicyTheme();
+ } else {
+ theme->colors_managed_by_policy = false;
+ }
+ theme->has_third_party_theme = theme_service_->UsingExtensionTheme();
+
+ // If Chrome WebUI Refresh 2023 flag is enabled and BrowserColorScheme is not
+ // set to follow the system, use BrowserColorScheme for deciding dark mode
+ // boolean. Otherwise, use the system value.
+ ThemeService::BrowserColorScheme colorScheme =
+ theme_service_->GetBrowserColorScheme();
+ if (features::IsChromeWebuiRefresh2023() &&
+ colorScheme != ThemeService::BrowserColorScheme::kSystem) {
+ theme->is_dark_mode =
+ colorScheme == ThemeService::BrowserColorScheme::kDark;
+ } else {
+ theme->is_dark_mode = native_theme->ShouldUseDarkColors();
+ }
+ client_->SetTheme(std::move(theme));
+}
+
+void ThemeColorPickerHandler::MaybeIncrementSeedColorChangeCount() {
+ if (!seed_color_changed_) {
+ const auto count =
+ profile_->GetPrefs()->GetInteger(prefs::kSeedColorChangeCount);
+ if (count < INT_MAX) {
+ profile_->GetPrefs()->SetInteger(prefs::kSeedColorChangeCount, count + 1);
+ }
+
+ seed_color_changed_ = true;
+ }
+}
+
+void ThemeColorPickerHandler::OnNativeThemeUpdated(
+ ui::NativeTheme* observed_theme) {
+ UpdateTheme();
+}
+
+void ThemeColorPickerHandler::OnThemeChanged() {
+ UpdateTheme();
+}
+
+void ThemeColorPickerHandler::OnCustomBackgroundImageUpdated() {
+ OnThemeChanged();
+}
+
+void ThemeColorPickerHandler::OnNtpCustomBackgroundServiceShuttingDown() {
+ ntp_custom_background_service_observation_.Reset();
+ ntp_custom_background_service_ = nullptr;
+}
diff --git a/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h
new file mode 100644
index 00000000000..614f5795e2a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h
@@ -0,0 +1,91 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_THEME_COLOR_PICKER_THEME_COLOR_PICKER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_THEME_COLOR_PICKER_THEME_COLOR_PICKER_HANDLER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/search/background/ntp_custom_background_service.h"
+#include "chrome/browser/search/background/ntp_custom_background_service_observer.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_observer.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "ui/base/mojom/themes.mojom.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/native_theme/native_theme_observer.h"
+#include "ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.mojom.h"
+
+namespace content {
+class WebContents;
+} // namespace content
+
+class Profile;
+
+class ThemeColorPickerHandler
+ : public theme_color_picker::mojom::ThemeColorPickerHandler,
+ public ui::NativeThemeObserver,
+ public ThemeServiceObserver,
+ public NtpCustomBackgroundServiceObserver {
+ public:
+ ThemeColorPickerHandler(
+ mojo::PendingReceiver<theme_color_picker::mojom::ThemeColorPickerHandler>
+ pending_handler,
+ mojo::PendingRemote<theme_color_picker::mojom::ThemeColorPickerClient>
+ pending_client,
+ NtpCustomBackgroundService* ntp_custom_background_service,
+ content::WebContents* web_contents);
+
+ ThemeColorPickerHandler(const ThemeColorPickerHandler&) = delete;
+ ThemeColorPickerHandler& operator=(const ThemeColorPickerHandler&) = delete;
+
+ ~ThemeColorPickerHandler() override;
+
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ // side_panel::mojom::CustomizeChromePageHandler:
+ void SetDefaultColor() override;
+ void SetGreyDefaultColor() override;
+ void SetSeedColor(SkColor seed_color,
+ ui::mojom::BrowserColorVariant variant) override;
+ void SetSeedColorFromHue(float hue) override;
+ void GetChromeColors(bool is_dark_mode,
+ bool extended_list,
+ GetChromeColorsCallback callback) override;
+ void RemoveBackgroundImage() override;
+ void UpdateTheme() override;
+
+ private:
+ void MaybeIncrementSeedColorChangeCount();
+
+ // ui::NativeThemeObserver:
+ void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
+
+ // ThemeServiceObserver:
+ void OnThemeChanged() override;
+
+ // NtpCustomBackgroundServiceObserver:
+ void OnCustomBackgroundImageUpdated() override;
+ void OnNtpCustomBackgroundServiceShuttingDown() override;
+
+ bool seed_color_changed_ = false;
+ raw_ptr<NtpCustomBackgroundService> ntp_custom_background_service_;
+ raw_ptr<Profile> profile_;
+ raw_ptr<content::WebContents> web_contents_;
+ raw_ptr<ThemeService> theme_service_;
+ base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
+ native_theme_observation_{this};
+ base::ScopedObservation<ThemeService, ThemeServiceObserver>
+ theme_service_observation_{this};
+ base::ScopedObservation<NtpCustomBackgroundService,
+ NtpCustomBackgroundServiceObserver>
+ ntp_custom_background_service_observation_{this};
+
+ mojo::Remote<theme_color_picker::mojom::ThemeColorPickerClient> client_;
+ mojo::Receiver<theme_color_picker::mojom::ThemeColorPickerHandler> receiver_;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_THEME_COLOR_PICKER_THEME_COLOR_PICKER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler_unittest.cc b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler_unittest.cc
new file mode 100644
index 00000000000..8ce6e56b565
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler_unittest.cc
@@ -0,0 +1,646 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h"
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/fixed_flat_map.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/new_tab_page/chrome_colors/generated_colors_info.h"
+#include "chrome/browser/search/background/ntp_custom_background_service.h"
+#include "chrome/browser/search/background/ntp_custom_background_service_observer.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
+#include "chrome/browser/ui/webui/cr_components/theme_color_picker/customize_chrome_colors.h"
+#include "chrome/common/themes/autogenerated_theme_util.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_web_contents_factory.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_builder.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/mojom/themes.mojom.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/color/color_provider.h"
+#include "ui/color/color_provider_key.h"
+#include "ui/color/dynamic_color/palette_factory.h"
+#include "ui/native_theme/native_theme.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace {
+
+using testing::_;
+
+ui::ColorProviderKey::SchemeVariant GetSchemeVariant(
+ ui::mojom::BrowserColorVariant color_variant) {
+ using BCV = ui::mojom::BrowserColorVariant;
+ using SV = ui::ColorProviderKey::SchemeVariant;
+ static constexpr auto kSchemeVariantMap = base::MakeFixedFlatMap<BCV, SV>({
+ {BCV::kTonalSpot, SV::kTonalSpot},
+ {BCV::kNeutral, SV::kNeutral},
+ {BCV::kVibrant, SV::kVibrant},
+ {BCV::kExpressive, SV::kExpressive},
+ });
+ return kSchemeVariantMap.at(color_variant);
+}
+
+class MockClient : public theme_color_picker::mojom::ThemeColorPickerClient {
+ public:
+ MockClient() = default;
+ ~MockClient() override = default;
+
+ mojo::PendingRemote<theme_color_picker::mojom::ThemeColorPickerClient>
+ BindAndGetRemote() {
+ DCHECK(!receiver_.is_bound());
+ return receiver_.BindNewPipeAndPassRemote();
+ }
+
+ void FlushForTesting() { receiver_.FlushForTesting(); }
+
+ MOCK_METHOD(void, SetTheme, (theme_color_picker::mojom::ThemePtr));
+
+ mojo::Receiver<theme_color_picker::mojom::ThemeColorPickerClient> receiver_{
+ this};
+};
+
+class MockNtpCustomBackgroundService : public NtpCustomBackgroundService {
+ public:
+ explicit MockNtpCustomBackgroundService(Profile* profile)
+ : NtpCustomBackgroundService(profile) {}
+ MOCK_METHOD(absl::optional<CustomBackground>, GetCustomBackground, ());
+ MOCK_METHOD(void, ResetCustomBackgroundInfo, ());
+ MOCK_METHOD(void, AddObserver, (NtpCustomBackgroundServiceObserver*));
+};
+
+class MockThemeService : public ThemeService {
+ public:
+ MockThemeService() : ThemeService(nullptr, theme_helper_) { set_ready(); }
+ using ThemeService::NotifyThemeChanged;
+ MOCK_METHOD(void, UseDefaultTheme, ());
+ MOCK_CONST_METHOD0(UsingDefaultTheme, bool());
+ MOCK_METHOD(void, UseDeviceTheme, (bool));
+ MOCK_CONST_METHOD0(UsingSystemTheme, bool());
+ MOCK_CONST_METHOD0(UsingExtensionTheme, bool());
+ MOCK_CONST_METHOD0(UsingPolicyTheme, bool());
+ MOCK_CONST_METHOD0(GetThemeID, std::string());
+ MOCK_METHOD(void, BuildAutogeneratedThemeFromColor, (SkColor));
+ MOCK_CONST_METHOD0(GetAutogeneratedThemeColor, SkColor());
+ MOCK_CONST_METHOD0(GetBrowserColorScheme, ThemeService::BrowserColorScheme());
+ MOCK_METHOD(void, SetUserColor, (absl::optional<SkColor>));
+ MOCK_CONST_METHOD0(GetUserColor, absl::optional<SkColor>());
+ MOCK_CONST_METHOD0(UsingDeviceTheme, bool());
+ MOCK_CONST_METHOD0(GetBrowserColorVariant, ui::mojom::BrowserColorVariant());
+ MOCK_METHOD(void,
+ SetUserColorAndBrowserColorVariant,
+ (SkColor, ui::mojom::BrowserColorVariant));
+ MOCK_METHOD(void, SetIsGrayscale, (bool));
+ MOCK_CONST_METHOD0(GetIsGrayscale, bool());
+
+ private:
+ ThemeHelper theme_helper_;
+};
+
+std::unique_ptr<TestingProfile> MakeTestingProfile() {
+ TestingProfile::Builder profile_builder;
+ profile_builder.AddTestingFactory(
+ ThemeServiceFactory::GetInstance(),
+ base::BindRepeating([](content::BrowserContext* context)
+ -> std::unique_ptr<KeyedService> {
+ return std::make_unique<testing::NiceMock<MockThemeService>>();
+ }));
+ return profile_builder.Build();
+}
+
+} // namespace
+
+class ThemeColorPickerHandlerTest : public testing::Test {
+ public:
+ ThemeColorPickerHandlerTest()
+ : profile_(MakeTestingProfile()),
+ mock_ntp_custom_background_service_(profile_.get()),
+ web_contents_(web_contents_factory_.CreateWebContents(profile_.get())),
+ mock_theme_service_(static_cast<MockThemeService*>(
+ ThemeServiceFactory::GetForProfile(profile_.get()))) {}
+
+ void SetUp() override {
+ EXPECT_CALL(mock_ntp_custom_background_service_, AddObserver)
+ .Times(1)
+ .WillOnce(
+ testing::SaveArg<0>(&ntp_custom_background_service_observer_));
+ handler_ = std::make_unique<ThemeColorPickerHandler>(
+ mojo::PendingReceiver<
+ theme_color_picker::mojom::ThemeColorPickerHandler>(),
+ mock_client_.BindAndGetRemote(), &mock_ntp_custom_background_service_,
+ web_contents_);
+ mock_client_.FlushForTesting();
+ EXPECT_EQ(handler_.get(), ntp_custom_background_service_observer_);
+
+ scoped_feature_list_.Reset();
+ }
+
+ TestingProfile& profile() { return *profile_; }
+ content::WebContents& web_contents() { return *web_contents_; }
+ ThemeColorPickerHandler& handler() { return *handler_; }
+ NtpCustomBackgroundServiceObserver& ntp_custom_background_service_observer() {
+ return *ntp_custom_background_service_observer_;
+ }
+ MockThemeService& mock_theme_service() { return *mock_theme_service_; }
+
+ protected:
+ // NOTE: The initialization order of these members matters.
+ content::BrowserTaskEnvironment task_environment_;
+ std::unique_ptr<TestingProfile> profile_;
+ testing::NiceMock<MockNtpCustomBackgroundService>
+ mock_ntp_custom_background_service_;
+ // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+ // #addr-of
+ RAW_PTR_EXCLUSION NtpCustomBackgroundServiceObserver*
+ ntp_custom_background_service_observer_;
+ content::TestWebContentsFactory web_contents_factory_;
+ raw_ptr<content::WebContents> web_contents_;
+ testing::NiceMock<MockClient> mock_client_;
+ raw_ptr<MockThemeService> mock_theme_service_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+ std::unique_ptr<ThemeColorPickerHandler> handler_;
+};
+
+TEST_F(ThemeColorPickerHandlerTest, GetChromeColorsExtended) {
+ std::vector<theme_color_picker::mojom::ChromeColorPtr> colors;
+ base::MockCallback<ThemeColorPickerHandler::GetChromeColorsCallback> callback;
+ EXPECT_CALL(callback, Run(testing::_))
+ .Times(1)
+ .WillOnce(testing::Invoke(
+ [&colors](std::vector<theme_color_picker::mojom::ChromeColorPtr>
+ colors_arg) { colors = std::move(colors_arg); }));
+ handler().GetChromeColors(false, true, callback.Get());
+
+ auto num_colors = sizeof(chrome_colors::kGeneratedColorsInfo) /
+ sizeof(chrome_colors::kGeneratedColorsInfo[0]);
+ ASSERT_EQ(num_colors, colors.size());
+ for (size_t i = 0; i < num_colors; i++) {
+ EXPECT_EQ(l10n_util::GetStringUTF8(
+ chrome_colors::kGeneratedColorsInfo[i].label_id),
+ colors[i]->name);
+ EXPECT_EQ(chrome_colors::kGeneratedColorsInfo[i].color, colors[i]->seed);
+ EXPECT_EQ(GetAutogeneratedThemeColors(
+ chrome_colors::kGeneratedColorsInfo[i].color)
+ .active_tab_color,
+ colors[i]->background);
+ EXPECT_EQ(GetAutogeneratedThemeColors(
+ chrome_colors::kGeneratedColorsInfo[i].color)
+ .frame_color,
+ colors[i]->foreground);
+ }
+}
+
+TEST_F(ThemeColorPickerHandlerTest, GetChromeColors) {
+ std::vector<theme_color_picker::mojom::ChromeColorPtr> colors;
+ base::MockCallback<ThemeColorPickerHandler::GetChromeColorsCallback> callback;
+ EXPECT_CALL(callback, Run(testing::_))
+ .Times(1)
+ .WillOnce(testing::Invoke(
+ [&colors](std::vector<theme_color_picker::mojom::ChromeColorPtr>
+ colors_arg) { colors = std::move(colors_arg); }));
+ handler().GetChromeColors(false, false, callback.Get());
+
+ ASSERT_EQ(kCustomizeChromeColors.size(), colors.size());
+ for (size_t i = 0; i < kCustomizeChromeColors.size(); i++) {
+ EXPECT_EQ(l10n_util::GetStringUTF8(kCustomizeChromeColors[i].label_id),
+ colors[i]->name);
+ EXPECT_EQ(kCustomizeChromeColors[i].color, colors[i]->seed);
+ EXPECT_EQ(GetAutogeneratedThemeColors(kCustomizeChromeColors[i].color)
+ .active_tab_color,
+ colors[i]->background);
+ EXPECT_EQ(GetAutogeneratedThemeColors(kCustomizeChromeColors[i].color)
+ .frame_color,
+ colors[i]->foreground);
+ }
+}
+
+TEST_F(ThemeColorPickerHandlerTest, GetChromeColorsGM3) {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kChromeRefresh2023, features::kChromeWebuiRefresh2023}, {});
+ std::vector<theme_color_picker::mojom::ChromeColorPtr> colors;
+ base::MockCallback<ThemeColorPickerHandler::GetChromeColorsCallback> callback;
+ EXPECT_CALL(callback, Run(testing::_))
+ .Times(2)
+ .WillRepeatedly(testing::Invoke(
+ [&colors](std::vector<theme_color_picker::mojom::ChromeColorPtr>
+ colors_arg) { colors = std::move(colors_arg); }));
+
+ // Test with Dark Mode on.
+ handler().GetChromeColors(true, false, callback.Get());
+
+ ASSERT_EQ(kDynamicCustomizeChromeColors.size(), colors.size());
+ for (size_t i = 0; i < kDynamicCustomizeChromeColors.size(); i++) {
+ auto palette = ui::GeneratePalette(
+ kDynamicCustomizeChromeColors[i].color,
+ GetSchemeVariant(kDynamicCustomizeChromeColors[i].variant));
+ EXPECT_EQ(
+ l10n_util::GetStringUTF8(kDynamicCustomizeChromeColors[i].label_id),
+ colors[i]->name);
+ EXPECT_EQ(kDynamicCustomizeChromeColors[i].color, colors[i]->seed);
+ EXPECT_EQ(palette->primary().get(80), colors[i]->background);
+ EXPECT_EQ(palette->primary().get(30), colors[i]->foreground);
+ EXPECT_EQ(palette->secondary().get(50), colors[i]->base);
+ }
+
+ // Test with Dark Mode off.
+ handler().GetChromeColors(false, false, callback.Get());
+
+ ASSERT_EQ(kDynamicCustomizeChromeColors.size(), colors.size());
+ for (size_t i = 0; i < kDynamicCustomizeChromeColors.size(); i++) {
+ auto palette = ui::GeneratePalette(
+ kDynamicCustomizeChromeColors[i].color,
+ GetSchemeVariant(kDynamicCustomizeChromeColors[i].variant));
+ EXPECT_EQ(
+ l10n_util::GetStringUTF8(kDynamicCustomizeChromeColors[i].label_id),
+ colors[i]->name);
+ EXPECT_EQ(kDynamicCustomizeChromeColors[i].color, colors[i]->seed);
+ EXPECT_EQ(palette->primary().get(40), colors[i]->background);
+ EXPECT_EQ(palette->primary().get(90), colors[i]->foreground);
+ EXPECT_EQ(palette->primary().get(80), colors[i]->base);
+ }
+}
+
+enum class ThemeUpdateSource {
+ kMojo,
+ kThemeService,
+ kNativeTheme,
+ kCustomBackgroundService,
+};
+
+class ThemeColorPickerHandlerSetThemeTest
+ : public ThemeColorPickerHandlerTest,
+ public ::testing::WithParamInterface<ThemeUpdateSource> {
+ public:
+ void UpdateTheme() {
+ switch (GetParam()) {
+ case ThemeUpdateSource::kMojo:
+ handler().UpdateTheme();
+ break;
+ case ThemeUpdateSource::kThemeService:
+ mock_theme_service().NotifyThemeChanged();
+ break;
+ case ThemeUpdateSource::kNativeTheme:
+ ui::NativeTheme::GetInstanceForNativeUi()->NotifyOnNativeThemeUpdated();
+ break;
+ case ThemeUpdateSource::kCustomBackgroundService:
+ ntp_custom_background_service_observer()
+ .OnCustomBackgroundImageUpdated();
+ break;
+ }
+ }
+};
+
+TEST_P(ThemeColorPickerHandlerSetThemeTest, SetTheme) {
+ theme_color_picker::mojom::ThemePtr theme;
+ EXPECT_CALL(mock_client_, SetTheme)
+ .Times(1)
+ .WillOnce(
+ testing::Invoke([&theme](theme_color_picker::mojom::ThemePtr arg) {
+ theme = std::move(arg);
+ }));
+ CustomBackground custom_background;
+ custom_background.custom_background_url = GURL("https://foo.com/img.png");
+ custom_background.custom_background_attribution_line_1 = "foo line";
+ custom_background.is_uploaded_image = false;
+ custom_background.custom_background_main_color = SK_ColorGREEN;
+ custom_background.collection_id = "test_collection";
+ custom_background.daily_refresh_enabled = false;
+ ON_CALL(mock_ntp_custom_background_service_, GetCustomBackground())
+ .WillByDefault(testing::Return(absl::make_optional(custom_background)));
+ ON_CALL(mock_theme_service(), GetAutogeneratedThemeColor())
+ .WillByDefault(testing::Return(SK_ColorBLUE));
+ ON_CALL(mock_theme_service(), GetUserColor())
+ .WillByDefault(testing::Return(absl::optional<SkColor>()));
+ ON_CALL(mock_theme_service(), UsingDefaultTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), UsingSystemTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), UsingPolicyTheme())
+ .WillByDefault(testing::Return(true));
+ ON_CALL(mock_theme_service(), GetIsGrayscale())
+ .WillByDefault(testing::Return(true));
+ ON_CALL(mock_theme_service(), UsingDeviceTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), GetBrowserColorVariant())
+ .WillByDefault(testing::Return(ui::mojom::BrowserColorVariant::kNeutral));
+ ui::NativeTheme::GetInstanceForNativeUi()->set_use_dark_colors(true);
+
+ UpdateTheme();
+ mock_client_.FlushForTesting();
+
+ ASSERT_TRUE(theme);
+ ASSERT_TRUE(theme->has_background_image);
+ EXPECT_EQ(SK_ColorGREEN,
+ theme->background_image_main_color.value_or(SK_ColorWHITE));
+ EXPECT_TRUE(theme->is_dark_mode);
+ EXPECT_EQ(SK_ColorBLUE, theme->seed_color);
+ EXPECT_EQ(
+ web_contents().GetColorProvider().GetColor(kColorNewTabPageBackground),
+ theme->background_color);
+ EXPECT_EQ(web_contents().GetColorProvider().GetColor(ui::kColorFrameActive),
+ theme->foreground_color);
+ EXPECT_EQ(web_contents().GetColorProvider().GetColor(kColorNewTabPageText),
+ theme->color_picker_icon_color);
+ EXPECT_TRUE(theme->colors_managed_by_policy);
+ EXPECT_TRUE(theme->is_grey_baseline);
+ EXPECT_EQ(theme->browser_color_variant,
+ ui::mojom::BrowserColorVariant::kNeutral);
+ EXPECT_FALSE(theme->follow_device_theme);
+}
+
+TEST_P(ThemeColorPickerHandlerSetThemeTest, SetThemeColorSchemeGM3) {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kChromeRefresh2023, features::kChromeWebuiRefresh2023}, {});
+ theme_color_picker::mojom::ThemePtr theme;
+ EXPECT_CALL(mock_client_, SetTheme)
+ .Times(2)
+ .WillRepeatedly(
+ testing::Invoke([&theme](theme_color_picker::mojom::ThemePtr arg) {
+ theme = std::move(arg);
+ }));
+
+ // Set mocks needed for UpdateTheme.
+ CustomBackground custom_background;
+ custom_background.custom_background_url = GURL("https://foo.com/img.png");
+ custom_background.custom_background_attribution_line_1 = "foo line";
+ custom_background.is_uploaded_image = false;
+ custom_background.custom_background_main_color = SK_ColorGREEN;
+ custom_background.collection_id = "test_collection";
+ custom_background.daily_refresh_enabled = false;
+ ON_CALL(mock_ntp_custom_background_service_, GetCustomBackground())
+ .WillByDefault(testing::Return(absl::make_optional(custom_background)));
+ ON_CALL(mock_theme_service(), GetAutogeneratedThemeColor())
+ .WillByDefault(testing::Return(SK_ColorBLUE));
+ ON_CALL(mock_theme_service(), GetUserColor())
+ .WillByDefault(testing::Return(SK_ColorGREEN));
+ ON_CALL(mock_theme_service(), UsingDefaultTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), UsingSystemTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), UsingPolicyTheme())
+ .WillByDefault(testing::Return(true));
+ ON_CALL(mock_theme_service(), GetIsGrayscale())
+ .WillByDefault(testing::Return(false));
+
+ // Set BrowserColorScheme to System and system to dark mode.
+ ON_CALL(mock_theme_service(), GetBrowserColorScheme())
+ .WillByDefault(
+ testing::Return(ThemeService::BrowserColorScheme::kSystem));
+ ui::NativeTheme::GetInstanceForNativeUi()->set_use_dark_colors(true);
+
+ UpdateTheme();
+ mock_client_.FlushForTesting();
+
+ // // Theme should be dark to match the system.
+ EXPECT_TRUE(theme->is_dark_mode);
+
+ // Set BrowserColorScheme to Light and leave system still on dark mode.
+ ON_CALL(mock_theme_service(), GetBrowserColorScheme())
+ .WillByDefault(testing::Return(ThemeService::BrowserColorScheme::kLight));
+
+ UpdateTheme();
+ mock_client_.FlushForTesting();
+
+ EXPECT_FALSE(theme->is_dark_mode);
+}
+
+TEST_P(ThemeColorPickerHandlerSetThemeTest, UsingDeviceThemeGM3) {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kChromeRefresh2023, features::kChromeWebuiRefresh2023}, {});
+ theme_color_picker::mojom::ThemePtr theme;
+ EXPECT_CALL(mock_client_, SetTheme)
+ .Times(1)
+ .WillRepeatedly(
+ testing::Invoke([&theme](theme_color_picker::mojom::ThemePtr arg) {
+ theme = std::move(arg);
+ }));
+
+ // Set mocks needed for UpdateTheme.
+ CustomBackground custom_background;
+ custom_background.custom_background_url = GURL("https://foo.com/img.png");
+ custom_background.custom_background_attribution_line_1 = "foo line";
+ custom_background.is_uploaded_image = false;
+ custom_background.custom_background_main_color = SK_ColorGREEN;
+ custom_background.collection_id = "test_collection";
+ custom_background.daily_refresh_enabled = false;
+ ON_CALL(mock_ntp_custom_background_service_, GetCustomBackground())
+ .WillByDefault(testing::Return(absl::make_optional(custom_background)));
+ ON_CALL(mock_theme_service(), UsingDefaultTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), UsingSystemTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), UsingPolicyTheme())
+ .WillByDefault(testing::Return(true));
+ ON_CALL(mock_theme_service(), GetIsGrayscale())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), GetBrowserColorScheme())
+ .WillByDefault(testing::Return(ThemeService::BrowserColorScheme::kLight));
+
+ // User color should be ignored as UsingDeviceTheme() will override it.
+ ON_CALL(mock_theme_service(), GetUserColor())
+ .WillByDefault(testing::Return(SK_ColorGREEN));
+ ON_CALL(mock_theme_service(), UsingDeviceTheme())
+ .WillByDefault(testing::Return(true));
+ ui::NativeTheme::GetInstanceForNativeUi()->set_user_color(SK_ColorBLUE);
+
+ UpdateTheme();
+ mock_client_.FlushForTesting();
+
+ EXPECT_EQ(SK_ColorBLUE, theme->seed_color);
+}
+
+TEST_P(ThemeColorPickerHandlerSetThemeTest, SetThemeWithDailyRefresh) {
+ theme_color_picker::mojom::ThemePtr theme;
+ EXPECT_CALL(mock_client_, SetTheme)
+ .Times(1)
+ .WillOnce(
+ testing::Invoke([&theme](theme_color_picker::mojom::ThemePtr arg) {
+ theme = std::move(arg);
+ }));
+ CustomBackground custom_background;
+ custom_background.custom_background_url = GURL("https://foo.com/img.png");
+ custom_background.daily_refresh_enabled = true;
+ custom_background.collection_id = "test_collection";
+ ON_CALL(mock_ntp_custom_background_service_, GetCustomBackground())
+ .WillByDefault(testing::Return(absl::make_optional(custom_background)));
+
+ UpdateTheme();
+ mock_client_.FlushForTesting();
+
+ ASSERT_TRUE(theme);
+ ASSERT_TRUE(theme->has_background_image);
+}
+
+TEST_P(ThemeColorPickerHandlerSetThemeTest, SetUploadedImage) {
+ theme_color_picker::mojom::ThemePtr theme;
+ EXPECT_CALL(mock_client_, SetTheme)
+ .Times(1)
+ .WillOnce(
+ testing::Invoke([&theme](theme_color_picker::mojom::ThemePtr arg) {
+ theme = std::move(arg);
+ }));
+ CustomBackground custom_background;
+ custom_background.custom_background_url = GURL("https://foo.com/img.png");
+ custom_background.is_uploaded_image = true;
+ ON_CALL(mock_ntp_custom_background_service_, GetCustomBackground())
+ .WillByDefault(testing::Return(absl::make_optional(custom_background)));
+ ON_CALL(mock_theme_service(), UsingDefaultTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), UsingSystemTheme())
+ .WillByDefault(testing::Return(false));
+
+ UpdateTheme();
+ mock_client_.FlushForTesting();
+
+ ASSERT_TRUE(theme);
+ ASSERT_TRUE(theme->has_background_image);
+}
+
+TEST_P(ThemeColorPickerHandlerSetThemeTest, SetThirdPartyTheme) {
+ theme_color_picker::mojom::ThemePtr theme;
+ EXPECT_CALL(mock_client_, SetTheme)
+ .Times(1)
+ .WillOnce(
+ testing::Invoke([&theme](theme_color_picker::mojom::ThemePtr arg) {
+ theme = std::move(arg);
+ }));
+ CustomBackground custom_background;
+ custom_background.custom_background_url = GURL("https://foo.com/img.png");
+
+ auto* extension_registry = extensions::ExtensionRegistry::Get(profile_.get());
+ scoped_refptr<const extensions::Extension> extension;
+ extension = extensions::ExtensionBuilder()
+ .SetManifest(base::Value::Dict()
+ .Set("name", "Foo Extension")
+ .Set("version", "1.0.0")
+ .Set("manifest_version", 2))
+ .SetID("foo")
+ .Build();
+ extension_registry->AddEnabled(extension);
+
+ ON_CALL(mock_ntp_custom_background_service_, GetCustomBackground())
+ .WillByDefault(testing::Return(absl::make_optional(custom_background)));
+ ON_CALL(mock_theme_service(), UsingDefaultTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), UsingExtensionTheme())
+ .WillByDefault(testing::Return(true));
+ ON_CALL(mock_theme_service(), UsingSystemTheme())
+ .WillByDefault(testing::Return(false));
+ ON_CALL(mock_theme_service(), GetThemeID())
+ .WillByDefault(testing::Return("foo"));
+
+ UpdateTheme();
+ mock_client_.FlushForTesting();
+
+ ASSERT_TRUE(theme);
+ ASSERT_TRUE(theme->has_background_image);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ ThemeColorPickerHandlerSetThemeTest,
+ ::testing::Values(ThemeUpdateSource::kMojo,
+ ThemeUpdateSource::kThemeService,
+ ThemeUpdateSource::kNativeTheme,
+ ThemeUpdateSource::kCustomBackgroundService));
+
+TEST_F(ThemeColorPickerHandlerTest, SetDefaultColor) {
+ EXPECT_CALL(mock_theme_service(), UseDefaultTheme).Times(1);
+
+ handler().SetDefaultColor();
+}
+
+TEST_F(ThemeColorPickerHandlerTest, SetDefaultColorGM3) {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kChromeRefresh2023, features::kChromeWebuiRefresh2023}, {});
+ absl::optional<SkColor> color;
+ EXPECT_CALL(mock_theme_service(), SetUserColor)
+ .Times(1)
+ .WillOnce(testing::SaveArg<0>(&color));
+ EXPECT_CALL(mock_theme_service(), UseDeviceTheme(false)).Times(1);
+
+ handler().SetDefaultColor();
+
+ EXPECT_EQ(SK_ColorTRANSPARENT, color.value_or(SK_ColorWHITE));
+}
+
+TEST_F(ThemeColorPickerHandlerTest, SetGreyDefaultColor) {
+ bool is_grey;
+ EXPECT_CALL(mock_theme_service(), SetIsGrayscale)
+ .Times(1)
+ .WillOnce(testing::SaveArg<0>(&is_grey));
+
+ handler().SetGreyDefaultColor();
+ EXPECT_TRUE(is_grey);
+}
+
+TEST_F(ThemeColorPickerHandlerTest, SetGreyDefaultColorGM3) {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kChromeRefresh2023, features::kChromeWebuiRefresh2023}, {});
+ bool is_grey;
+ EXPECT_CALL(mock_theme_service(), SetIsGrayscale)
+ .Times(1)
+ .WillOnce(testing::SaveArg<0>(&is_grey));
+ EXPECT_CALL(mock_theme_service(), UseDeviceTheme(false)).Times(1);
+
+ handler().SetGreyDefaultColor();
+ EXPECT_TRUE(is_grey);
+}
+
+TEST_F(ThemeColorPickerHandlerTest, SetSeedColor) {
+ SkColor color = SK_ColorWHITE;
+ EXPECT_CALL(mock_theme_service(), BuildAutogeneratedThemeFromColor)
+ .Times(1)
+ .WillOnce(testing::SaveArg<0>(&color));
+
+ handler().SetSeedColor(SK_ColorBLUE,
+ ui::mojom::BrowserColorVariant::kTonalSpot);
+
+ EXPECT_EQ(SK_ColorBLUE, color);
+}
+
+TEST_F(ThemeColorPickerHandlerTest, SetSeedColorGM3) {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kChromeRefresh2023, features::kChromeWebuiRefresh2023}, {});
+ absl::optional<SkColor> color;
+ ui::mojom::BrowserColorVariant variant;
+ EXPECT_CALL(mock_theme_service(), SetUserColorAndBrowserColorVariant)
+ .Times(1)
+ .WillOnce(testing::DoAll(testing::SaveArg<0>(&color),
+ testing::SaveArg<1>(&variant)));
+ EXPECT_CALL(mock_theme_service(), UseDeviceTheme(false)).Times(1);
+
+ handler().SetSeedColor(SK_ColorBLUE,
+ ui::mojom::BrowserColorVariant::kTonalSpot);
+
+ EXPECT_EQ(SK_ColorBLUE, color.value_or(SK_ColorWHITE));
+ EXPECT_EQ(ui::mojom::BrowserColorVariant::kTonalSpot, variant);
+}
+
+TEST_F(ThemeColorPickerHandlerTest, RemoveBackgroundImage) {
+ EXPECT_CALL(mock_ntp_custom_background_service_, ResetCustomBackgroundInfo)
+ .Times(1);
+
+ handler().RemoveBackgroundImage();
+}
diff --git a/chromium/chrome/browser/ui/webui/device_log_ui.cc b/chromium/chrome/browser/ui/webui/device_log_ui.cc
index caa508dbb35..67f006446b9 100644
--- a/chromium/chrome/browser/ui/webui/device_log_ui.cc
+++ b/chromium/chrome/browser/ui/webui/device_log_ui.cc
@@ -133,6 +133,7 @@ DeviceLogUI::DeviceLogUI(content::WebUI* web_ui)
{"logTypeCameraText", IDS_DEVICE_LOG_TYPE_CAMERA},
{"logTypeGeolocationText", IDS_DEVICE_LOG_TYPE_GEOLOCATION},
{"logTypeExtensionsText", IDS_DEVICE_LOG_TYPE_EXTENSIONS},
+ {"logTypeDisplayText", IDS_DEVICE_LOG_TYPE_DISPLAY},
{"logEntryFormat", IDS_DEVICE_LOG_ENTRY},
};
html->AddLocalizedStrings(kStrings);
diff --git a/chromium/chrome/browser/ui/webui/discards/graph_dump_impl.h b/chromium/chrome/browser/ui/webui/discards/graph_dump_impl.h
index 07d14af0a0f..8305cbbcaa2 100644
--- a/chromium/chrome/browser/ui/webui/discards/graph_dump_impl.h
+++ b/chromium/chrome/browser/ui/webui/discards/graph_dump_impl.h
@@ -133,6 +133,9 @@ class DiscardsGraphDumpImpl : public discards::mojom::GraphDump,
void OnTypeChanged(const performance_manager::PageNode* page_node,
performance_manager::PageType previous_type) override {}
// Ignored.
+ void OnIsFocusedChanged(
+ const performance_manager::PageNode* page_node) override {}
+ // Ignored.
void OnIsVisibleChanged(
const performance_manager::PageNode* page_node) override {}
// Ignored.
diff --git a/chromium/chrome/browser/ui/webui/discards/graph_dump_impl_unittest.cc b/chromium/chrome/browser/ui/webui/discards/graph_dump_impl_unittest.cc
index ceac761a9ab..7efd3658f35 100644
--- a/chromium/chrome/browser/ui/webui/discards/graph_dump_impl_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/discards/graph_dump_impl_unittest.cc
@@ -225,11 +225,16 @@ TEST_F(DiscardsGraphDumpImplTest, ChangeStream) {
task_environment.RunUntilIdle();
// Validate that the initial graph state dump is complete. Note that there is
- // an update for each node as part of the initial state dump.
- EXPECT_EQ(8u, change_stream.num_changes());
- EXPECT_EQ(8u, change_stream.id_set().size());
-
- EXPECT_EQ(2u, change_stream.process_map().size());
+ // an update for each node as part of the initial state dump, except the
+ // system node.
+ size_t expected_changes =
+ graph_.GetAllFrameNodes().size() + graph_.GetAllPageNodes().size() +
+ graph_.GetAllProcessNodes().size() + graph_.GetAllWorkerNodes().size();
+ EXPECT_EQ(expected_changes, change_stream.num_changes());
+ EXPECT_EQ(expected_changes, change_stream.id_set().size());
+
+ EXPECT_EQ(graph_.GetAllProcessNodes().size(),
+ change_stream.process_map().size());
for (const auto& kv : change_stream.process_map()) {
const auto* process_info = kv.second.get();
EXPECT_NE(0u, process_info->id);
@@ -237,12 +242,13 @@ TEST_F(DiscardsGraphDumpImplTest, ChangeStream) {
base::JSONReader::Read(process_info->description_json));
}
- EXPECT_EQ(3u, change_stream.frame_map().size());
+ EXPECT_EQ(graph_.GetAllFrameNodes().size(), change_stream.frame_map().size());
for (const auto& kv : change_stream.frame_map()) {
EXPECT_EQ(base::JSONReader::Read("{\"test\":{\"type\":\"frame\"}}"),
base::JSONReader::Read(kv.second->description_json));
}
- EXPECT_EQ(1u, change_stream.worker_map().size());
+ EXPECT_EQ(graph_.GetAllWorkerNodes().size(),
+ change_stream.worker_map().size());
for (const auto& kv : change_stream.worker_map()) {
EXPECT_EQ(base::JSONReader::Read("{\"test\":{\"type\":\"worker\"}}"),
base::JSONReader::Read(kv.second->description_json));
@@ -269,7 +275,7 @@ TEST_F(DiscardsGraphDumpImplTest, ChangeStream) {
// Make sure we have one top-level frame per page.
EXPECT_EQ(change_stream.page_map().size(), top_level_frames);
- EXPECT_EQ(2u, change_stream.page_map().size());
+ EXPECT_EQ(graph_.GetAllPageNodes().size(), change_stream.page_map().size());
for (const auto& kv : change_stream.page_map()) {
const auto& page = kv.second;
EXPECT_NE(0u, page->id);
@@ -290,7 +296,8 @@ TEST_F(DiscardsGraphDumpImplTest, ChangeStream) {
task_environment.RunUntilIdle();
// Main frame navigation results in a notification for the url.
- EXPECT_EQ(9u, change_stream.num_changes());
+ expected_changes += 1;
+ EXPECT_EQ(expected_changes, change_stream.num_changes());
EXPECT_FALSE(base::Contains(change_stream.id_set(), child_frame_id));
const auto main_page_it = change_stream.page_map().find(
diff --git a/chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc b/chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc
index cab6e34ec75..564cab88415 100644
--- a/chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc
+++ b/chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc
@@ -12,9 +12,12 @@
#include "chrome/browser/profiles/profile_key.h"
#include "components/download/public/background_service/background_download_service.h"
#include "components/download/public/background_service/download_params.h"
+#include "components/policy/content/policy_blocklist_service.h"
#include "content/public/browser/web_ui.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
+using policy::URLBlocklist;
+
namespace download_internals {
DownloadInternalsUIMessageHandler::DownloadInternalsUIMessageHandler()
@@ -113,6 +116,16 @@ void DownloadInternalsUIMessageHandler::HandleStartDownload(
return;
}
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PolicyBlocklistService* service =
+ PolicyBlocklistFactory::GetForBrowserContext(profile);
+ URLBlocklist::URLBlocklistState blocklist_state =
+ service->GetURLBlocklistState(url);
+ if (blocklist_state == URLBlocklist::URLBlocklistState::URL_IN_BLOCKLIST) {
+ LOG(WARNING) << "URL is blocked by a policy.";
+ return;
+ }
+
download::DownloadParams params;
params.guid = base::Uuid::GenerateRandomV4().AsLowercaseString();
params.client = download::DownloadClient::DEBUGGING;
diff --git a/chromium/chrome/browser/ui/webui/downloads/BUILD.gn b/chromium/chrome/browser/ui/webui/downloads/BUILD.gn
index 0bb8b1ccb65..0c3ab417392 100644
--- a/chromium/chrome/browser/ui/webui/downloads/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/downloads/BUILD.gn
@@ -10,4 +10,9 @@ mojom("mojo_bindings") {
sources = [ "downloads.mojom" ]
webui_module_path = "/"
use_typescript_sources = true
+
+ public_deps = [
+ "//mojo/public/mojom/base",
+ "//url/mojom:url_mojom_gurl",
+ ]
}
diff --git a/chromium/chrome/browser/ui/webui/downloads/downloads.mojom b/chromium/chrome/browser/ui/webui/downloads/downloads.mojom
index 721e8f4d049..a6868f772c8 100644
--- a/chromium/chrome/browser/ui/webui/downloads/downloads.mojom
+++ b/chromium/chrome/browser/ui/webui/downloads/downloads.mojom
@@ -4,6 +4,9 @@
module downloads.mojom;
+import "mojo/public/mojom/base/string16.mojom";
+import "url/mojom/url.mojom";
+
// This is the information associated with a download used for rendering in the
// UI.
struct Data {
@@ -38,7 +41,13 @@ struct Data {
// confirmation, the state will indicate the download is in progress, complete
// or cancelled.
string state;
- string url;
+ // The URL of the download item. Present if this field is populated with the
+ // original URL. Omitted if the URL should not be clickable on the downloads
+ // page, due to URL size limits requiring truncation.
+ url.mojom.Url? url;
+ // The display string for the URL in the UI. May be truncated/elided from the
+ // original URL, and IDN domains may be converted to Unicode.
+ mojo_base.mojom.String16 display_url;
};
interface PageHandlerFactory {
diff --git a/chromium/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc b/chromium/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc
index 4345d5a2551..29055559821 100644
--- a/chromium/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc
+++ b/chromium/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc
@@ -23,6 +23,7 @@
#include "base/threading/thread.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/bubble/download_bubble_ui_controller.h"
#include "chrome/browser/download/download_danger_prompt.h"
#include "chrome/browser/download/download_history.h"
#include "chrome/browser/download/download_item_model.h"
@@ -30,9 +31,12 @@
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_query.h"
#include "chrome/browser/download/drag_download_item.h"
+#include "chrome/browser/download/offline_item_utils.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/webui/downloads/downloads.mojom.h"
#include "chrome/browser/ui/webui/fileicon_source.h"
#include "chrome/common/chrome_switches.h"
@@ -89,6 +93,19 @@ void CountDownloadsDOMEvents(DownloadsDOMEvent event) {
DOWNLOADS_DOM_EVENT_MAX);
}
+void PromptForScanningInBubble(content::WebContents* web_contents,
+ download::DownloadItem* download) {
+ Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
+ if (!browser) {
+ return;
+ }
+ browser->window()
+ ->GetDownloadBubbleUIController()
+ ->GetDownloadDisplayController()
+ ->OpenSecuritySubpage(
+ OfflineItemUtils::GetContentIdForDownload(download));
+}
+
} // namespace
DownloadsDOMHandler::DownloadsDOMHandler(
@@ -422,7 +439,17 @@ void DownloadsDOMHandler::OpenDuringScanningRequiringGesture(
void DownloadsDOMHandler::DeepScan(const std::string& id) {
CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DEEP_SCAN);
download::DownloadItem* download = GetDownloadByStringId(id);
- if (download) {
+ if (!download) {
+ return;
+ }
+
+ if (base::FeatureList::IsEnabled(
+ safe_browsing::kDeepScanningEncryptedArchives) &&
+ DownloadItemWarningData::IsEncryptedArchive(download)) {
+ // For encrypted archives, we need a password from the user. We will request
+ // this in the download bubble.
+ PromptForScanningInBubble(GetWebUIWebContents(), download);
+ } else {
DownloadItemModel model(download);
DownloadCommands commands(model.GetWeakPtr());
commands.ExecuteCommand(DownloadCommands::DEEP_SCAN);
diff --git a/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.cc b/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.cc
index ef0d2a4ab2f..ba71bc32dbd 100644
--- a/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.cc
+++ b/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.cc
@@ -14,6 +14,7 @@
#include "base/functional/callback_helpers.h"
#include "base/i18n/rtl.h"
#include "base/i18n/unicodestring.h"
+#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
@@ -27,6 +28,7 @@
#include "components/download/public/common/download_danger_type.h"
#include "components/download/public/common/download_item.h"
#include "components/safe_browsing/core/common/features.h"
+#include "components/url_formatter/elide_url.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/download_manager.h"
@@ -36,6 +38,7 @@
#include "net/base/filename_util.h"
#include "third_party/icu/source/i18n/unicode/datefmt.h"
#include "ui/base/l10n/time_format.h"
+#include "url/url_constants.h"
using content::BrowserContext;
using content::DownloadManager;
@@ -45,9 +48,6 @@ using DownloadVector = DownloadManager::DownloadVector;
namespace {
-// Max URL length to be sent to the download page.
-const int kMaxURLLength = 2 * 1024 * 1024;
-
// Returns a string constant to be used as the |danger_type| value in
// CreateDownloadData(). This can be the empty string, if the danger type is not
// relevant for the UI.
@@ -107,6 +107,17 @@ std::string TimeFormatLongDate(const base::Time& time) {
return base::UTF16ToUTF8(base::i18n::UnicodeStringToString16(date_string));
}
+std::u16string GetFormattedDisplayUrl(const GURL& url) {
+ std::u16string result = url_formatter::FormatUrlForSecurityDisplay(url);
+ // Truncate long URL to avoid surpassing mojo data limit (c.f.
+ // crbug.com/1070451). If it's really this long, the user won't be able to see
+ // the end of it anyway.
+ if (result.size() > url::kMaxURLChars) {
+ result.resize(url::kMaxURLChars);
+ }
+ return result;
+}
+
} // namespace
DownloadsListTracker::DownloadsListTracker(
@@ -233,7 +244,10 @@ downloads::mojom::DataPtr DownloadsListTracker::CreateDownloadData(
base::FilePath download_path(download_item->GetTargetFilePath());
file_value->file_path = download_path.AsUTF8Unsafe();
- file_value->file_url = net::FilePathToFileURL(download_path).spec();
+ GURL file_url = net::FilePathToFileURL(download_path);
+ if (file_url.is_valid()) {
+ file_value->file_url = file_url.spec();
+ }
extensions::DownloadedByExtension* by_ext =
extensions::DownloadedByExtension::Get(download_item);
@@ -264,10 +278,12 @@ downloads::mojom::DataPtr DownloadsListTracker::CreateDownloadData(
file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
file_value->file_name = base::UTF16ToUTF8(file_name);
- file_value->url = download_item->GetURL().spec();
- // If URL is too long, truncate it.
- if (file_value->url.size() > kMaxURLLength)
- file_value->url.resize(kMaxURLLength);
+ // If URL is too long, don't make it clickable.
+ if (download_item->GetURL().is_valid() &&
+ download_item->GetURL().spec().length() <= url::kMaxURLChars) {
+ file_value->url = absl::make_optional<GURL>(download_item->GetURL());
+ }
+ file_value->display_url = GetFormattedDisplayUrl(download_item->GetURL());
file_value->total = static_cast<int>(download_item->GetTotalBytes());
file_value->file_externally_removed =
download_item->GetFileExternallyRemoved();
diff --git a/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.h b/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.h
index 2de77ad41ed..79c97183f43 100644
--- a/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.h
+++ b/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker.h
@@ -12,6 +12,7 @@
#include <string>
#include "base/functional/callback_forward.h"
+#include "base/gtest_prod_util.h"
#include "chrome/browser/ui/webui/downloads/downloads.mojom.h"
#include "components/download/content/public/all_download_item_notifier.h"
#include "components/download/public/common/download_item.h"
@@ -81,6 +82,13 @@ class DownloadsListTracker
void SetChunkSizeForTesting(size_t chunk_size);
private:
+ FRIEND_TEST_ALL_PREFIXES(DownloadsListTrackerTest,
+ CreateDownloadData_UrlFormatting_OmitUserPass);
+ FRIEND_TEST_ALL_PREFIXES(DownloadsListTrackerTest,
+ CreateDownloadData_UrlFormatting_Idn);
+ FRIEND_TEST_ALL_PREFIXES(DownloadsListTrackerTest,
+ CreateDownloadData_UrlFormatting_VeryLong);
+
struct StartTimeComparator {
bool operator()(const download::DownloadItem* a,
const download::DownloadItem* b) const;
diff --git a/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker_unittest.cc b/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker_unittest.cc
index 722245b504d..8d8adf54e36 100644
--- a/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/downloads/downloads_list_tracker_unittest.cc
@@ -33,6 +33,7 @@ using download::MockDownloadItem;
using DownloadVector = std::vector<DownloadItem*>;
using testing::_;
using testing::Return;
+using testing::ReturnRefOfCopy;
namespace {
@@ -90,6 +91,9 @@ class DownloadsListTrackerTest : public testing::Test {
ON_CALL(*new_item, GetId()).WillByDefault(Return(id));
ON_CALL(*new_item, GetStartTime()).WillByDefault(Return(started));
ON_CALL(*new_item, IsTransient()).WillByDefault(Return(false));
+ ON_CALL(*new_item, GetTargetFilePath())
+ .WillByDefault(
+ ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo.txt"))));
return new_item;
}
@@ -314,3 +318,48 @@ TEST_F(DownloadsListTrackerTest, IgnoreTransientDownloads) {
std::vector<uint64_t> expected;
EXPECT_CALL(page_, InsertItems(0, MatchIds(expected)));
}
+
+TEST_F(DownloadsListTrackerTest,
+ CreateDownloadData_UrlFormatting_OmitUserPass) {
+ MockDownloadItem* item = CreateNextItem();
+ ON_CALL(*item, GetURL())
+ .WillByDefault(ReturnRefOfCopy(GURL("https://user:pass@example.test")));
+
+ auto tracker = std::make_unique<DownloadsListTracker>(
+ manager(), page_.BindAndGetRemote());
+
+ downloads::mojom::DataPtr data = tracker->CreateDownloadData(item);
+ EXPECT_TRUE(data->url);
+ EXPECT_EQ(*data->url, "https://user:pass@example.test/");
+ EXPECT_EQ(data->display_url, u"https://example.test");
+}
+
+TEST_F(DownloadsListTrackerTest, CreateDownloadData_UrlFormatting_Idn) {
+ MockDownloadItem* item = CreateNextItem();
+ ON_CALL(*item, GetURL())
+ .WillByDefault(ReturnRefOfCopy(GURL("https://xn--6qqa088eba.test")));
+
+ auto tracker = std::make_unique<DownloadsListTracker>(
+ manager(), page_.BindAndGetRemote());
+
+ downloads::mojom::DataPtr data = tracker->CreateDownloadData(item);
+ EXPECT_TRUE(data->url);
+ EXPECT_EQ(*data->url, "https://xn--6qqa088eba.test/");
+ EXPECT_EQ(data->display_url, u"https://\u4f60\u597d\u4f60\u597d.test");
+}
+
+TEST_F(DownloadsListTrackerTest, CreateDownloadData_UrlFormatting_VeryLong) {
+ std::string url = "https://" + std::string(2 * 1024 * 1024, 'a') + ".test";
+ std::u16string expected =
+ u"https://" + std::u16string(2 * 1024 * 1024 - 8, 'a');
+
+ MockDownloadItem* item = CreateNextItem();
+ ON_CALL(*item, GetURL()).WillByDefault(ReturnRefOfCopy(GURL(url)));
+
+ auto tracker = std::make_unique<DownloadsListTracker>(
+ manager(), page_.BindAndGetRemote());
+
+ downloads::mojom::DataPtr data = tracker->CreateDownloadData(item);
+ EXPECT_FALSE(data->url);
+ EXPECT_EQ(data->display_url, expected);
+}
diff --git a/chromium/chrome/browser/ui/webui/downloads/downloads_ui.cc b/chromium/chrome/browser/ui/webui/downloads/downloads_ui.cc
index adf8f9b3fdf..e56ad5469d6 100644
--- a/chromium/chrome/browser/ui/webui/downloads/downloads_ui.cc
+++ b/chromium/chrome/browser/ui/webui/downloads/downloads_ui.cc
@@ -37,6 +37,7 @@
#include "components/history/core/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/profile_metrics/browser_profile_type.h"
+#include "components/safe_browsing/core/common/features.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
@@ -98,6 +99,7 @@ content::WebUIDataSource* CreateAndAddDownloadsUIHTMLSource(Profile* profile) {
// Deep scanning strings.
{"deepScannedSafeDesc", IDS_DEEP_SCANNED_SAFE_DESCRIPTION},
+ {"deepScannedFailedDesc", IDS_DEEP_SCANNED_FAILED_DESCRIPTION},
{"deepScannedOpenedDangerousDesc",
IDS_DEEP_SCANNED_OPENED_DANGEROUS_DESCRIPTION},
{"sensitiveContentWarningDesc",
@@ -107,7 +109,6 @@ content::WebUIDataSource* CreateAndAddDownloadsUIHTMLSource(Profile* profile) {
{"blockedTooLargeDesc", IDS_BLOCKED_TOO_LARGE_DESCRIPTION},
{"blockedPasswordProtectedDesc",
IDS_BLOCKED_PASSWORD_PROTECTED_DESCRIPTION},
- {"promptForScanningDesc", IDS_BLOCK_REASON_PROMPT_FOR_SCANNING},
// Controls.
{"controlPause", IDS_DOWNLOAD_LINK_PAUSE},
@@ -118,8 +119,7 @@ content::WebUIDataSource* CreateAndAddDownloadsUIHTMLSource(Profile* profile) {
{"controlRetry", IDS_DOWNLOAD_LINK_RETRY},
{"controlledByUrl", IDS_DOWNLOAD_BY_EXTENSION_URL},
{"controlOpenNow", IDS_OPEN_DOWNLOAD_NOW},
- {"controlDeepScan", IDS_DOWNLOAD_DEEP_SCAN},
- {"controlBypassDeepScan", IDS_DOWNLOAD_BYPASS_DEEP_SCAN},
+ {"controlOpenAnyway", IDS_OPEN_DOWNLOAD_ANYWAY},
{"toastClearedAll", IDS_DOWNLOAD_TOAST_CLEARED_ALL},
{"toastRemovedFromList", IDS_DOWNLOAD_TOAST_REMOVED_FROM_LIST},
{"undo", IDS_DOWNLOAD_UNDO},
@@ -137,12 +137,37 @@ content::WebUIDataSource* CreateAndAddDownloadsUIHTMLSource(Profile* profile) {
IDS_BLOCK_REASON_UNWANTED_DOWNLOAD);
source->AddLocalizedString("insecureDownloadDesc",
IDS_BLOCK_REASON_INSECURE_DOWNLOAD);
- source->AddLocalizedString("asyncScanningDownloadDesc",
- IDS_BLOCK_REASON_DEEP_SCANNING);
source->AddLocalizedString("accountCompromiseDownloadDesc",
IDS_BLOCK_REASON_ACCOUNT_COMPROMISE);
- source->AddBoolean("hasShowInFolder",
- browser_defaults::kDownloadPageHasShowInFolder);
+
+ bool update_deep_scanning_ux =
+ base::FeatureList::IsEnabled(safe_browsing::kDeepScanningUpdatedUX);
+ source->AddLocalizedString("asyncScanningDownloadDesc",
+ update_deep_scanning_ux
+ ? IDS_BLOCK_REASON_DEEP_SCANNING_UPDATED
+ : IDS_BLOCK_REASON_DEEP_SCANNING);
+ source->AddLocalizedString("asyncScanningDownloadDescSecond",
+ IDS_BLOCK_REASON_DEEP_SCANNING_SECOND_UPDATED);
+ source->AddLocalizedString("promptForScanningDesc",
+ update_deep_scanning_ux
+ ? IDS_BLOCK_REASON_PROMPT_FOR_SCANNING_UPDATED
+ : IDS_BLOCK_REASON_PROMPT_FOR_SCANNING);
+ source->AddLocalizedString("controlDeepScan",
+ update_deep_scanning_ux
+ ? IDS_DOWNLOAD_DEEP_SCAN_UPDATED
+ : IDS_DOWNLOAD_DEEP_SCAN);
+ source->AddLocalizedString("controlBypassDeepScan",
+ update_deep_scanning_ux
+ ? IDS_DOWNLOAD_BYPASS_DEEP_SCAN_UPDATED
+ : IDS_DOWNLOAD_BYPASS_DEEP_SCAN);
+ source->AddBoolean("updateDeepScanningUX", update_deep_scanning_ux);
+
+ // New chrome://downloads icons, colors, strings, etc. to be consistent with
+ // download bubble.
+ bool improved_download_warnings_ux = base::FeatureList::IsEnabled(
+ safe_browsing::kImprovedDownloadPageWarnings);
+ source->AddBoolean("improvedDownloadWarningsUX",
+ improved_download_warnings_ux);
// Build an Accelerator to describe undo shortcut
// NOTE: the undo shortcut is also defined in downloads/downloads.html
diff --git a/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc b/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
index c120525b336..b792b305449 100644
--- a/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
+++ b/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
@@ -13,6 +13,7 @@
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/engagement_resources.h"
#include "chrome/grit/engagement_resources_map.h"
@@ -98,12 +99,9 @@ SiteEngagementUI::SiteEngagementUI(content::WebUI* web_ui)
// Set up the chrome://site-engagement/ source.
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
Profile::FromWebUI(web_ui), chrome::kChromeUISiteEngagementHost);
- source->OverrideContentSecurityPolicy(
- network::mojom::CSPDirectiveName::ScriptSrc,
- "script-src chrome://resources chrome://webui-test 'self';");
- source->AddResourcePaths(
- base::make_span(kEngagementResources, kEngagementResourcesSize));
- source->SetDefaultResource(IDR_ENGAGEMENT_SITE_ENGAGEMENT_HTML);
+ webui::SetupWebUIDataSource(
+ source, base::make_span(kEngagementResources, kEngagementResourcesSize),
+ IDR_ENGAGEMENT_SITE_ENGAGEMENT_HTML);
}
WEB_UI_CONTROLLER_TYPE_IMPL(SiteEngagementUI)
diff --git a/chromium/chrome/browser/ui/webui/extension_control_handler.cc b/chromium/chrome/browser/ui/webui/extension_control_handler.cc
index 502079c8e08..bf906a9cf18 100644
--- a/chromium/chrome/browser/ui/webui/extension_control_handler.cc
+++ b/chromium/chrome/browser/ui/webui/extension_control_handler.cc
@@ -13,6 +13,13 @@
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_system.h"
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/strings/strcat.h"
+#include "chrome/browser/ash/crosapi/browser_manager.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/common/webui_url_constants.h"
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
ExtensionControlHandler::ExtensionControlHandler() = default;
ExtensionControlHandler::~ExtensionControlHandler() = default;
@@ -21,10 +28,19 @@ void ExtensionControlHandler::RegisterMessages() {
"disableExtension",
base::BindRepeating(&ExtensionControlHandler::HandleDisableExtension,
base::Unretained(this)));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ web_ui()->RegisterMessageCallback(
+ "openExtensionPageInLacros",
+ base::BindRepeating(
+ &ExtensionControlHandler::HandleOpenExtensionPageInLacros,
+ base::Unretained(this)));
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
void ExtensionControlHandler::HandleDisableExtension(
const base::Value::List& args) {
+ CHECK_EQ(args.size(), 1u);
const std::string& extension_id = args[0].GetString();
extensions::ExtensionService* extension_service =
extensions::ExtensionSystem::Get(Profile::FromWebUI(web_ui()))
@@ -33,3 +49,17 @@ void ExtensionControlHandler::HandleDisableExtension(
extension_service->DisableExtension(
extension_id, extensions::disable_reason::DISABLE_USER_ACTION);
}
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+void ExtensionControlHandler::HandleOpenExtensionPageInLacros(
+ const base::Value::List& args) {
+ CHECK_EQ(args.size(), 1u);
+ const std::string& extension_id = args[0].GetString();
+ GURL url(
+ base::StrCat({chrome::kChromeUIExtensionsURL, "?id=", extension_id}));
+ CHECK(url.is_valid());
+
+ crosapi::BrowserManager::Get()->SwitchToTab(
+ url, NavigateParams::PathBehavior::RESPECT);
+}
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chromium/chrome/browser/ui/webui/extension_control_handler.h b/chromium/chrome/browser/ui/webui/extension_control_handler.h
index 39d544f177e..2c345ed6cb3 100644
--- a/chromium/chrome/browser/ui/webui/extension_control_handler.h
+++ b/chromium/chrome/browser/ui/webui/extension_control_handler.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_UI_WEBUI_EXTENSION_CONTROL_HANDLER_H_
#define CHROME_BROWSER_UI_WEBUI_EXTENSION_CONTROL_HANDLER_H_
+#include "build/chromeos_buildflags.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
// A class that provides a message handler that disables extensions, intended
@@ -25,6 +26,12 @@ class ExtensionControlHandler : public content::WebUIMessageHandler {
// Handler for the "disableExtension" message. Extension ID is passed as the
// single string argument.
void HandleDisableExtension(const base::Value::List& args);
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // Handler for the "openExtensionPageInLacros" message. Extension ID is passed
+ // as the single string argument.
+ void HandleOpenExtensionPageInLacros(const base::Value::List& args);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
};
#endif // CHROME_BROWSER_UI_WEBUI_EXTENSION_CONTROL_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
index 6f577625ef6..e9c50fd81cb 100644
--- a/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/extensions/extension_settings_browsertest.h"
-
#include <string>
#include "base/command_line.h"
@@ -15,113 +13,22 @@
#include "chrome/browser/extensions/activity_log/activity_log.h"
#include "chrome/browser/extensions/api/developer_private/developer_private_api.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
-#include "chrome/browser/extensions/unpacked_installer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/extensions/extension_settings_test_base.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
#include "chrome/test/base/ui_test_utils.h"
-#include "components/prefs/pref_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
-#include "extensions/browser/extension_dialog_auto_confirm.h"
-#include "extensions/browser/extension_system.h"
#include "extensions/test/extension_test_message_listener.h"
-using extensions::Extension;
-using extensions::TestManagementPolicyProvider;
-
-ExtensionSettingsUIBrowserTest::ExtensionSettingsUIBrowserTest()
- : policy_provider_(TestManagementPolicyProvider::PROHIBIT_MODIFY_STATUS |
- TestManagementPolicyProvider::MUST_REMAIN_ENABLED |
- TestManagementPolicyProvider::MUST_REMAIN_INSTALLED),
- test_data_dir_(base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
- .AppendASCII("extensions")) {}
-
-ExtensionSettingsUIBrowserTest::~ExtensionSettingsUIBrowserTest() {}
-
-void ExtensionSettingsUIBrowserTest::InstallGoodExtension() {
- EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("good.crx")));
-}
-
-void ExtensionSettingsUIBrowserTest::InstallErrorsExtension() {
- EXPECT_TRUE(
- InstallExtension(test_data_dir_.AppendASCII("error_console")
- .AppendASCII("runtime_and_manifest_errors")));
- EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("error_console")
- .AppendASCII("deep_stack_trace")));
-}
-
-void ExtensionSettingsUIBrowserTest::InstallSharedModule() {
- base::FilePath shared_module_path =
- test_data_dir_.AppendASCII("api_test").AppendASCII("shared_module");
- EXPECT_TRUE(InstallExtension(shared_module_path.AppendASCII("shared")));
- EXPECT_TRUE(InstallExtension(shared_module_path.AppendASCII("import_pass")));
-}
-
-void ExtensionSettingsUIBrowserTest::InstallPackagedApp() {
- EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("packaged_app")));
-}
-
-void ExtensionSettingsUIBrowserTest::InstallHostedApp() {
- EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("hosted_app")));
-}
-
-void ExtensionSettingsUIBrowserTest::InstallPlatformApp() {
- EXPECT_TRUE(InstallExtension(
- test_data_dir_.AppendASCII("platform_apps").AppendASCII("minimal")));
-}
-
-const extensions::Extension*
-ExtensionSettingsUIBrowserTest::InstallExtensionWithInPageOptions() {
- const extensions::Extension* extension =
- InstallExtension(test_data_dir_.AppendASCII("options_page_in_view"));
- EXPECT_TRUE(extension);
- return extension;
-}
-
-void ExtensionSettingsUIBrowserTest::AddManagedPolicyProvider() {
- extensions::ExtensionSystem* extension_system =
- extensions::ExtensionSystem::Get(browser()->profile());
- extension_system->management_policy()->RegisterProvider(&policy_provider_);
-}
-
-void ExtensionSettingsUIBrowserTest::SetAutoConfirmUninstall() {
- uninstall_auto_confirm_ =
- std::make_unique<extensions::ScopedTestDialogAutoConfirm>(
- extensions::ScopedTestDialogAutoConfirm::ACCEPT);
-}
-
-void ExtensionSettingsUIBrowserTest::SetDevModeEnabled(bool enabled) {
- browser()->profile()->GetPrefs()->SetBoolean(
- prefs::kExtensionsUIDeveloperMode, enabled);
-}
-
-void ExtensionSettingsUIBrowserTest::ShrinkWebContentsView() {
- content::WebContents* web_contents =
- browser()->tab_strip_model()->GetActiveWebContents();
- CHECK(web_contents);
- web_contents->Resize(gfx::Rect(0, 0, 400, 400));
-}
-
-void ExtensionSettingsUIBrowserTest::
- SetSilenceDeprecatedManifestVersionWarnings(bool silence) {
- Extension::set_silence_deprecated_manifest_version_warnings_for_testing(
- silence);
-}
-
-const Extension* ExtensionSettingsUIBrowserTest::InstallExtension(
- const base::FilePath& path) {
- extensions::ChromeTestExtensionLoader loader(browser()->profile());
- loader.set_ignore_manifest_warnings(true);
- return loader.LoadExtension(path).get();
-}
+using ExtensionSettingsUIBrowserTest = ExtensionSettingsTestBase;
// Tests that viewing a source of the options page works fine.
// This is a regression test for https://crbug.com/796080.
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_test_base.cc b/chromium/chrome/browser/ui/webui/extensions/extension_settings_test_base.cc
new file mode 100644
index 00000000000..380bd5b5819
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_test_base.cc
@@ -0,0 +1,89 @@
+// Copyright 2013 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/extensions/extension_settings_test_base.h"
+
+#include <string>
+
+#include "base/path_service.h"
+#include "chrome/browser/extensions/api/developer_private/developer_private_api.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/pref_names.h"
+#include "extensions/browser/extension_dialog_auto_confirm.h"
+
+using extensions::Extension;
+
+ExtensionSettingsTestBase::ExtensionSettingsTestBase()
+ : test_data_dir_(base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
+ .AppendASCII("extensions")) {}
+
+ExtensionSettingsTestBase::~ExtensionSettingsTestBase() = default;
+
+void ExtensionSettingsTestBase::InstallGoodExtension() {
+ EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("good.crx")));
+}
+
+void ExtensionSettingsTestBase::InstallErrorsExtension() {
+ EXPECT_TRUE(
+ InstallExtension(test_data_dir_.AppendASCII("error_console")
+ .AppendASCII("runtime_and_manifest_errors")));
+ EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("error_console")
+ .AppendASCII("deep_stack_trace")));
+}
+
+void ExtensionSettingsTestBase::InstallSharedModule() {
+ base::FilePath shared_module_path =
+ test_data_dir_.AppendASCII("api_test").AppendASCII("shared_module");
+ EXPECT_TRUE(InstallExtension(shared_module_path.AppendASCII("shared")));
+ EXPECT_TRUE(InstallExtension(shared_module_path.AppendASCII("import_pass")));
+}
+
+void ExtensionSettingsTestBase::InstallPackagedApp() {
+ EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("packaged_app")));
+}
+
+void ExtensionSettingsTestBase::InstallHostedApp() {
+ EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("hosted_app")));
+}
+
+void ExtensionSettingsTestBase::InstallPlatformApp() {
+ EXPECT_TRUE(InstallExtension(
+ test_data_dir_.AppendASCII("platform_apps").AppendASCII("minimal")));
+}
+
+const extensions::Extension*
+ExtensionSettingsTestBase::InstallExtensionWithInPageOptions() {
+ const extensions::Extension* extension =
+ InstallExtension(test_data_dir_.AppendASCII("options_page_in_view"));
+ EXPECT_TRUE(extension);
+ return extension;
+}
+
+void ExtensionSettingsTestBase::SetAutoConfirmUninstall() {
+ uninstall_auto_confirm_ =
+ std::make_unique<extensions::ScopedTestDialogAutoConfirm>(
+ extensions::ScopedTestDialogAutoConfirm::ACCEPT);
+}
+
+void ExtensionSettingsTestBase::SetDevModeEnabled(bool enabled) {
+ browser()->profile()->GetPrefs()->SetBoolean(
+ prefs::kExtensionsUIDeveloperMode, enabled);
+}
+
+void ExtensionSettingsTestBase::SetSilenceDeprecatedManifestVersionWarnings(
+ bool silence) {
+ Extension::set_silence_deprecated_manifest_version_warnings_for_testing(
+ silence);
+}
+
+const Extension* ExtensionSettingsTestBase::InstallExtension(
+ const base::FilePath& path) {
+ extensions::ChromeTestExtensionLoader loader(browser()->profile());
+ loader.set_ignore_manifest_warnings(true);
+ return loader.LoadExtension(path).get();
+}
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h b/chromium/chrome/browser/ui/webui/extensions/extension_settings_test_base.h
index 11beab3d18e..00d1ff8e3b0 100644
--- a/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_test_base.h
@@ -2,33 +2,32 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_BROWSERTEST_H_
-#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_BROWSERTEST_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_TEST_BASE_H_
+#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_TEST_BASE_H_
#include <memory>
#include "base/files/file_path.h"
#include "chrome/browser/extensions/install_verifier.h"
-#include "chrome/test/base/web_ui_browser_test.h"
+#include "chrome/test/base/web_ui_mocha_browser_test.h"
#include "extensions/browser/scoped_ignore_content_verifier_for_test.h"
-#include "extensions/browser/test_management_policy.h"
namespace extensions {
class Extension;
class ScopedTestDialogAutoConfirm;
-}
+} // namespace extensions
-// C++ test fixture used by extension_settings_browsertest.js.
-class ExtensionSettingsUIBrowserTest : public WebUIBrowserTest {
+// C++ base class used by Extensions tests in chrome/test/data/webui/extensions/
+// and chrome/browser/ui/webui/extensions/.
+class ExtensionSettingsTestBase : public WebUIMochaBrowserTest {
public:
- ExtensionSettingsUIBrowserTest();
+ ExtensionSettingsTestBase();
- ExtensionSettingsUIBrowserTest(const ExtensionSettingsUIBrowserTest&) =
+ ExtensionSettingsTestBase(const ExtensionSettingsTestBase&) = delete;
+ ExtensionSettingsTestBase& operator=(const ExtensionSettingsTestBase&) =
delete;
- ExtensionSettingsUIBrowserTest& operator=(
- const ExtensionSettingsUIBrowserTest&) = delete;
- ~ExtensionSettingsUIBrowserTest() override;
+ ~ExtensionSettingsTestBase() override;
protected:
void InstallGoodExtension();
@@ -47,19 +46,11 @@ class ExtensionSettingsUIBrowserTest : public WebUIBrowserTest {
// and returns it back to the caller. Can return null upon failure.
const extensions::Extension* InstallExtensionWithInPageOptions();
- void AddManagedPolicyProvider();
-
void SetAutoConfirmUninstall();
- // Enables the error console so errors are displayed in the extensions page.
- void EnableErrorConsole();
-
// Sets the DevMode status for the current profile.
void SetDevModeEnabled(bool enabled);
- // Shrinks the web contents view in order to ensure vertical overflow.
- void ShrinkWebContentsView();
-
// Sets whether to ignore errors for deprecated manifest versions.
void SetSilenceDeprecatedManifestVersionWarnings(bool silence);
@@ -68,9 +59,6 @@ class ExtensionSettingsUIBrowserTest : public WebUIBrowserTest {
private:
const extensions::Extension* InstallExtension(const base::FilePath& path);
- // Used to simulate managed extensions (by being registered as a provider).
- extensions::TestManagementPolicyProvider policy_provider_;
-
const base::FilePath test_data_dir_;
// Disable extension content verification.
@@ -83,4 +71,4 @@ class ExtensionSettingsUIBrowserTest : public WebUIBrowserTest {
uninstall_auto_confirm_;
};
-#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_BROWSERTEST_H_
+#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_TEST_BASE_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/extensions_internals_source.cc b/chromium/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
index 8e13da2837e..c1c606baead 100644
--- a/chromium/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
+++ b/chromium/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
@@ -6,9 +6,9 @@
#include <memory>
#include <string>
-#include <unordered_map>
#include <utility>
+#include "base/containers/flat_map.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
@@ -199,14 +199,23 @@ base::Value::List DisableReasonsToList(int disable_reasons) {
// The JSON we generate looks like this:
// Note:
// - tab_specific permissions can have 0 or more DICT entries with each tab id
-// pointing to the api, explicit_host, manifest and scriptable_host permission
-// lists.
+// pointing to the api, explicit_host, manifest and scriptable_host permission
+// lists.
// - In some cases manifest or api permissions rather than just being a STRING
-// can be a DICT with the name keying a more complex object with detailed
-// information. This is the case for subclasses of ManifestPermission and
-// APIPermission which override the ToValue function.
+// can be a DICT with the name keying a more complex object with detailed
+// information. This is the case for subclasses of ManifestPermission and
+// APIPermission which override the ToValue function.
+// - "background_page_keepalives" and "service_worker_keepalives" are mutually
+// exclusive.
//
// [ {
+// "background_page_keepalives": {
+// "activities": [ {
+// "extra_data": "render-frame",
+// "type": "PROCESS_MANAGER"
+// } ],
+// "count": 1
+// },
// "creation_flags": [ "ALLOW_FILE_ACCESS", "FROM_WEBSTORE" ],
// "disable_reasons": ["DISABLE_USER_ACTION"],
// "event_listeners": {
@@ -218,13 +227,6 @@ base::Value::List DisableReasonsToList(int disable_reasons) {
// } ]
// },
// "id": "bhloflhklmhfpedakmangadcdofhnnoh",
-// "keepalive": {
-// "activities": [ {
-// "extra_data": "render-frame",
-// "type": "PROCESS_MANAGER"
-// } ],
-// "count": 1
-// },
// "location": "INTERNAL",
// "manifest_version": 2,
// "name": "Earth View from Google Earth",
@@ -256,6 +258,14 @@ base::Value::List DisableReasonsToList(int disable_reasons) {
// "manifest": [ ],
// "scriptable_hosts": [ ]
// },
+// "service_worker_keepalives": {
+// "activities": [ {
+// "extra_data": "tabs.create",
+// "timeout_type": "Default",
+// "type": "API_FUNCTION"
+// } ]
+// "count": 1
+// }
// "type": "TYPE_EXTENSION",
// "version": "2.18.5"
// } ]
@@ -264,6 +274,12 @@ base::Value::List DisableReasonsToList(int disable_reasons) {
//
// LIST
// DICT
+// "background_page_keepalives": DICT
+// "activities": LIST
+// DICT
+// "extra_data": STRING
+// "type": STRING
+// "count": INT
// "creation_flags": LIST
// STRING
// "disable_reasons": LIST
@@ -278,12 +294,6 @@ base::Value::List DisableReasonsToList(int disable_reasons) {
// "is_lazy": STRING
// "url": STRING
// "id": STRING
-// "keepalive": DICT
-// "activities": LIST
-// DICT
-// "extra_data": STRING
-// "type": STRING
-// "count": INT
// "location": STRING
// "manifest_version": INT
// "name": STRING
@@ -319,10 +329,19 @@ base::Value::List DisableReasonsToList(int disable_reasons) {
// STRING
// "scriptable_hosts": LIST
// STRING
+// "service_worker_keepalies": DICT
+// "activities": LIST
+// DICT
+// "extra_data": STRING
+// "timeout_type": STRING,
+// "type": STRING
+// "count": INT
// "type": STRING
// "version": STRING
constexpr base::StringPiece kActivitesKey = "activites";
+constexpr base::StringPiece kBackgroundPageKeepalivesKey =
+ "background_page_keepalives";
constexpr base::StringPiece kCountKey = "count";
constexpr base::StringPiece kEventNameKey = "event_name";
constexpr base::StringPiece kEventsListenersKey = "event_listeners";
@@ -336,7 +355,6 @@ constexpr base::StringPiece kInternalsVersionKey = "version";
constexpr base::StringPiece kIsForServiceWorkerKey = "is_for_service_worker";
constexpr base::StringPiece kIsLazyKey = "is_lazy";
constexpr base::StringPiece kListenersKey = "listeners";
-constexpr base::StringPiece kKeepaliveKey = "keepalive";
constexpr base::StringPiece kListenerUrlKey = "url";
constexpr base::StringPiece kLocationKey = "location";
constexpr base::StringPiece kManifestVersionKey = "manifest_version";
@@ -350,9 +368,12 @@ constexpr base::StringPiece kPermissionsApiKey = "api";
constexpr base::StringPiece kPermissionsManifestKey = "manifest";
constexpr base::StringPiece kPermissionsExplicitHostsKey = "explicit_hosts";
constexpr base::StringPiece kPermissionsScriptableHostsKey = "scriptable_hosts";
+constexpr base::StringPiece kServiceWorkerKeepalivesKey =
+ "service_worker_keepalives";
+constexpr base::StringPiece kTimeoutTypeKey = "timeout_type";
constexpr base::StringPiece kTypeKey = "type";
-base::Value::Dict FormatKeepaliveData(
+base::Value::Dict FormatBackgroundPageKeepaliveData(
extensions::ProcessManager* process_manager,
const extensions::Extension* extension) {
base::Value::Dict keepalive_data;
@@ -372,6 +393,39 @@ base::Value::Dict FormatKeepaliveData(
return keepalive_data;
}
+base::Value::Dict FormatServiceWorkerKeepaliveData(
+ extensions::ProcessManager& process_manager,
+ const extensions::ExtensionId& extension_id) {
+ base::Value::Dict keepalive_data;
+ auto keepalives =
+ process_manager.GetServiceWorkerKeepaliveDataForRecords(extension_id);
+ keepalive_data.Set(kCountKey, base::checked_cast<int>(keepalives.size()));
+ base::Value::List activities_data;
+
+ auto get_timeout_type_value =
+ [](content::ServiceWorkerExternalRequestTimeoutType timeout_type) {
+ switch (timeout_type) {
+ case content::ServiceWorkerExternalRequestTimeoutType::kDefault:
+ return "Default";
+ case content::ServiceWorkerExternalRequestTimeoutType::
+ kDoesNotTimeout:
+ return "Does Not Timeout";
+ }
+ };
+
+ for (const auto& keepalive : keepalives) {
+ base::Value::Dict activities_entry;
+ activities_entry.Set(
+ kTypeKey, extensions::Activity::ToString(keepalive.activity_type));
+ activities_entry.Set(kExtraDataKey, keepalive.extra_data);
+ activities_entry.Set(kTimeoutTypeKey,
+ get_timeout_type_value(keepalive.timeout_type));
+ activities_data.Append(std::move(activities_entry));
+ }
+ keepalive_data.Set(kActivitesKey, std::move(activities_data));
+ return keepalive_data;
+}
+
// Formats API and Manifest permissions, which can have details that we add as a
// dictionary rather than just the string name
template <typename T>
@@ -441,9 +495,7 @@ void AddEventListenerData(extensions::EventRouter* event_router,
base::Value::List* data) {
// A map of extension ID to the listener data for that extension,
// which is of type LIST of DICTIONARY.
- std::unordered_map<base::StringPiece, base::Value::List,
- base::StringPieceHash>
- listeners_map;
+ base::flat_map<base::StringPiece, base::Value::List> listeners_map;
// Build the map of extension IDs to the list of events.
for (const auto& entry : event_router->listeners().listeners()) {
@@ -526,8 +578,12 @@ std::string ExtensionsInternalsSource::WriteToString() const {
extension_data.Set(
kInternalsDisableReasonsKey,
DisableReasonsToList(prefs->GetDisableReasons(extension->id())));
- extension_data.Set(kKeepaliveKey,
- FormatKeepaliveData(process_manager, extension.get()));
+ extension_data.Set(
+ kBackgroundPageKeepalivesKey,
+ FormatBackgroundPageKeepaliveData(process_manager, extension.get()));
+ extension_data.Set(
+ kServiceWorkerKeepalivesKey,
+ FormatServiceWorkerKeepaliveData(*process_manager, extension->id()));
extension_data.Set(kLocationKey, LocationToString(extension->location()));
extension_data.Set(kManifestVersionKey, extension->manifest_version());
extension_data.Set(kInternalsNameKey, extension->name());
diff --git a/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc
index e7abdf55aba..cf6352455a0 100644
--- a/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -120,6 +120,12 @@ content::WebUIDataSource* CreateAndAddExtensionsSource(Profile* profile,
IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_CUSTOMIZE_PER_EXTENSION},
{"editSitePermissionsRestrictExtensions",
IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_RESTRICT_EXTENSIONS},
+ {"enableToggleTooltipDisabled",
+ IDS_EXTENSIONS_ENABLE_TOGGLE_TOOLTIP_DISABLED},
+ {"enableToggleTooltipEnabled",
+ IDS_EXTENSIONS_ENABLE_TOGGLE_TOOLTIP_ENABLED},
+ {"enableToggleTooltipEnabledWithSiteAccess",
+ IDS_EXTENSIONS_ENABLE_TOGGLE_TOOLTIP_ENABLED_WITH_SITE_ACCESS},
{"errorsPageHeading", IDS_EXTENSIONS_ERROR_PAGE_HEADING},
{"clearActivities", IDS_EXTENSIONS_CLEAR_ACTIVITIES},
{"clearAll", IDS_EXTENSIONS_ERROR_CLEAR_ALL},
@@ -130,9 +136,12 @@ content::WebUIDataSource* CreateAndAddExtensionsSource(Profile* profile,
{"anonymousFunction", IDS_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION},
{"errorContext", IDS_EXTENSIONS_ERROR_CONTEXT},
{"errorContextUnknown", IDS_EXTENSIONS_ERROR_CONTEXT_UNKNOWN},
+ {"safetyCheckExtensionsDetailPagePrimaryLabel",
+ IDS_EXTENSIONS_SAFETY_CHECK_PRIMARY_LABEL},
+ {"safetyCheckExtensionsKeep", IDS_CONFIRM_DOWNLOAD},
{"stackTrace", IDS_EXTENSIONS_ERROR_STACK_TRACE},
// TODO(dpapad): Unify with Settings' IDS_SETTINGS_WEB_STORE.
- {"openChromeWebStore", IDS_EXTENSIONS_SIDEBAR_OPEN_CHROME_WEB_STORE},
+ {"sidebarDiscoverMore", IDS_EXTENSIONS_SIDEBAR_DISCOVER_MORE},
{"keyboardShortcuts", IDS_EXTENSIONS_SIDEBAR_KEYBOARD_SHORTCUTS},
{"incognitoInfoWarning", IDS_EXTENSIONS_INCOGNITO_WARNING},
{"hostPermissionsDescription", IDS_EXTENSIONS_HOST_PERMISSIONS_DESCRIPTION},
@@ -210,6 +219,7 @@ content::WebUIDataSource* CreateAndAddExtensionsSource(Profile* profile,
{"itemPermissionsEmpty", IDS_EXTENSIONS_ITEM_PERMISSIONS_EMPTY},
{"itemPermissionsAndSiteAccessEmpty",
IDS_EXTENSIONS_ITEM_PERMISSIONS_AND_SITE_ACCESS_EMPTY},
+ {"itemPinToToolbar", IDS_EXTENSIONS_ITEM_PIN_TO_TOOLBAR},
{"itemRemoveExtension", IDS_EXTENSIONS_ITEM_REMOVE_EXTENSION},
{"itemShowAccessRequestsInToolbar",
IDS_EXTENSIONS_ITEM_SHOW_ACCESS_REQUESTS_IN_TOOLBAR},
@@ -345,7 +355,12 @@ content::WebUIDataSource* CreateAndAddExtensionsSource(Profile* profile,
{"viewServiceWorker", IDS_EXTENSIONS_SERVICE_WORKER_BACKGROUND},
{"safetyCheckKeepExtension", IDS_EXTENSIONS_SC_KEEP_EXT},
{"safetyCheckRemoveAll", IDS_EXTENSIONS_SC_REMOVE_ALL},
-
+ {"safetyCheckAllDoneForNow", IDS_EXTENSIONS_SC_ALL_DONE_FOR_NOW},
+ {"safetyCheckAllExtensions", IDS_EXTENSIONS_SC_ALL_EXTENSIONS},
+ {"safetyCheckRemoveButtonA11yLabel",
+ IDS_EXTENSIONS_SC_REMOVE_BUTTON_A11Y_LABEL},
+ {"safetyCheckOptionMenuA11yLabel",
+ IDS_EXTENSIONS_SC_OPTION_MENU_A11Y_LABEL},
#if BUILDFLAG(IS_CHROMEOS_ASH)
{"manageKioskApp", IDS_EXTENSIONS_MANAGE_KIOSK_APP},
{"kioskAddApp", IDS_EXTENSIONS_KIOSK_ADD_APP},
@@ -407,6 +422,10 @@ content::WebUIDataSource* CreateAndAddExtensionsSource(Profile* profile,
source->AddString(kLoadTimeClassesKey, GetLoadTimeClasses(in_dev_mode));
+ source->AddBoolean(
+ "safetyCheckExtensionsReviewEnabled",
+ base::FeatureList::IsEnabled(features::kSafetyCheckExtensions));
+
source->AddBoolean(kEnableEnhancedSiteControls,
base::FeatureList::IsEnabled(
extensions_features::kExtensionsMenuAccessControl));
diff --git a/chromium/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc b/chromium/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
index da4a994fd1d..e4484a873e6 100644
--- a/chromium/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
+++ b/chromium/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
@@ -14,15 +14,16 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_key.h"
#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/supervised_user/child_accounts/child_account_service.h"
#include "chrome/browser/supervised_user/supervised_user_browser_utils.h"
#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
#include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
#include "chrome/common/channel_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/tribool.h"
+#include "components/supervised_user/core/browser/child_account_service.h"
#include "components/supervised_user/core/browser/supervised_user_error_page.h"
#include "components/supervised_user/core/browser/supervised_user_settings_service.h"
+#include "components/supervised_user/core/common/features.h"
#include "components/supervised_user/core/common/supervised_user_utils.h"
#include "components/url_formatter/url_fixer.h"
#include "content/public/browser/browser_thread.h"
@@ -190,7 +191,7 @@ void FamilyLinkUserInternalsMessageHandler::SendBasicInfo() {
base::Value::List* section_general = AddSection(&section_list, "General");
AddSectionEntry(section_general, "Child detection enabled",
- ChildAccountService::IsChildAccountDetectionEnabled());
+ supervised_user::IsChildAccountSupervisionEnabled());
Profile* profile = Profile::FromWebUI(web_ui());
diff --git a/chromium/chrome/browser/ui/webui/feedback/feedback_handler.cc b/chromium/chrome/browser/ui/webui/feedback/feedback_handler.cc
index 8ee77efd87f..e5ef87b16cb 100644
--- a/chromium/chrome/browser/ui/webui/feedback/feedback_handler.cc
+++ b/chromium/chrome/browser/ui/webui/feedback/feedback_handler.cc
@@ -39,7 +39,7 @@ void ShowChildPage(Profile* profile,
ChildWebDialog* child_dialog = new ChildWebDialog(
profile, dialog->GetWidget(), url, title,
/*modal_type=*/
- isParentModal ? ui::MODAL_TYPE_WINDOW : ui::MODAL_TYPE_NONE, args,
+ isParentModal ? ui::MODAL_TYPE_SYSTEM : ui::MODAL_TYPE_NONE, args,
dialog_width, dialog_height, can_resize, can_minimize);
child_dialog->Show();
@@ -101,7 +101,7 @@ void FeedbackHandler::HandleShowBluetoothLogsInfo(
ShowChildPage(Profile::FromWebUI(web_ui()), dialog_,
ChildPageURL("html/bluetooth_logs_info.html"), std::u16string(),
std::string(),
- /*dialog_width=*/400, /*dialog_height=*/120,
+ /*dialog_width=*/400, /*dialog_height=*/190,
/*can_resize=*/false, /*can_minimize=*/false);
}
#endif // BUILDFLAG(IS_CHROMEOS)
diff --git a/chromium/chrome/browser/ui/webui/feedback/feedback_ui.cc b/chromium/chrome/browser/ui/webui/feedback/feedback_ui.cc
index da8a999efa1..d2fd6b0ada6 100644
--- a/chromium/chrome/browser/ui/webui/feedback/feedback_ui.cc
+++ b/chromium/chrome/browser/ui/webui/feedback/feedback_ui.cc
@@ -21,18 +21,16 @@
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#include "chrome/browser/ash/arc/arc_util.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
namespace {
// Jelly colors should only be considered enabled when jelly styling is
-// enabled for OS Feedback and the OS Feedback app is being used. Listener is
-// added here to enable correct coloring in child web dialogs when launched
-// from OS Feedback.
+// enabled for Feedback on ChromeOS.
bool IsJellyColorsEnabled() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
- return ash::features::IsJellyEnabledForOsFeedback() &&
- base::FeatureList::IsEnabled(ash::features::kOsFeedback);
+ return ash::features::IsJellyEnabledForOsFeedback();
#else
return false;
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -80,6 +78,8 @@ void AddStringResources(content::WebUIDataSource* source,
source->AddLocalizedStrings(kStrings);
#if BUILDFLAG(IS_CHROMEOS_ASH)
+ source->AddLocalizedString("mayBeSharedWithPartnerNote",
+ IDS_FEEDBACK_TOOL_MAY_BE_SHARED_NOTE);
source->AddLocalizedString(
"sysInfo",
arc::IsArcPlayStoreEnabledForProfile(profile)
diff --git a/chromium/chrome/browser/ui/webui/flags/flags_ui.cc b/chromium/chrome/browser/ui/webui/flags/flags_ui.cc
index a9c25185b21..521e47df783 100644
--- a/chromium/chrome/browser/ui/webui/flags/flags_ui.cc
+++ b/chromium/chrome/browser/ui/webui/flags/flags_ui.cc
@@ -68,10 +68,10 @@ content::WebUIDataSource* CreateAndAddFlagsUIHTMLSource(Profile* profile) {
source->EnableReplaceI18nInJS();
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::ScriptSrc,
- "script-src chrome://resources 'self' 'unsafe-eval';");
+ "script-src chrome://resources 'self';");
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::TrustedTypes,
- "trusted-types jstemplate static-types;");
+ "trusted-types static-types;");
source->AddString(flags_ui::kVersion,
std::string(version_info::GetVersionNumber()));
diff --git a/chromium/chrome/browser/ui/webui/flags/flags_ui_handler.cc b/chromium/chrome/browser/ui/webui/flags/flags_ui_handler.cc
index a192b9a4dc9..748e2011226 100644
--- a/chromium/chrome/browser/ui/webui/flags/flags_ui_handler.cc
+++ b/chromium/chrome/browser/ui/webui/flags/flags_ui_handler.cc
@@ -22,6 +22,23 @@
#include "chrome/browser/profiles/profile.h"
#endif
+namespace {
+bool ExtractKeyValue(const base::Value::List& args,
+ std::string& key,
+ std::string& value) {
+ if (args.size() != 2) {
+ return false;
+ }
+
+ if (!args[0].is_string() || !args[1].is_string()) {
+ return false;
+ }
+ key = args[0].GetString();
+ value = args[1].GetString();
+ return !key.empty();
+}
+} // namespace
+
FlagsUIHandler::FlagsUIHandler()
: access_(flags_ui::kGeneralAccessFlagsOnly),
experimental_features_callback_id_(""),
@@ -44,6 +61,10 @@ void FlagsUIHandler::RegisterMessages() {
base::BindRepeating(&FlagsUIHandler::HandleSetOriginListFlagMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ flags_ui::kSetStringFlag,
+ base::BindRepeating(&FlagsUIHandler::HandleSetStringFlagMessage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
flags_ui::kRestartBrowser,
base::BindRepeating(&FlagsUIHandler::HandleRestartBrowser,
base::Unretained(this)));
@@ -155,24 +176,26 @@ void FlagsUIHandler::HandleEnableExperimentalFeatureMessage(
void FlagsUIHandler::HandleSetOriginListFlagMessage(
const base::Value::List& args) {
DCHECK(flags_storage_);
- if (args.size() != 2) {
+ std::string entry_internal_name, value_str;
+ if (!ExtractKeyValue(args, entry_internal_name, value_str)) {
NOTREACHED();
return;
}
- if (!args[0].is_string() || !args[1].is_string()) {
- NOTREACHED();
- return;
- }
- const std::string& entry_internal_name = args[0].GetString();
- const std::string& value_str = args[1].GetString();
- if (entry_internal_name.empty()) {
+ about_flags::SetOriginListFlag(entry_internal_name, value_str,
+ flags_storage_.get());
+}
+
+void FlagsUIHandler::HandleSetStringFlagMessage(const base::Value::List& args) {
+ DCHECK(flags_storage_);
+ std::string entry_internal_name, value_str;
+ if (!ExtractKeyValue(args, entry_internal_name, value_str)) {
NOTREACHED();
return;
}
- about_flags::SetOriginListFlag(entry_internal_name, value_str,
- flags_storage_.get());
+ about_flags::SetStringFlag(entry_internal_name, value_str,
+ flags_storage_.get());
}
void FlagsUIHandler::HandleRestartBrowser(const base::Value::List& args) {
diff --git a/chromium/chrome/browser/ui/webui/flags/flags_ui_handler.h b/chromium/chrome/browser/ui/webui/flags/flags_ui_handler.h
index 2b0d7aa984d..0e4fc1d905d 100644
--- a/chromium/chrome/browser/ui/webui/flags/flags_ui_handler.h
+++ b/chromium/chrome/browser/ui/webui/flags/flags_ui_handler.h
@@ -52,6 +52,9 @@ class FlagsUIHandler : public content::WebUIMessageHandler {
// Callback for the "setOriginListFlag" message.
void HandleSetOriginListFlagMessage(const base::Value::List& args);
+ // Callback for the "setStringFlag" message.
+ void HandleSetStringFlagMessage(const base::Value::List& args);
+
// Callback for the "restartBrowser" message. Restores all tabs on restart.
void HandleRestartBrowser(const base::Value::List& args);
diff --git a/chromium/chrome/browser/ui/webui/flags/flags_ui_handler_unittest.cc b/chromium/chrome/browser/ui/webui/flags/flags_ui_handler_unittest.cc
new file mode 100644
index 00000000000..c1d086abdf8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/flags/flags_ui_handler_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/flags/flags_ui_handler.h"
+
+#include "base/test/task_environment.h"
+#include "components/flags_ui/flags_storage.h"
+#include "components/flags_ui/flags_ui_constants.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestFlagStorage : public flags_ui::FlagsStorage {
+ public:
+ // Retrieves the flags as a set of strings.
+ std::set<std::string> GetFlags() const override { return flags_; }
+ // Stores the |flags| and returns true on success.
+ bool SetFlags(const std::set<std::string>& flags) override {
+ flags_ = flags;
+ return true;
+ }
+
+ // Retrieves the serialized origin list corresponding to
+ // |internal_entry_name|. Does not check if the return value is well formed.
+ std::string GetOriginListFlag(
+ const std::string& internal_entry_name) const override {
+ if (origin_list_flags_.count(internal_entry_name) == 0) {
+ return "";
+ }
+ return origin_list_flags_.at(internal_entry_name);
+ };
+ // Sets the serialized |origin_list_value| corresponding to
+ // |internal_entry_name|. Does not check if |origin_list_value| is well
+ // formed.
+ void SetOriginListFlag(const std::string& internal_entry_name,
+ const std::string& origin_list_value) override {
+ origin_list_flags_[internal_entry_name] = origin_list_value;
+ };
+
+ std::string GetStringFlag(
+ const std::string& internal_entry_name) const override {
+ return GetOriginListFlag(internal_entry_name);
+ }
+ void SetStringFlag(const std::string& internal_entry_name,
+ const std::string& value) override {
+ SetOriginListFlag(internal_entry_name, value);
+ }
+
+ // Lands pending changes to disk immediately.
+ void CommitPendingWrites() override{};
+
+ private:
+ std::set<std::string> flags_;
+ std::map<std::string, std::string> origin_list_flags_;
+};
+
+class TestFlagsUIHandler : public FlagsUIHandler {
+ public:
+ // Make public for testing.
+ using FlagsUIHandler::set_web_ui;
+};
+
+class FlagsUIHandlerTest : public testing::Test {
+ public:
+ void SetUp() override {
+ std::unique_ptr<TestFlagStorage> storage =
+ std::make_unique<TestFlagStorage>();
+ storage_ = storage.get();
+
+ handler_ = std::make_unique<TestFlagsUIHandler>();
+ handler_->Init(std::move(storage),
+ flags_ui::FlagAccess::kOwnerAccessToFlags);
+ handler_->set_web_ui(&web_ui_);
+ handler_->RegisterMessages();
+ }
+
+ protected:
+ content::TestWebUI web_ui_;
+ std::unique_ptr<TestFlagsUIHandler> handler_;
+ raw_ptr<TestFlagStorage> storage_;
+};
+
+TEST_F(FlagsUIHandlerTest, HandlesSetString) {
+ // Need to use an actual feature name for ChromeOS.
+ const std::string kTestFeature = "protected-audience-debug-token";
+ EXPECT_EQ("", storage_->GetStringFlag(kTestFeature));
+
+ web_ui_.HandleReceivedMessage(
+ flags_ui::kSetStringFlag,
+ base::Value::List().Append(kTestFeature).Append("value"));
+ EXPECT_EQ("value", storage_->GetStringFlag(kTestFeature));
+}
+
+TEST_F(FlagsUIHandlerTest, HandlesSetOriginList) {
+ // Need to use an actual feature name for ChromeOS.
+ const std::string kTestFeature = "isolate-origins";
+ EXPECT_EQ("", storage_->GetOriginListFlag(kTestFeature));
+
+ web_ui_.HandleReceivedMessage(
+ flags_ui::kSetOriginListFlag,
+ base::Value::List()
+ .Append(kTestFeature)
+ .Append("https://foo.com,invalid,http://bar.org"));
+ EXPECT_EQ("https://foo.com,http://bar.org",
+ storage_->GetOriginListFlag(kTestFeature));
+}
+
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/hats/DIR_METADATA b/chromium/chrome/browser/ui/webui/hats/DIR_METADATA
new file mode 100644
index 00000000000..4b06f183eb4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/hats/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+ component: "UI>Browser>HaTS"
+}
diff --git a/chromium/chrome/browser/ui/webui/hats/OWNERS b/chromium/chrome/browser/ui/webui/hats/OWNERS
new file mode 100644
index 00000000000..bbdb9fdfe9a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/hats/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ui/hats/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/hats/hats_ui.cc b/chromium/chrome/browser/ui/webui/hats/hats_ui.cc
new file mode 100644
index 00000000000..73650866c52
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/hats/hats_ui.cc
@@ -0,0 +1,41 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/hats/hats_ui.h"
+
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/hats_resources.h"
+#include "chrome/grit/hats_resources_map.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+HatsUIConfig::HatsUIConfig()
+ : WebUIConfig(content::kChromeUIUntrustedScheme,
+ chrome::kChromeUIUntrustedHatsHost) {}
+
+bool HatsUIConfig::IsWebUIEnabled(content::BrowserContext* browser_context) {
+ return base::FeatureList::IsEnabled(features::kHaTSWebUI);
+}
+
+std::unique_ptr<content::WebUIController> HatsUIConfig::CreateWebUIController(
+ content::WebUI* web_ui,
+ const GURL& url) {
+ return std::make_unique<HatsUI>(web_ui);
+}
+
+HatsUI::HatsUI(content::WebUI* web_ui) : ui::UntrustedWebUIController(web_ui) {
+ content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+ web_ui->GetWebContents()->GetBrowserContext(),
+ chrome::kChromeUIUntrustedHatsURL);
+
+ // Add required resources.
+ webui::SetupWebUIDataSource(
+ source, base::make_span(kHatsResources, kHatsResourcesSize),
+ IDR_HATS_HATS_HTML);
+}
+
+WEB_UI_CONTROLLER_TYPE_IMPL(HatsUI)
diff --git a/chromium/chrome/browser/ui/webui/hats/hats_ui.h b/chromium/chrome/browser/ui/webui/hats/hats_ui.h
new file mode 100644
index 00000000000..95bca34e232
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/hats/hats_ui.h
@@ -0,0 +1,37 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_HATS_HATS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_HATS_HATS_UI_H_
+
+#include "content/public/browser/webui_config.h"
+#include "ui/webui/untrusted_web_ui_controller.h"
+
+// The configuration for the chrome-untrusted://hats page.
+class HatsUIConfig : public content::WebUIConfig {
+ public:
+ HatsUIConfig();
+ ~HatsUIConfig() override = default;
+
+ bool IsWebUIEnabled(content::BrowserContext* browser_context) override;
+
+ std::unique_ptr<content::WebUIController> CreateWebUIController(
+ content::WebUI* web_ui,
+ const GURL& url) override;
+};
+
+class HatsUI : public ui::UntrustedWebUIController {
+ public:
+ explicit HatsUI(content::WebUI* web_ui);
+
+ HatsUI(const HatsUI&) = delete;
+ HatsUI& operator=(const HatsUI&) = delete;
+
+ ~HatsUI() override = default;
+
+ private:
+ WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HATS_HATS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc
index 2e75591445b..160f2e55e88 100644
--- a/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -17,7 +17,7 @@
#include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
#include "chrome/browser/ash/settings/cros_settings.h"
#include "chrome/browser/browser_process.h"
-#include "chrome/browser/policy/management_utils.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
@@ -28,6 +28,7 @@
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/strings/grit/chromeos_strings.h"
+#include "components/policy/core/common/management/management_service.h"
#include "content/public/browser/web_contents.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
@@ -324,7 +325,7 @@ void VersionUpdaterCros::UpdateStatusChanged(
if (status.last_attempt_error() ==
static_cast<int32_t>(
update_engine::ErrorCode::kOmahaUpdateIgnoredPerPolicy)) {
- if (policy::IsDeviceEnterpriseManaged()) {
+ if (policy::ManagementServiceFactory::GetForPlatform()->IsManaged()) {
my_status = DISABLED_BY_ADMIN;
} else {
// Handle the special case where after a consumer rollback,
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc
index 4d04404b536..e1d987e9656 100644
--- a/chromium/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc
@@ -98,8 +98,9 @@ class VersionUpdaterCrosTest : public ::testing::Test {
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<ash::NetworkHandlerTestHelper> network_handler_test_helper_;
std::unique_ptr<VersionUpdater> version_updater_;
- raw_ptr<VersionUpdaterCros, ExperimentalAsh> version_updater_cros_ptr_;
- raw_ptr<ash::FakeUpdateEngineClient, ExperimentalAsh>
+ raw_ptr<VersionUpdaterCros, DanglingUntriaged | ExperimentalAsh>
+ version_updater_cros_ptr_;
+ raw_ptr<ash::FakeUpdateEngineClient, DanglingUntriaged | ExperimentalAsh>
fake_update_engine_client_; // Not owned.
user_manager::ScopedUserManager user_manager_enabler_;
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_mac.h b/chromium/chrome/browser/ui/webui/help/version_updater_mac.h
index feb1feda7d2..1ecbf52bd26 100644
--- a/chromium/chrome/browser/ui/webui/help/version_updater_mac.h
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_mac.h
@@ -9,7 +9,6 @@
#include <string.h>
-#include "base/mac/scoped_nsobject.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/webui/help/version_updater.h"
@@ -18,7 +17,7 @@
@class KeystoneObserver;
-// OS X implementation of version update functionality, used by the WebUI
+// macOS implementation of version update functionality, used by the WebUI
// About/Help page.
class VersionUpdaterMac : public VersionUpdater {
public:
@@ -61,8 +60,8 @@ class VersionUpdaterMac : public VersionUpdater {
// The visible state of the promote button.
bool show_promote_button_;
- // The observer that will receive keystone status updates.
- base::scoped_nsobject<KeystoneObserver> keystone_observer_;
+ // The observer that will receive Keystone status updates.
+ KeystoneObserver* __strong keystone_observer_;
base::WeakPtrFactory<VersionUpdaterMac> weak_factory_{this};
};
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_mac.mm b/chromium/chrome/browser/ui/webui/help/version_updater_mac.mm
index e54963a452e..ae390bdaa55 100644
--- a/chromium/chrome/browser/ui/webui/help/version_updater_mac.mm
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_mac.mm
@@ -4,21 +4,20 @@
#include "chrome/browser/ui/webui/help/version_updater_mac.h"
-#include "base/memory/raw_ptr.h"
-
#import <Foundation/Foundation.h>
#include <algorithm>
#include <string>
#include <utility>
+#include "base/apple/foundation_util.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/mac/authorization_util.h"
-#include "base/mac/foundation_util.h"
+#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/escape.h"
#include "base/strings/stringprintf.h"
@@ -42,10 +41,7 @@
// KeystoneObserver is a simple notification observer for Keystone status
// updates. It will be created and managed by VersionUpdaterMac.
-@interface KeystoneObserver : NSObject {
- @private
- raw_ptr<VersionUpdaterMac> _versionUpdater; // Weak.
-}
+@interface KeystoneObserver : NSObject
// Initialize an observer with an updater. The updater owns this object.
- (instancetype)initWithUpdater:(VersionUpdaterMac*)updater;
@@ -55,27 +51,28 @@
@end // @interface KeystoneObserver
-@implementation KeystoneObserver
+@implementation KeystoneObserver {
+ raw_ptr<VersionUpdaterMac> _versionUpdater; // Weak.
+}
- (instancetype)initWithUpdater:(VersionUpdaterMac*)updater {
if ((self = [super init])) {
_versionUpdater = updater;
- NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
- [center addObserver:self
- selector:@selector(handleStatusNotification:)
- name:kAutoupdateStatusNotification
- object:nil];
+ [NSNotificationCenter.defaultCenter
+ addObserver:self
+ selector:@selector(handleStatusNotification:)
+ name:kAutoupdateStatusNotification
+ object:nil];
}
return self;
}
- (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [super dealloc];
+ [NSNotificationCenter.defaultCenter removeObserver:self];
}
- (void)handleStatusNotification:(NSNotification*)notification {
- _versionUpdater->UpdateStatus([notification userInfo]);
+ _versionUpdater->UpdateStatus(notification.userInfo);
}
@end // @implementation KeystoneObserver
@@ -192,11 +189,11 @@ void VersionUpdaterMac::PromoteUpdater() {
}
void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
- AutoupdateStatus keystone_status = static_cast<AutoupdateStatus>(
- [base::mac::ObjCCastStrict<NSNumber>(dictionary[kAutoupdateStatusStatus])
- intValue]);
+ AutoupdateStatus keystone_status =
+ static_cast<AutoupdateStatus>([base::apple::ObjCCastStrict<NSNumber>(
+ dictionary[kAutoupdateStatusStatus]) intValue]);
std::string error_messages =
- base::SysNSStringToUTF8(base::mac::ObjCCastStrict<NSString>(
+ base::SysNSStringToUTF8(base::apple::ObjCCastStrict<NSString>(
dictionary[kAutoupdateStatusErrorMessages]));
bool enable_promote_button = true;
diff --git a/chromium/chrome/browser/ui/webui/history_clusters/history_clusters_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/history_clusters/history_clusters_handler_browsertest.cc
index 7fb67fa1164..504d0adc49e 100644
--- a/chromium/chrome/browser/ui/webui/history_clusters/history_clusters_handler_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/history_clusters/history_clusters_handler_browsertest.cc
@@ -44,7 +44,7 @@ class HistoryClustersHandlerBrowserTest : public InProcessBrowserTest {
}
protected:
- raw_ptr<HistoryClustersHandler, DanglingUntriaged> handler_;
+ raw_ptr<HistoryClustersHandler, AcrossTasksDanglingUntriaged> handler_;
private:
base::test::ScopedFeatureList feature_list_;
diff --git a/chromium/chrome/browser/ui/webui/identity_internals_ui.cc b/chromium/chrome/browser/ui/webui/identity_internals_ui.cc
index b9bad54e2fe..00856a95c3b 100644
--- a/chromium/chrome/browser/ui/webui/identity_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/identity_internals_ui.cc
@@ -24,6 +24,7 @@
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_id.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -114,7 +115,7 @@ class IdentityInternalsTokenRevoker : public GaiaAuthConsumer {
const std::string& access_token() const { return access_token_; }
// Returns the ID of the extension the access token is related to.
- const std::string& extension_id() const { return extension_id_; }
+ const extensions::ExtensionId& extension_id() const { return extension_id_; }
// GaiaAuthConsumer implementation.
void OnOAuth2RevokeTokenCompleted(
@@ -124,7 +125,7 @@ class IdentityInternalsTokenRevoker : public GaiaAuthConsumer {
// An object used to start a token revoke request.
GaiaAuthFetcher fetcher_;
// An ID of an extension the access token is related to.
- const std::string extension_id_;
+ const extensions::ExtensionId extension_id_;
// The access token to revoke.
const std::string access_token_;
// The JS callback to resolve when revoking is done.
diff --git a/chromium/chrome/browser/ui/webui/inspect_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/inspect_ui_browsertest.cc
index 54596bd835d..e3e96e0ab0e 100644
--- a/chromium/chrome/browser/ui/webui/inspect_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/inspect_ui_browsertest.cc
@@ -11,13 +11,17 @@
#include "chrome/common/url_constants.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/base/web_ui_browser_test.h"
+#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/test_navigation_observer.h"
+#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/blink/public/common/features.h"
using content::WebContents;
@@ -150,6 +154,62 @@ IN_PROC_BROWSER_TEST_F(InspectUITest, MAYBE_LaunchUIDevtools) {
base::Value(false)));
}
+class InspectUISharedStorageTest : public InspectUITest {
+ public:
+ InspectUISharedStorageTest() {
+ scoped_feature_list_.InitWithFeatures(
+ /*enabled_features=*/{blink::features::kSharedStorageAPI,
+ privacy_sandbox::kPrivacySandboxSettings4,
+ features::kPrivacySandboxAdsAPIsOverride,
+ privacy_sandbox::
+ kOverridePrivacySandboxSettingsLocalTesting},
+ /*disabled_features=*/{});
+ }
+
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ https_server_.AddDefaultHandlers(GetChromeTestDataDir());
+ https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+ content::SetupCrossSiteRedirector(&https_server_);
+
+ ASSERT_TRUE(https_server_.Start());
+
+ InspectUITest::SetUpOnMainThread();
+ }
+
+ protected:
+ base::test::ScopedFeatureList scoped_feature_list_;
+ net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
+};
+
+// TODO(b/280457934): Fix crash for Javascript coverage build and re-enable.
+#if BUILDFLAG(USE_JAVASCRIPT_COVERAGE)
+#define MAYBE_SharedStorageWorklet DISABLED_SharedStorageWorklet
+#else
+#define MAYBE_SharedStorageWorklet SharedStorageWorklet
+#endif
+IN_PROC_BROWSER_TEST_F(InspectUISharedStorageTest, MAYBE_SharedStorageWorklet) {
+ GURL main_frame_url = https_server_.GetURL("a.test", "/empty.html");
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+ const char kModuleScriptFile[] = "/shared_storage/simple_module.js";
+
+ GURL module_script_url = https_server_.GetURL("a.test", kModuleScriptFile);
+ EXPECT_TRUE(
+ content::ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
+ content::JsReplace("sharedStorage.worklet.addModule($1)",
+ module_script_url)));
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL(chrome::kChromeUIInspectURL),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testTargetListed", base::Value("#shared-storage-worklets"),
+ base::Value("populateWorkerTargets"), base::Value(kModuleScriptFile)));
+}
+
class InspectUIFencedFrameTest : public InspectUITest {
public:
content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
diff --git a/chromium/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc b/chromium/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
index 0aa7f28a4dc..476d6d8fe59 100644
--- a/chromium/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
+++ b/chromium/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
@@ -12,8 +12,8 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/user_education/user_education_service.h"
-#include "chrome/browser/ui/user_education/user_education_service_factory.h"
+#include "chrome/browser/user_education/user_education_service.h"
+#include "chrome/browser/user_education/user_education_service_factory.h"
#include "components/user_education/common/feature_promo_registry.h"
#include "components/user_education/common/feature_promo_specification.h"
#include "components/user_education/common/tutorial_description.h"
@@ -25,13 +25,13 @@
namespace {
user_education::TutorialService* GetTutorialService(Profile* profile) {
- auto* service = UserEducationServiceFactory::GetForProfile(profile);
+ auto* service = UserEducationServiceFactory::GetForBrowserContext(profile);
return service ? &service->tutorial_service() : nullptr;
}
user_education::FeaturePromoRegistry* GetFeaturePromoRegistry(
Profile* profile) {
- auto* service = UserEducationServiceFactory::GetForProfile(profile);
+ auto* service = UserEducationServiceFactory::GetForBrowserContext(profile);
return service ? &service->feature_promo_registry() : nullptr;
}
@@ -66,8 +66,9 @@ std::vector<std::string> GetSupportedPlatforms(
std::vector<std::string> GetPromoInstructions(
const user_education::FeaturePromoSpecification& spec) {
std::vector<std::string> instructions;
- if (!spec.bubble_title_text().empty()) {
- instructions.emplace_back(base::UTF16ToUTF8(spec.bubble_title_text()));
+ if (spec.bubble_title_string_id()) {
+ instructions.emplace_back(
+ l10n_util::GetStringUTF8(spec.bubble_title_string_id()));
}
instructions.emplace_back(
l10n_util::GetStringUTF8(spec.bubble_body_string_id()));
diff --git a/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index 5498a854fff..1adc311274b 100644
--- a/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -34,6 +34,7 @@
#include "components/safe_browsing/content/browser/safe_browsing_blocking_page.h"
#include "components/safe_browsing/content/browser/ui_manager.h"
#include "components/safe_browsing/core/browser/db/database_manager.h"
+#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
#include "components/security_interstitials/content/bad_clock_blocking_page.h"
#include "components/security_interstitials/content/blocked_interception_blocking_page.h"
#include "components/security_interstitials/content/https_only_mode_blocking_page.h"
@@ -321,9 +322,11 @@ CreateSafeBrowsingBlockingPage(content::WebContents* web_contents) {
resource.threat_type = threat_type;
resource.render_process_id = primary_main_frame_id.child_id;
resource.render_frame_id = primary_main_frame_id.frame_routing_id;
- resource.threat_source = g_browser_process->safe_browsing_service()
- ->database_manager()
- ->GetThreatSource();
+ resource.threat_source =
+ g_browser_process->safe_browsing_service()
+ ->database_manager()
+ ->GetBrowseUrlThreatSource(
+ safe_browsing::CheckBrowseUrlType::kHashDatabase);
// Normally safebrowsing interstitial types which block the main page load
// (SB_THREAT_TYPE_URL_MALWARE, SB_THREAT_TYPE_URL_PHISHING, and
@@ -364,9 +367,11 @@ std::unique_ptr<EnterpriseWarnPage> CreateEnterpriseWarnPage(
resource.threat_type = safe_browsing::SB_THREAT_TYPE_MANAGED_POLICY_WARN;
resource.render_process_id = primary_main_frame_id.child_id;
resource.render_frame_id = primary_main_frame_id.frame_routing_id;
- resource.threat_source = g_browser_process->safe_browsing_service()
- ->database_manager()
- ->GetThreatSource();
+ resource.threat_source =
+ g_browser_process->safe_browsing_service()
+ ->database_manager()
+ ->GetBrowseUrlThreatSource(
+ safe_browsing::CheckBrowseUrlType::kHashDatabase);
return std::make_unique<EnterpriseWarnPage>(
ui_manager, web_contents, kRequestUrl,
@@ -413,9 +418,11 @@ CreateSafeBrowsingQuietBlockingPage(content::WebContents* web_contents) {
resource.threat_type = threat_type;
resource.render_process_id = primary_main_frame_id.child_id;
resource.render_frame_id = primary_main_frame_id.frame_routing_id;
- resource.threat_source = g_browser_process->safe_browsing_service()
- ->database_manager()
- ->GetThreatSource();
+ resource.threat_source =
+ g_browser_process->safe_browsing_service()
+ ->database_manager()
+ ->GetBrowseUrlThreatSource(
+ safe_browsing::CheckBrowseUrlType::kHashDatabase);
// Normally safebrowsing interstitial types which block the main page load
// (SB_THREAT_TYPE_URL_MALWARE, SB_THREAT_TYPE_URL_PHISHING, and
diff --git a/chromium/chrome/browser/ui/webui/intro/intro_handler.cc b/chromium/chrome/browser/ui/webui/intro/intro_handler.cc
index a1662c125be..86eb0130183 100644
--- a/chromium/chrome/browser/ui/webui/intro/intro_handler.cc
+++ b/chromium/chrome/browser/ui/webui/intro/intro_handler.cc
@@ -22,7 +22,6 @@
#include "chrome/browser/ui/webui/intro/intro_ui.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
-#include "chrome/grit/google_chrome_strings.h"
#include "components/policy/core/common/cloud/cloud_policy_store.h"
#include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
#include "components/signin/public/base/consent_level.h"
@@ -265,10 +264,15 @@ base::Value::Dict GetProfileInfoValue(content::WebUI& web_ui) {
#endif
} // namespace
-IntroHandler::IntroHandler(base::RepeatingCallback<void(IntroChoice)> callback,
- bool is_device_managed)
- : callback_(std::move(callback)), is_device_managed_(is_device_managed) {
- DCHECK(callback_);
+IntroHandler::IntroHandler(
+ base::RepeatingCallback<void(IntroChoice)> intro_callback,
+ base::OnceCallback<void(DefaultBrowserChoice)> default_browser_callback,
+ bool is_device_managed)
+ : intro_callback_(std::move(intro_callback)),
+ default_browser_callback_(std::move(default_browser_callback)),
+ is_device_managed_(is_device_managed) {
+ DCHECK(intro_callback_);
+ DCHECK(default_browser_callback_);
}
IntroHandler::~IntroHandler() = default;
@@ -288,6 +292,14 @@ void IntroHandler::RegisterMessages() {
"initializeMainView",
base::BindRepeating(&IntroHandler::HandleInitializeMainView,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setAsDefaultBrowser",
+ base::BindRepeating(&IntroHandler::HandleSetAsDefaultBrowser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "skipDefaultBrowser",
+ base::BindRepeating(&IntroHandler::HandleSkipDefaultBrowser,
+ base::Unretained(this)));
}
void IntroHandler::OnJavascriptAllowed() {
@@ -310,13 +322,13 @@ void IntroHandler::OnJavascriptAllowed() {
void IntroHandler::HandleContinueWithAccount(const base::Value::List& args) {
CHECK(args.empty());
- callback_.Run(IntroChoice::kContinueWithAccount);
+ intro_callback_.Run(IntroChoice::kContinueWithAccount);
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
void IntroHandler::HandleContinueWithoutAccount(const base::Value::List& args) {
CHECK(args.empty());
- callback_.Run(IntroChoice::kContinueWithoutAccount);
+ intro_callback_.Run(IntroChoice::kContinueWithoutAccount);
}
void IntroHandler::ResetIntroButtons() {
@@ -331,6 +343,27 @@ void IntroHandler::HandleInitializeMainView(const base::Value::List& args) {
AllowJavascript();
}
+void IntroHandler::HandleSetAsDefaultBrowser(const base::Value::List& args) {
+ CHECK(args.empty());
+ if (default_browser_callback_) {
+ std::move(default_browser_callback_)
+ .Run(DefaultBrowserChoice::kSetAsDefault);
+ }
+}
+
+void IntroHandler::HandleSkipDefaultBrowser(const base::Value::List& args) {
+ CHECK(args.empty());
+ if (default_browser_callback_) {
+ std::move(default_browser_callback_).Run(DefaultBrowserChoice::kSkip);
+ }
+}
+
+void IntroHandler::ResetDefaultBrowserButtons() {
+ if (IsJavascriptAllowed()) {
+ FireWebUIListener("reset-default-browser-buttons");
+ }
+}
+
#if BUILDFLAG(IS_CHROMEOS_LACROS)
void IntroHandler::UpdateProfileInfo() {
DCHECK(IsJavascriptAllowed());
diff --git a/chromium/chrome/browser/ui/webui/intro/intro_handler.h b/chromium/chrome/browser/ui/webui/intro/intro_handler.h
index d590c257e4a..25b8fb24d0d 100644
--- a/chromium/chrome/browser/ui/webui/intro/intro_handler.h
+++ b/chromium/chrome/browser/ui/webui/intro/intro_handler.h
@@ -12,11 +12,14 @@
#include "content/public/browser/web_ui_message_handler.h"
enum class IntroChoice;
+enum class DefaultBrowserChoice;
class IntroHandler : public content::WebUIMessageHandler {
public:
- explicit IntroHandler(base::RepeatingCallback<void(IntroChoice)> callback,
- bool is_device_managed);
+ explicit IntroHandler(
+ base::RepeatingCallback<void(IntroChoice)> intro_callback,
+ base::OnceCallback<void(DefaultBrowserChoice)> default_browser_callback,
+ bool is_device_managed);
IntroHandler(const IntroHandler&) = delete;
IntroHandler& operator=(const IntroHandler&) = delete;
@@ -29,6 +32,7 @@ class IntroHandler : public content::WebUIMessageHandler {
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
void ResetIntroButtons();
#endif
+ void ResetDefaultBrowserButtons();
private:
// Handles "continueWithAccount" message from the page. No arguments.
@@ -46,6 +50,16 @@ class IntroHandler : public content::WebUIMessageHandler {
// This message is sent when the view is created.
void HandleInitializeMainView(const base::Value::List& args);
+ // Handles "setAsDefaultBrowser" message from the page. No arguments.
+ // This message is sent when the user confirms that they want to set Chrome as
+ // their default browser.
+ void HandleSetAsDefaultBrowser(const base::Value::List& args);
+
+ // Handles "skipDefaultBrowser" message from the page. No arguments.
+ // This message is sent when the user skips the prompt to set Chrome as their
+ // default browser.
+ void HandleSkipDefaultBrowser(const base::Value::List& args);
+
#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Sends an updated profile info (avatar, domain etc..) to the WebUI.
void UpdateProfileInfo();
@@ -55,7 +69,8 @@ class IntroHandler : public content::WebUIMessageHandler {
// that will be caught and handled in the ts file.
void FireManagedDisclaimerUpdate(std::string disclaimer);
- const base::RepeatingCallback<void(IntroChoice)> callback_;
+ const base::RepeatingCallback<void(IntroChoice)> intro_callback_;
+ base::OnceCallback<void(DefaultBrowserChoice)> default_browser_callback_;
const bool is_device_managed_ = false;
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
std::unique_ptr<policy::CloudPolicyStore::Observer> policy_store_observer_;
diff --git a/chromium/chrome/browser/ui/webui/intro/intro_ui.cc b/chromium/chrome/browser/ui/webui/intro/intro_ui.cc
index 30f722e9049..c46a6674d33 100644
--- a/chromium/chrome/browser/ui/webui/intro/intro_ui.cc
+++ b/chromium/chrome/browser/ui/webui/intro/intro_ui.cc
@@ -24,7 +24,6 @@
#include "components/signin/public/base/signin_buildflags.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/strings/grit/components_chromium_strings.h"
-#include "components/strings/grit/components_google_chrome_strings.h"
#include "content/public/browser/web_ui_data_source.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
@@ -62,6 +61,21 @@ IntroUI::IntroUI(content::WebUI* web_ui) : content::WebUIController(web_ui) {
default:
NOTREACHED();
}
+
+ int default_browser_title_id;
+ int default_browser_subtitle_id;
+ switch (kForYouFreDefaultBrowserVariant.Get()) {
+ case DefaultBrowserVariant::kCurrent: {
+ default_browser_title_id = IDS_FRE_DEFAULT_BROWSER_TITLE;
+ default_browser_subtitle_id = IDS_FRE_DEFAULT_BROWSER_SUBTITLE;
+ break;
+ }
+ case DefaultBrowserVariant::kNew: {
+ default_browser_title_id = IDS_FRE_DEFAULT_BROWSER_TITLE_NEW;
+ default_browser_subtitle_id = IDS_FRE_DEFAULT_BROWSER_SUBTITLE_NEW;
+ break;
+ }
+ }
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
int title_id = IDS_PRIMARY_PROFILE_FIRST_RUN_NO_NAME_TITLE;
#endif
@@ -85,6 +99,13 @@ IntroUI::IntroUI(content::WebUI* web_ui) : content::WebUIController(web_ui) {
{"declineSignInButtonTitle", IDS_FRE_DECLINE_SIGN_IN_BUTTON_TITLE},
{"acceptSignInButtonTitle", IDS_FRE_ACCEPT_SIGN_IN_BUTTON_TITLE},
{"productLogoAltText", IDS_SHORT_PRODUCT_LOGO_ALT_TEXT},
+ // Strings for default browser promo subpage.
+ {"defaultBrowserTitle", default_browser_title_id},
+ {"defaultBrowserSubtitle", default_browser_subtitle_id},
+ {"defaultBrowserIllustrationAltText",
+ IDS_FRE_DEFAULT_BROWSER_ILLUSTRATION_ALT_TEXT},
+ {"defaultBrowserSetAsDefault", IDS_FRE_DEFAULT_BROWSER_SET_AS_DEFAULT},
+ {"defaultBrowserSkip", IDS_FRE_DEFAULT_BROWSER_SKIP},
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
{"proceedLabel", IDS_PRIMARY_PROFILE_FIRST_RUN_NEXT_BUTTON_LABEL},
@@ -110,6 +131,10 @@ IntroUI::IntroUI(content::WebUI* web_ui) : content::WebUIController(web_ui) {
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
source->AddBoolean("isDeviceManaged", is_device_managed);
+ // Setup chrome://intro/default-browser UI.
+ source->AddResourcePath(chrome::kChromeUIIntroDefaultBrowserSubPage,
+ IDR_INTRO_DEFAULT_BROWSER_DEFAULT_BROWSER_HTML);
+
source->AddResourcePath("images/product-logo.svg", IDR_PRODUCT_LOGO_SVG);
source->AddResourcePath("images/product-logo-animation.svg",
IDR_PRODUCT_LOGO_ANIMATION_SVG);
@@ -123,6 +148,8 @@ IntroUI::IntroUI(content::WebUI* web_ui) : content::WebUIController(web_ui) {
// Unretained ok: `this` owns the handler.
auto intro_handler = std::make_unique<IntroHandler>(
base::BindRepeating(&IntroUI::HandleSigninChoice, base::Unretained(this)),
+ base::BindOnce(&IntroUI::HandleDefaultBrowserChoice,
+ base::Unretained(this)),
is_device_managed);
intro_handler_ = intro_handler.get();
web_ui->AddMessageHandler(std::move(intro_handler));
@@ -143,6 +170,12 @@ void IntroUI::SetSigninChoiceCallback(IntroSigninChoiceCallback callback) {
#endif
}
+void IntroUI::SetDefaultBrowserCallback(DefaultBrowserCallback callback) {
+ DCHECK(!callback->is_null());
+ default_browser_callback_ = std::move(callback);
+ intro_handler_->ResetDefaultBrowserButtons();
+}
+
void IntroUI::HandleSigninChoice(IntroChoice choice) {
if (signin_choice_callback_->is_null()) {
LOG(WARNING) << "Unexpected signin choice event";
@@ -151,4 +184,16 @@ void IntroUI::HandleSigninChoice(IntroChoice choice) {
}
}
+// For a given `IntroUI` instance, this will be called only once, even if
+// `SetDefaultBrowserCallback()` is called again. This is because after the
+// first call, the handler will drop the link, since it took a OnceCallback.
+// This is fine because the step should not be shown more than once.
+void IntroUI::HandleDefaultBrowserChoice(DefaultBrowserChoice choice) {
+ if (default_browser_callback_->is_null()) {
+ LOG(WARNING) << "Unexpected default browser choice event";
+ } else {
+ std::move(default_browser_callback_.value()).Run(choice);
+ }
+}
+
WEB_UI_CONTROLLER_TYPE_IMPL(IntroUI)
diff --git a/chromium/chrome/browser/ui/webui/intro/intro_ui.h b/chromium/chrome/browser/ui/webui/intro/intro_ui.h
index b0d2c26ac6f..104dc78c165 100644
--- a/chromium/chrome/browser/ui/webui/intro/intro_ui.h
+++ b/chromium/chrome/browser/ui/webui/intro/intro_ui.h
@@ -18,11 +18,32 @@ enum class IntroChoice {
kQuit,
};
+// This is also used for logging, so do not remove or reorder existing entries.
+enum class DefaultBrowserChoice {
+ // The user set Chrome as their default browser.
+ kSetAsDefault = 0,
+ // The user skipped the prompt to set Chrome as their default browser.
+ kSkip = 1,
+ // The user exited the first run flow while on the prompt to set Chrome as
+ // their default browser.
+ kQuit = 2,
+ // The prompt was not shown due to a timeout when checking if the browser is
+ // already default.
+ kNotShownOnTimeout = 3,
+ // Add any new values above this one, and update kMaxValue to the highest
+ // enumerator value.
+ kMaxValue = kNotShownOnTimeout
+};
+
// Callback specification for `SetSigninChoiceCallback()`.
using IntroSigninChoiceCallback =
base::StrongAlias<class IntroSigninChoiceCallbackTag,
base::OnceCallback<void(IntroChoice)>>;
+using DefaultBrowserCallback =
+ base::StrongAlias<class DefaultBrowserCallbackTag,
+ base::OnceCallback<void(DefaultBrowserChoice)>>;
+
// The WebUI controller for `chrome://intro`.
// Drops user inputs until a callback to receive the next one is provided by
// calling `SetSigninChoiceCallback()`.
@@ -36,13 +57,16 @@ class IntroUI : public content::WebUIController {
~IntroUI() override;
void SetSigninChoiceCallback(IntroSigninChoiceCallback callback);
+ void SetDefaultBrowserCallback(DefaultBrowserCallback callback);
private:
friend class ProfilePickerLacrosFirstRunBrowserTestBase;
void HandleSigninChoice(IntroChoice choice);
+ void HandleDefaultBrowserChoice(DefaultBrowserChoice choice);
IntroSigninChoiceCallback signin_choice_callback_;
+ DefaultBrowserCallback default_browser_callback_;
raw_ptr<IntroHandler> intro_handler_;
WEB_UI_CONTROLLER_TYPE_DECL();
diff --git a/chromium/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc b/chromium/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc
index 736a4d24d6d..c146b50a551 100644
--- a/chromium/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc
+++ b/chromium/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc
@@ -61,8 +61,9 @@ void InvalidationsMessageHandler::UIReady(const base::Value::List& args) {
invalidation::ProfileInvalidationProvider* invalidation_provider =
GetInvalidationProvider(Profile::FromWebUI(web_ui()));
if (invalidation_provider) {
- logger_ = invalidation_provider->GetInvalidationService()
- ->GetInvalidationLogger();
+ // TODO(crbug.com/1056181): removed with old sync invalidation service,
+ // consider using for other invalidation services.
+ NOTIMPLEMENTED();
}
if (logger_ && !logger_->IsObserverRegistered(this))
logger_->RegisterObserver(this);
@@ -74,9 +75,9 @@ void InvalidationsMessageHandler::HandleRequestDetailedStatus(
invalidation::ProfileInvalidationProvider* invalidation_provider =
GetInvalidationProvider(Profile::FromWebUI(web_ui()));
if (invalidation_provider) {
- invalidation_provider->GetInvalidationService()->RequestDetailedStatus(
- base::BindRepeating(&InvalidationsMessageHandler::OnDetailedStatus,
- weak_ptr_factory_.GetWeakPtr()));
+ // TODO(crbug.com/1056181): removed with old sync invalidation service,
+ // consider using for other invalidation services.
+ NOTIMPLEMENTED();
}
}
diff --git a/chromium/chrome/browser/ui/webui/invalidations/invalidations_ui.cc b/chromium/chrome/browser/ui/webui/invalidations/invalidations_ui.cc
index 4b8afd4173a..65a7aac4793 100644
--- a/chromium/chrome/browser/ui/webui/invalidations/invalidations_ui.cc
+++ b/chromium/chrome/browser/ui/webui/invalidations/invalidations_ui.cc
@@ -16,7 +16,6 @@
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
-#include "ui/resources/grit/webui_resources.h"
void CreateAndAddInvalidationsHTMLSource(Profile* profile) {
// This is done once per opening of the page
@@ -27,8 +26,6 @@ void CreateAndAddInvalidationsHTMLSource(Profile* profile) {
network::mojom::CSPDirectiveName::ScriptSrc,
"script-src chrome://resources chrome://webui-test 'self' "
"'unsafe-eval';");
- source->AddResourcePath("test_loader_util.js",
- IDR_WEBUI_JS_TEST_LOADER_UTIL_JS);
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::TrustedTypes,
"trusted-types jstemplate webui-test-script;");
diff --git a/chromium/chrome/browser/ui/webui/local_state/local_state_ui.cc b/chromium/chrome/browser/ui/webui/local_state/local_state_ui.cc
index 4154e4fbfe0..3f0b0fa7fff 100644
--- a/chromium/chrome/browser/ui/webui/local_state/local_state_ui.cc
+++ b/chromium/chrome/browser/ui/webui/local_state/local_state_ui.cc
@@ -6,9 +6,11 @@
#include <memory>
#include <string>
+#include <vector>
#include "base/functional/bind.h"
#include "base/values.h"
+#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
@@ -39,6 +41,17 @@ class LocalStateUIHandler : public content::WebUIMessageHandler {
// Called from JS when the page has loaded. Serializes local state prefs and
// sends them to the page.
void HandleRequestJson(const base::Value::List& args);
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // On ChromeOS, the local state file contains some information about other
+ // user accounts which we don't want to expose to other users. In that case,
+ // this will filter out the prefs to only include variations and UMA related
+ // fields, which don't contain PII.
+ std::vector<std::string> accepted_pref_prefixes_{"variations",
+ "user_experience_metrics"};
+#else
+ std::vector<std::string> accepted_pref_prefixes_;
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
};
void LocalStateUIHandler::RegisterMessages() {
@@ -50,8 +63,9 @@ void LocalStateUIHandler::RegisterMessages() {
void LocalStateUIHandler::HandleRequestJson(const base::Value::List& args) {
AllowJavascript();
- absl::optional<std::string> json =
- GetPrefsAsJson(g_browser_process->local_state());
+
+ absl::optional<std::string> json = local_state_utils::GetPrefsAsJson(
+ g_browser_process->local_state(), accepted_pref_prefixes_);
if (!json) {
json = "Error loading Local State file.";
}
diff --git a/chromium/chrome/browser/ui/webui/location_internals/BUILD.gn b/chromium/chrome/browser/ui/webui/location_internals/BUILD.gn
new file mode 100644
index 00000000000..c1ee62b6f96
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/location_internals/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojo_bindings") {
+ sources = [ "location_internals.mojom" ]
+ deps = [ "//services/device/public/mojom:geolocation_internals" ]
+ webui_module_path = "/"
+ use_typescript_sources = true
+}
diff --git a/chromium/chrome/browser/ui/webui/location_internals/location_internals.mojom b/chromium/chrome/browser/ui/webui/location_internals/location_internals.mojom
new file mode 100644
index 00000000000..df912ff4d6a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/location_internals/location_internals.mojom
@@ -0,0 +1,19 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojom;
+
+import "services/device/public/mojom/geolocation_internals.mojom";
+
+// Interface for chrome://location-internals to bind
+// device.mojom.GeolocationInternals interface. It has to be created from
+// device.mojom.DeviceService to make sure the same singleton is being accessed
+// from WebUI.
+interface LocationInternalsHandler {
+ // Method for chrome://location-internals to bind GeolocationInternals
+ // interface.
+ BindInternalsInterface(
+ pending_receiver<device.mojom.GeolocationInternals> receiver);
+};
+
diff --git a/chromium/chrome/browser/ui/webui/location_internals/location_internals_handler.cc b/chromium/chrome/browser/ui/webui/location_internals/location_internals_handler.cc
new file mode 100644
index 00000000000..cea313d5bf8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/location_internals/location_internals_handler.cc
@@ -0,0 +1,21 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/location_internals/location_internals_handler.h"
+
+#include <utility>
+
+#include "content/public/browser/device_service.h"
+
+LocationInternalsHandler::LocationInternalsHandler(
+ mojo::PendingReceiver<mojom::LocationInternalsHandler> receiver)
+ : receiver_(this, std::move(receiver)) {}
+
+LocationInternalsHandler::~LocationInternalsHandler() = default;
+
+void LocationInternalsHandler::BindInternalsInterface(
+ mojo::PendingReceiver<device::mojom::GeolocationInternals> receiver) {
+ // Forward the request to the DeviceService.
+ content::GetDeviceService().BindGeolocationInternals(std::move(receiver));
+}
diff --git a/chromium/chrome/browser/ui/webui/location_internals/location_internals_handler.h b/chromium/chrome/browser/ui/webui/location_internals/location_internals_handler.h
new file mode 100644
index 00000000000..190c40d5a40
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/location_internals/location_internals_handler.h
@@ -0,0 +1,34 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_LOCATION_INTERNALS_LOCATION_INTERNALS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_LOCATION_INTERNALS_LOCATION_INTERNALS_HANDLER_H_
+
+#include "chrome/browser/ui/webui/location_internals/location_internals.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "services/device/public/mojom/geolocation_internals.mojom.h"
+
+// Handles API requests from chrome://location-internals page by implementing
+// mojom::LocationInternalsHandler.
+class LocationInternalsHandler : public mojom::LocationInternalsHandler {
+ public:
+ explicit LocationInternalsHandler(
+ mojo::PendingReceiver<mojom::LocationInternalsHandler> receiver);
+
+ LocationInternalsHandler(const LocationInternalsHandler&) = delete;
+ LocationInternalsHandler& operator=(const LocationInternalsHandler&) = delete;
+
+ ~LocationInternalsHandler() override;
+
+ // mojom::LocationInternalsHandler:
+ void BindInternalsInterface(
+ mojo::PendingReceiver<device::mojom::GeolocationInternals> receiver)
+ override;
+
+ private:
+ mojo::Receiver<mojom::LocationInternalsHandler> receiver_;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_LOCATION_INTERNALS_LOCATION_INTERNALS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.cc b/chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.cc
index 96c867cad7e..6107c2fbedb 100644
--- a/chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/location_internals/location_internals_ui.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/location_internals/location_internals_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/location_internals_resources.h"
@@ -28,3 +29,8 @@ LocationInternalsUI::LocationInternalsUI(content::WebUI* web_ui)
LocationInternalsUI::~LocationInternalsUI() = default;
WEB_UI_CONTROLLER_TYPE_IMPL(LocationInternalsUI)
+
+void LocationInternalsUI::BindInterface(
+ mojo::PendingReceiver<mojom::LocationInternalsHandler> receiver) {
+ handler_ = std::make_unique<LocationInternalsHandler>(std::move(receiver));
+}
diff --git a/chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.h b/chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.h
index d8f0f75bbda..b96b4b7bdb9 100644
--- a/chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.h
+++ b/chromium/chrome/browser/ui/webui/location_internals/location_internals_ui.h
@@ -5,9 +5,12 @@
#ifndef CHROME_BROWSER_UI_WEBUI_LOCATION_INTERNALS_LOCATION_INTERNALS_UI_H_
#define CHROME_BROWSER_UI_WEBUI_LOCATION_INTERNALS_LOCATION_INTERNALS_UI_H_
+#include "chrome/browser/ui/webui/location_internals/location_internals.mojom-forward.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "ui/webui/mojo_web_ui_controller.h"
+class LocationInternalsHandler;
+
// The WebUI for chrome://location-internals
class LocationInternalsUI : public ui::MojoWebUIController {
public:
@@ -18,7 +21,13 @@ class LocationInternalsUI : public ui::MojoWebUIController {
~LocationInternalsUI() override;
+ // Instantiates the implementor of the mojom::LocationInternalsHandler mojo
+ // interface passing the pending receiver that will be internally bound.
+ void BindInterface(
+ mojo::PendingReceiver<mojom::LocationInternalsHandler> receiver);
+
private:
+ std::unique_ptr<LocationInternalsHandler> handler_;
WEB_UI_CONTROLLER_TYPE_DECL();
};
diff --git a/chromium/chrome/browser/ui/webui/management/management_ui.cc b/chromium/chrome/browser/ui/webui/management/management_ui.cc
index 089a9f6145f..0cb023b4f13 100644
--- a/chromium/chrome/browser/ui/webui/management/management_ui.cc
+++ b/chromium/chrome/browser/ui/webui/management/management_ui.cc
@@ -107,6 +107,10 @@ content::WebUIDataSource* CreateAndAddManagementUIHtmlSource(Profile* profile) {
{"extensionReportingTitle", IDS_MANAGEMENT_EXTENSIONS_INSTALLED},
{"extensionName", IDS_MANAGEMENT_EXTENSIONS_NAME},
{"extensionPermissions", IDS_MANAGEMENT_EXTENSIONS_PERMISSIONS},
+ {"applicationReporting", IDS_MANAGEMENT_APPLICATION_REPORTING},
+ {"applicationReportingTitle", IDS_MANAGEMENT_APPLICATIONS_INSTALLED},
+ {"applicationName", IDS_MANAGEMENT_APPLICATIONS_NAME},
+ {"applicationPermissions", IDS_MANAGEMENT_APPLICATIONS_PERMISSIONS},
{"title", IDS_MANAGEMENT_TITLE},
{"toolbarTitle", IDS_MANAGEMENT_TOOLBAR_TITLE},
{"searchPrompt", IDS_SETTINGS_SEARCH_PROMPT},
@@ -149,6 +153,7 @@ content::WebUIDataSource* CreateAndAddManagementUIHtmlSource(Profile* profile) {
{kManagementOnPageVisitedEvent, IDS_MANAGEMENT_PAGE_VISITED_EVENT},
{kManagementOnPageVisitedVisibleData,
IDS_MANAGEMENT_PAGE_VISITED_VISIBLE_DATA},
+ {kManagementLegacyTechReport, IDS_MANAGEMENT_LEGACY_TECH_REPORT},
};
source->AddLocalizedStrings(kLocalizedStrings);
@@ -218,21 +223,7 @@ std::u16string ManagementUI::GetManagementPageSubtitle(Profile* profile) {
l10n_util::GetStringUTF16(device_type),
base::UTF8ToUTF16(account_manager));
#else // BUILDFLAG(IS_CHROMEOS_ASH)
- const auto account_manager =
- chrome::GetAccountManagerIdentity(profile).value_or(std::string());
- const auto managed =
- profile->GetProfilePolicyConnector()->IsManaged() ||
- g_browser_process->browser_policy_connector()->HasMachineLevelPolicies();
- if (account_manager.empty()) {
- return l10n_util::GetStringUTF16(managed
- ? IDS_MANAGEMENT_SUBTITLE
- : IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE);
- }
- if (managed) {
- return l10n_util::GetStringFUTF16(IDS_MANAGEMENT_SUBTITLE_MANAGED_BY,
- base::UTF8ToUTF16(account_manager));
- }
- return l10n_util::GetStringUTF16(IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE);
+ return chrome::GetManagementPageSubtitle(profile);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
diff --git a/chromium/chrome/browser/ui/webui/management/management_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/management/management_ui_browsertest.cc
index 5c2dd41ecc0..900060a4627 100644
--- a/chromium/chrome/browser/ui/webui/management/management_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/management/management_ui_browsertest.cc
@@ -106,7 +106,7 @@ IN_PROC_BROWSER_TEST_F(ManagementUITest, MAYBE_ManagementStateChange) {
base::UTF8ToUTF16(chrome::kManagedUiLearnMoreUrl),
base::EscapeForHTML(l10n_util::GetStringUTF16(
IDS_MANAGEMENT_LEARN_MORE_ACCCESSIBILITY_TEXT)))},
- {"extensionReportingTitle",
+ {"extensionReportingSubtitle",
l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED)},
{"pageSubtitle",
l10n_util::GetStringUTF16(IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE)},
@@ -139,7 +139,7 @@ IN_PROC_BROWSER_TEST_F(ManagementUITest, MAYBE_ManagementStateChange) {
base::UTF8ToUTF16(chrome::kManagedUiLearnMoreUrl),
base::EscapeForHTML(l10n_util::GetStringUTF16(
IDS_MANAGEMENT_LEARN_MORE_ACCCESSIBILITY_TEXT)))},
- {"extensionReportingTitle",
+ {"extensionReportingSubtitle",
l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED)},
{"pageSubtitle", l10n_util::GetStringUTF16(IDS_MANAGEMENT_SUBTITLE)},
{"managedWebsitesSubtitle",
diff --git a/chromium/chrome/browser/ui/webui/management/management_ui_handler.cc b/chromium/chrome/browser/ui/webui/management/management_ui_handler.cc
index ead63e66b64..7174cbdbf83 100644
--- a/chromium/chrome/browser/ui/webui/management/management_ui_handler.cc
+++ b/chromium/chrome/browser/ui/webui/management/management_ui_handler.cc
@@ -22,20 +22,28 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
+#include "chrome/browser/apps/app_service/app_icon/app_icon_source.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/device_api/managed_configuration_api.h"
#include "chrome/browser/device_api/managed_configuration_api_factory.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/enterprise/connectors/connectors_service.h"
+#include "chrome/browser/enterprise/reporting/prefs.h"
#include "chrome/browser/media/webrtc/capture_policy_utils.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
-#include "chrome/browser/policy/management_utils.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/managed_ui.h"
+#include "chrome/browser/web_applications/web_app_constants.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_utils.h"
#include "chrome/common/pref_names.h"
+#include "components/enterprise/browser/reporting/common_pref_names.h"
+#include "components/policy/core/common/management/management_service.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/strings/grit/components_strings.h"
#include "components/supervised_user/core/common/pref_names.h"
@@ -164,18 +172,22 @@ const char kManagementOnPageVisitedEvent[] = "managementOnPageVisitedEvent";
const char kManagementOnPageVisitedVisibleData[] =
"managementOnPageVisitedVisibleData";
+const char kManagementLegacyTechReport[] = "managementLegacyTechReport";
+
const char kReportingTypeDevice[] = "device";
const char kReportingTypeExtensions[] = "extensions";
const char kReportingTypeSecurity[] = "security";
const char kReportingTypeUser[] = "user";
const char kReportingTypeUserActivity[] = "user-activity";
+const char kReportingTypeLegacyTech[] = "legacy-tech";
enum class ReportingType {
kDevice,
kExtensions,
kSecurity,
kUser,
- kUserActivity
+ kUserActivity,
+ kLegacyTech,
};
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
@@ -234,10 +246,6 @@ bool IsProfileManaged(Profile* profile) {
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
-bool IsDeviceManaged() {
- return policy::IsDeviceEnterpriseManaged();
-}
-
enum class DeviceReportingType {
kSupervisedUser,
kDeviceActivity,
@@ -256,6 +264,7 @@ enum class DeviceReportingType {
kLoginLogout,
kCRDSessions,
kPeripherals,
+ kLegacyTech,
};
#else
@@ -306,6 +315,8 @@ std::string ToJSDeviceReportingType(const DeviceReportingType& type) {
return "crd sessions";
case DeviceReportingType::kPeripherals:
return "peripherals";
+ case DeviceReportingType::kLegacyTech:
+ return kReportingTypeLegacyTech;
default:
NOTREACHED() << "Unknown device reporting type";
return "device";
@@ -454,10 +465,20 @@ void AddDeviceReportingInfo(base::Value::List* report_sources,
DeviceReportingType::kAndroidApplication);
}
+ if (!profile->GetPrefs()
+ ->GetList(enterprise_reporting::kCloudLegacyTechReportAllowlist)
+ .empty()) {
+ AddDeviceReportingElement(report_sources, kManagementLegacyTechReport,
+ DeviceReportingType::kLegacyTech);
+ }
+
bool report_login_logout = false;
ash::CrosSettings::Get()->GetBoolean(ash::kReportDeviceLoginLogout,
&report_login_logout);
- if (report_login_logout) {
+ bool report_xdr_events = false;
+ ash::CrosSettings::Get()->GetBoolean(ash::kDeviceReportXDREvents,
+ &report_xdr_events);
+ if (report_login_logout || report_xdr_events) {
AddDeviceReportingElement(report_sources, kManagementReportLoginLogout,
DeviceReportingType::kLoginLogout);
}
@@ -530,6 +551,8 @@ const char* GetReportingTypeValue(ReportingType reportingType) {
return kReportingTypeUser;
case ReportingType::kUserActivity:
return kReportingTypeUserActivity;
+ case ReportingType::kLegacyTech:
+ return kReportingTypeLegacyTech;
default:
return kReportingTypeSecurity;
}
@@ -591,7 +614,8 @@ void ManagementUIHandler::InitializeInternal(content::WebUI* web_ui,
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
handler->account_managed_ = IsProfileManaged(profile);
- handler->device_managed_ = IsDeviceManaged();
+ handler->device_managed_ =
+ policy::ManagementServiceFactory::GetForPlatform()->IsManaged();
#else
handler->account_managed_ = IsProfileManaged(profile) || IsBrowserManaged();
#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -632,6 +656,10 @@ void ManagementUIHandler::RegisterMessages() {
base::BindRepeating(&ManagementUIHandler::HandleGetManagedWebsites,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "getApplications",
+ base::BindRepeating(&ManagementUIHandler::HandleGetApplications,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"initBrowserReportingInfo",
base::BindRepeating(&ManagementUIHandler::HandleInitBrowserReportingInfo,
base::Unretained(this)));
@@ -668,20 +696,25 @@ void ManagementUIHandler::AddReportingInfo(base::Value::List* report_sources) {
&on_prem_reporting_extension_stable_policy_map,
&on_prem_reporting_extension_beta_policy_map};
- const auto* cloud_reporting_policy_value =
- GetPolicyService()
- ->GetPolicies(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
- std::string()))
- .GetValue(policy::key::kCloudReportingEnabled,
- base::Value::Type::BOOLEAN);
const bool cloud_reporting_policy_enabled =
- cloud_reporting_policy_value && cloud_reporting_policy_value->GetBool();
+ g_browser_process->local_state()->GetBoolean(
+ enterprise_reporting::kCloudReportingEnabled);
+ const bool cloud_legacy_tech_report_enabled =
+ !Profile::FromWebUI(web_ui())
+ ->GetPrefs()
+ ->GetList(enterprise_reporting::kCloudLegacyTechReportAllowlist)
+ .empty();
+
+ if (cloud_legacy_tech_report_enabled) {
+ Profile::FromWebUI(web_ui())->GetPrefs()->GetList(
+ enterprise_reporting::kCloudLegacyTechReportAllowlist)[0];
+ }
const struct {
- const char* policy_key;
+ const char* reporting_extension_policy_key;
const char* message;
const ReportingType reporting_type;
- const bool enabled_by_default;
+ const bool cloud_reporting_enabled;
} report_definitions[] = {
{kPolicyKeyReportMachineIdData, kManagementExtensionReportMachineName,
ReportingType::kDevice, cloud_reporting_policy_enabled},
@@ -700,17 +733,19 @@ void ManagementUIHandler::AddReportingInfo(base::Value::List* report_sources) {
{kPolicyKeyReportUserBrowsingData,
kManagementExtensionReportUserBrowsingData, ReportingType::kUserActivity,
false},
- };
+ {kPolicyKeyReportUserBrowsingData, kManagementLegacyTechReport,
+ ReportingType::kLegacyTech, cloud_legacy_tech_report_enabled}};
std::unordered_set<const char*> enabled_messages;
for (auto& report_definition : report_definitions) {
- if (report_definition.enabled_by_default) {
+ if (report_definition.cloud_reporting_enabled) {
enabled_messages.insert(report_definition.message);
- } else if (report_definition.policy_key) {
+ } else if (report_definition.reporting_extension_policy_key) {
for (const policy::PolicyMap* policy_map : policy_maps) {
const base::Value* policy_value = policy_map->GetValue(
- report_definition.policy_key, base::Value::Type::BOOLEAN);
+ report_definition.reporting_extension_policy_key,
+ base::Value::Type::BOOLEAN);
if (policy_value && policy_value->GetBool()) {
enabled_messages.insert(report_definition.message);
break;
@@ -806,15 +841,19 @@ void ManagementUIHandler::AddMonitoredNetworkPrivacyDisclosure(
}
// Check if DeviceReportXDREvents is enabled.
- auto* xdr_policy_value = GetPolicyService()
- ->GetPolicies(policy::PolicyNamespace(
- policy::POLICY_DOMAIN_CHROME, std::string()))
- .GetValue(policy::key::kDeviceReportXDREvents,
- base::Value::Type::BOOLEAN);
- bool xdr_policy_enabled = xdr_policy_value && xdr_policy_value->GetBool();
-
- if (xdr_policy_enabled) {
- response->Set("showMonitoredNetworkPrivacyDisclosure", xdr_policy_enabled);
+ auto* report_xdr_events_policy_value =
+ GetPolicyService()
+ ->GetPolicies(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
+ std::string()))
+ .GetValue(policy::key::kDeviceReportXDREvents,
+ base::Value::Type::BOOLEAN);
+ bool report_xdr_events_policy_enabled =
+ report_xdr_events_policy_value &&
+ report_xdr_events_policy_value->GetBool();
+
+ if (report_xdr_events_policy_enabled) {
+ response->Set("showMonitoredNetworkPrivacyDisclosure",
+ report_xdr_events_policy_enabled);
return;
}
@@ -908,9 +947,12 @@ base::Value::Dict ManagementUIHandler::GetContextualManagedData(
if (enterprise_manager.empty()) {
response.Set(
- "extensionReportingTitle",
+ "extensionReportingSubtitle",
l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
response.Set(
+ "applicationReportingSubtitle",
+ l10n_util::GetStringUTF16(IDS_MANAGEMENT_APPLICATIONS_INSTALLED));
+ response.Set(
"managedWebsitesSubtitle",
l10n_util::GetStringUTF16(IDS_MANAGEMENT_MANAGED_WEBSITES_EXPLANATION));
@@ -932,9 +974,13 @@ base::Value::Dict ManagementUIHandler::GetContextualManagedData(
} else {
response.Set(
- "extensionReportingTitle",
+ "extensionReportingSubtitle",
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
base::UTF8ToUTF16(enterprise_manager)));
+ response.Set(
+ "applicationReportingSubtitle",
+ l10n_util::GetStringFUTF16(IDS_MANAGEMENT_APPLICATIONS_INSTALLED_BY,
+ base::UTF8ToUTF16(enterprise_manager)));
response.Set("managedWebsitesSubtitle",
l10n_util::GetStringFUTF16(
IDS_MANAGEMENT_MANAGED_WEBSITES_BY_EXPLANATION,
@@ -1054,6 +1100,45 @@ base::Value::List ManagementUIHandler::GetManagedWebsitesInfo(
return managed_websites;
}
+base::Value::List ManagementUIHandler::GetApplicationsInfo(
+ Profile* profile) const {
+ base::Value::List applications;
+
+ auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
+ // Only display web apps for the profile that contains them e.g. Lacros
+ // primary profile when Lacros is enabled.
+ if (provider == nullptr) {
+ return applications;
+ }
+
+ auto& registrar = provider->registrar_unsafe();
+
+ for (const web_app::AppId& app_id : registrar.GetAppIds()) {
+ base::Value::List permission_messages;
+ // Display RunOnOsLogin if it is set to autostart by admin policy.
+ web_app::ValueWithPolicy<web_app::RunOnOsLoginMode> policy =
+ registrar.GetAppRunOnOsLoginMode(app_id);
+ if (!policy.user_controllable &&
+ web_app::IsRunOnOsLoginModeEnabledForAutostart(policy.value)) {
+ permission_messages.Append(l10n_util::GetStringUTF16(
+ IDS_MANAGEMENT_APPLICATIONS_RUN_ON_OS_LOGIN));
+ }
+
+ if (!permission_messages.empty()) {
+ base::Value::Dict app_info;
+ app_info.Set("name", registrar.GetAppShortName(app_id));
+ // We try to match the same icon size as used for the extensions
+ GURL icon = apps::AppIconSource::GetIconURL(
+ app_id, extension_misc::EXTENSION_ICON_SMALLISH);
+ app_info.Set("icon", icon.spec());
+ app_info.Set("permissions", std::move(permission_messages));
+ applications.Append(std::move(app_info));
+ }
+ }
+
+ return applications;
+}
+
policy::PolicyService* ManagementUIHandler::GetPolicyService() {
return Profile::FromWebUI(web_ui())
->GetProfilePolicyConnector()
@@ -1270,6 +1355,13 @@ void ManagementUIHandler::HandleGetManagedWebsites(
GetManagedWebsitesInfo(Profile::FromWebUI(web_ui())));
}
+void ManagementUIHandler::HandleGetApplications(const base::Value::List& args) {
+ AllowJavascript();
+
+ ResolveJavascriptCallback(args[0] /* callback_id */,
+ GetApplicationsInfo(Profile::FromWebUI(web_ui())));
+}
+
void ManagementUIHandler::HandleInitBrowserReportingInfo(
const base::Value::List& args) {
base::Value::List report_sources;
@@ -1322,9 +1414,12 @@ void ManagementUIHandler::UpdateManagedState() {
bool managed_state_changed = false;
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
managed_state_changed |= account_managed_ != IsProfileManaged(profile);
- managed_state_changed |= device_managed_ != IsDeviceManaged();
+ managed_state_changed |=
+ device_managed_ !=
+ policy::ManagementServiceFactory::GetForPlatform()->IsManaged();
account_managed_ = IsProfileManaged(profile);
- device_managed_ = IsDeviceManaged();
+ device_managed_ =
+ policy::ManagementServiceFactory::GetForPlatform()->IsManaged();
#else
managed_state_changed |=
account_managed_ != (IsProfileManaged(profile) || IsBrowserManaged());
diff --git a/chromium/chrome/browser/ui/webui/management/management_ui_handler.h b/chromium/chrome/browser/ui/webui/management/management_ui_handler.h
index b6dccd7b3fc..426e6338762 100644
--- a/chromium/chrome/browser/ui/webui/management/management_ui_handler.h
+++ b/chromium/chrome/browser/ui/webui/management/management_ui_handler.h
@@ -88,6 +88,8 @@ extern const char kManagementOnPrintVisibleData[];
extern const char kManagementOnPageVisitedEvent[];
extern const char kManagementOnPageVisitedVisibleData[];
+extern const char kManagementLegacyTechReport[];
+
extern const char kPolicyKeyReportMachineIdData[];
extern const char kPolicyKeyReportUserIdData[];
extern const char kPolicyKeyReportVersionData[];
@@ -102,6 +104,7 @@ extern const char kReportingTypeExtensions[];
extern const char kReportingTypeSecurity[];
extern const char kReportingTypeUser[];
extern const char kReportingTypeUserActivity[];
+extern const char kReportingTypeLegacyTech[];
namespace extensions {
class Extension;
@@ -172,6 +175,7 @@ class ManagementUIHandler : public content::WebUIMessageHandler,
base::Value::Dict GetContextualManagedData(Profile* profile);
base::Value::Dict GetThreatProtectionInfo(Profile* profile);
base::Value::List GetManagedWebsitesInfo(Profile* profile) const;
+ base::Value::List GetApplicationsInfo(Profile* profile) const;
virtual policy::PolicyService* GetPolicyService();
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
virtual device_signals::UserPermissionService* GetUserPermissionService();
@@ -214,6 +218,7 @@ class ManagementUIHandler : public content::WebUIMessageHandler,
void HandleGetContextualManagedData(const base::Value::List& args);
void HandleGetThreatProtectionInfo(const base::Value::List& args);
void HandleGetManagedWebsites(const base::Value::List& args);
+ void HandleGetApplications(const base::Value::List& args);
void HandleInitBrowserReportingInfo(const base::Value::List& args);
void AsyncUpdateLogo();
diff --git a/chromium/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc b/chromium/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
index 4202a44b381..33d497db75d 100644
--- a/chromium/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
@@ -19,12 +19,18 @@
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
+#include "chrome/browser/ash/policy/remote_commands/fake_start_crd_session_job_delegate.h"
#include "chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h"
+#include "chrome/browser/enterprise/reporting/prefs.h"
#include "chrome/browser/policy/dm_token_utils.h"
+#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/ui/webui/management/management_ui_handler.h"
#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "components/enterprise/browser/reporting/common_pref_names.h"
+#include "components/enterprise/browser/reporting/real_time_report_type.h"
#include "components/enterprise/common/proto/connectors.pb.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/cloud/dm_token.h"
@@ -36,9 +42,14 @@
#include "components/policy/core/common/policy_types.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_service.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/strings/grit/components_strings.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_web_ui.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -64,12 +75,10 @@
#include "chrome/browser/ash/settings/device_settings_test_helper.h"
#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
-#include "chrome/browser/chromeos/policy/dlp/mock_dlp_rules_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_rules_manager.h"
#include "chrome/browser/net/secure_dns_config.h"
#include "chrome/browser/net/stub_resolver_config_reader.h"
#include "chrome/browser/net/system_network_context_manager.h"
-#include "chrome/browser/prefs/browser_prefs.h"
-#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
#include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
@@ -81,18 +90,15 @@
#include "chromeos/ash/components/system/fake_statistics_provider.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/account_id/account_id.h"
-#include "components/enterprise/browser/reporting/common_pref_names.h"
#include "components/onc/onc_pref_names.h"
#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
#include "components/policy/core/common/cloud/mock_cloud_external_data_manager.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
#include "components/policy/core/common/cloud/mock_signing_service.h"
-#include "components/prefs/testing_pref_service.h"
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_manager/fake_user_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -122,7 +128,7 @@ using testing::Return;
using testing::ReturnRef;
struct ContextualManagementSourceUpdate {
- std::u16string extension_reporting_title;
+ std::u16string extension_reporting_subtitle;
std::u16string managed_websites_title;
std::u16string subtitle;
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -141,7 +147,6 @@ const char kUser[] = "user@domain.com";
const char kGaiaId[] = "gaia_id";
} // namespace
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-
#if BUILDFLAG(IS_CHROMEOS_ASH)
// This class is just to mock the behaviour of the few flags we need for
// simulating the behaviour of the policy::DeviceStatusCollector.
@@ -202,18 +207,25 @@ class TestDeviceCloudPolicyManagerAsh
: DeviceCloudPolicyManagerAsh(std::move(store),
nullptr,
nullptr,
- state_keys_broker) {
+ state_keys_broker,
+ crd_delegate_) {
set_component_policy_disabled_for_testing(true);
}
~TestDeviceCloudPolicyManagerAsh() override = default;
+
+ private:
+ policy::FakeStartCrdSessionJobDelegate crd_delegate_;
};
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
class TestManagementUIHandler : public ManagementUIHandler {
public:
TestManagementUIHandler() = default;
- explicit TestManagementUIHandler(policy::PolicyService* policy_service)
- : policy_service_(policy_service) {}
+ TestManagementUIHandler(policy::PolicyService* policy_service,
+ content::WebUI* web_ui)
+ : policy_service_(policy_service) {
+ set_web_ui(web_ui);
+ }
~TestManagementUIHandler() override = default;
@@ -225,7 +237,7 @@ class TestManagementUIHandler : public ManagementUIHandler {
return GetContextualManagedData(profile);
}
- base::Value::List GetExtensionReportingInfo(bool can_collect_signals = true) {
+ base::Value::List GetReportingInfo(bool can_collect_signals = true) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
EXPECT_CALL(mock_user_permission_service_, CanCollectSignals())
.WillOnce(
@@ -286,16 +298,15 @@ class ManagementUIHandlerTests : public TestingBaseClass {
public:
#if BUILDFLAG(IS_CHROMEOS_ASH)
ManagementUIHandlerTests()
- : TestingBaseClass(),
- device_domain_(u"devicedomain.com"),
+ : device_domain_(u"devicedomain.com"),
task_runner_(base::MakeRefCounted<base::TestSimpleTaskRunner>()),
state_keys_broker_(&session_manager_client_),
- handler_(&policy_service_) {
+ handler_(&policy_service_, &web_ui_) {
ON_CALL(policy_service_, GetPolicies(_))
.WillByDefault(ReturnRef(empty_policy_map_));
}
#else
- ManagementUIHandlerTests() : TestingBaseClass(), handler_(&policy_service_) {
+ ManagementUIHandlerTests() : handler_(&policy_service_, &web_ui_) {
ON_CALL(policy_service_, GetPolicies(_))
.WillByDefault(ReturnRef(empty_policy_map_));
}
@@ -343,8 +354,8 @@ class ManagementUIHandlerTests : public TestingBaseClass {
}
void ExtractContextualSourceUpdate(const base::Value::Dict& data) {
- extracted_.extension_reporting_title =
- ExtractPathFromDict(data, "extensionReportingTitle");
+ extracted_.extension_reporting_subtitle =
+ ExtractPathFromDict(data, "extensionReportingSubtitle");
extracted_.managed_websites_title =
ExtractPathFromDict(data, "managedWebsitesSubtitle");
extracted_.subtitle = ExtractPathFromDict(data, "pageSubtitle");
@@ -387,6 +398,7 @@ class ManagementUIHandlerTests : public TestingBaseClass {
std::string device_domain;
base::FilePath crostini_ansible_playbook_filepath;
bool insights_extension_enabled;
+ bool legacy_tech_reporting_enabled;
base::Value::List report_app_inventory;
base::Value::List report_app_usage;
};
@@ -416,10 +428,17 @@ class ManagementUIHandlerTests : public TestingBaseClass {
setup_config_.insights_extension_enabled = false;
setup_config_.report_app_inventory = base::Value::List();
setup_config_.report_app_usage = base::Value::List();
+ setup_config_.legacy_tech_reporting_enabled = false;
+ }
+
+ void SetUpLocalState() {
+ RegisterLocalState(local_state_.registry());
+ TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
void SetUp() override {
+ SetUpLocalState();
install_attributes_ = std::make_unique<ash::ScopedStubInstallAttributes>(
ash::StubInstallAttributes::CreateUnset());
DeviceSettingsTestBase::SetUp();
@@ -441,20 +460,17 @@ class ManagementUIHandlerTests : public TestingBaseClass {
}
void TearDown() override {
network_handler_test_helper_.reset();
- profile_.reset();
TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
DeviceSettingsTestBase::TearDown();
}
void SetUpConnectManager() {
- RegisterLocalState(local_state_.registry());
std::unique_ptr<policy::DeviceCloudPolicyStoreAsh> store =
std::make_unique<policy::DeviceCloudPolicyStoreAsh>(
device_settings_service_.get(), install_attributes_->Get(),
base::SingleThreadTaskRunner::GetCurrentDefault());
manager_ = std::make_unique<TestDeviceCloudPolicyManagerAsh>(
std::move(store), &state_keys_broker_);
- TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
manager_.get()->Initialize(&local_state_);
}
@@ -486,6 +502,13 @@ class ManagementUIHandlerTests : public TestingBaseClass {
GetTestConfig().crostini_report_usage);
local_state_.SetBoolean(enterprise_reporting::kCloudReportingEnabled,
GetTestConfig().cloud_reporting_enabled);
+ if (GetTestConfig().legacy_tech_reporting_enabled) {
+ base::Value::List allowlist;
+ allowlist.Append("www.example.com");
+ profile_->GetTestingPrefService()->SetManagedPref(
+ enterprise_reporting::kCloudLegacyTechReportAllowlist,
+ std::make_unique<base::Value>(std::move(allowlist)));
+ }
profile_->GetPrefs()->SetFilePath(
crostini::prefs::kCrostiniAnsiblePlaybookFilePath,
@@ -516,6 +539,11 @@ class ManagementUIHandlerTests : public TestingBaseClass {
}
return result;
}
+#else
+ void SetUp() override { SetUpLocalState(); }
+ void TearDown() override {
+ TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
+ }
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
void SetUpProfileAndHandler() {
@@ -525,6 +553,9 @@ class ManagementUIHandlerTests : public TestingBaseClass {
builder.OverridePolicyConnectorIsManagedForTesting(true);
}
profile_ = builder.Build();
+ web_contents_ = content::WebContents::Create(
+ content::WebContents::CreateParams(profile_.get()));
+ web_ui_.set_web_contents(web_contents_.get());
handler_.SetAccountManagedForTesting(GetTestConfig().managed_account);
handler_.SetDeviceManagedForTesting(GetTestConfig().managed_device);
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -561,8 +592,8 @@ class ManagementUIHandlerTests : public TestingBaseClass {
#endif
- std::u16string GetExtensionReportingTitle() const {
- return extracted_.extension_reporting_title;
+ std::u16string GetExtensionReportingSubtitle() const {
+ return extracted_.extension_reporting_subtitle;
}
std::u16string GetManagedWebsitesTitle() const {
@@ -621,12 +652,12 @@ class ManagementUIHandlerTests : public TestingBaseClass {
policy::PolicyMap empty_policy_map_;
std::u16string device_domain_;
ContextualManagementSourceUpdate extracted_;
+ TestingPrefServiceSimple local_state_;
+ TestingPrefServiceSimple user_prefs_;
#if BUILDFLAG(IS_CHROMEOS_ASH)
std::unique_ptr<ash::NetworkHandlerTestHelper> network_handler_test_helper_;
std::unique_ptr<ash::ScopedStubInstallAttributes> install_attributes_;
std::unique_ptr<crostini::FakeCrostiniFeatures> crostini_features_;
- TestingPrefServiceSimple local_state_;
- TestingPrefServiceSimple user_prefs_;
std::unique_ptr<StubResolverConfigReader> stub_resolver_config_reader_;
std::unique_ptr<TestDeviceCloudPolicyManagerAsh> manager_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
@@ -635,8 +666,10 @@ class ManagementUIHandlerTests : public TestingBaseClass {
ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
#else
content::BrowserTaskEnvironment task_environment_;
- std::unique_ptr<TestingProfile> profile_;
#endif
+ std::unique_ptr<TestingProfile> profile_;
+ content::TestWebUI web_ui_;
+ std::unique_ptr<content::WebContents> web_contents_;
#if BUILDFLAG(IS_CHROMEOS_LACROS)
chromeos::ScopedLacrosServiceTestHelper scoped_lacros_test_helper_;
@@ -745,7 +778,7 @@ TEST_F(ManagementUIHandlerTests,
GetTestConfig().managed_account = false;
SetUpProfileAndHandler();
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
EXPECT_EQ(
GetManagedWebsitesTitle(),
@@ -765,7 +798,7 @@ TEST_F(ManagementUIHandlerTests,
ResetTestConfig();
SetUpProfileAndHandler();
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
EXPECT_EQ(
GetManagedWebsitesTitle(),
@@ -787,7 +820,7 @@ TEST_F(ManagementUIHandlerTests,
GetTestConfig().override_policy_connector_is_managed = true;
SetUpProfileAndHandler();
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
EXPECT_EQ(
GetManagedWebsitesTitle(),
@@ -812,7 +845,7 @@ TEST_F(ManagementUIHandlerTests,
GetTestConfig().managed_account = false;
SetUpProfileAndHandler();
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
base::UTF8ToUTF16(domain)));
EXPECT_EQ(
@@ -836,7 +869,7 @@ TEST_F(ManagementUIHandlerTests,
GetTestConfig().managed_account = false;
SetUpProfileAndHandler();
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
EXPECT_EQ(
GetManagedWebsitesTitle(),
@@ -860,7 +893,7 @@ TEST_F(ManagementUIHandlerTests,
GetTestConfig().override_policy_connector_is_managed = true;
SetUpProfileAndHandler();
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
base::UTF8ToUTF16(domain)));
EXPECT_EQ(
@@ -892,7 +925,7 @@ TEST_F(ManagementUIHandlerTests,
GetTestConfig().device_domain = "";
SetUpProfileAndHandler();
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
base::UTF8ToUTF16(domain)));
EXPECT_EQ(
@@ -917,7 +950,7 @@ TEST_F(ManagementUIHandlerTests,
GetTestConfig().device_domain = "";
SetUpProfileAndHandler();
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
EXPECT_EQ(
GetManagedWebsitesTitle(),
@@ -942,7 +975,7 @@ TEST_F(ManagementUIHandlerTests,
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_SUBTITLE_MANAGED_BY,
l10n_util::GetStringUTF16(device_type),
device_domain()));
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
device_domain()));
EXPECT_EQ(
@@ -966,7 +999,7 @@ TEST_F(ManagementUIHandlerTests,
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_SUBTITLE_MANAGED_BY,
l10n_util::GetStringUTF16(device_type),
device_domain()));
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
device_domain()));
EXPECT_EQ(GetManagementOverview(),
@@ -994,7 +1027,7 @@ TEST_F(ManagementUIHandlerTests,
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_SUBTITLE_MANAGED_BY,
l10n_util::GetStringUTF16(device_type),
device_domain()));
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
device_domain()));
EXPECT_EQ(
@@ -1020,7 +1053,7 @@ TEST_F(ManagementUIHandlerTests, ManagementContextualSourceUpdateUnmanaged) {
EXPECT_EQ(GetPageSubtitle(),
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE,
l10n_util::GetStringUTF16(device_type)));
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED));
EXPECT_EQ(
GetManagedWebsitesTitle(),
@@ -1048,7 +1081,7 @@ TEST_F(ManagementUIHandlerTests,
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_SUBTITLE_MANAGED_BY,
l10n_util::GetStringUTF16(device_type),
device_domain()));
- EXPECT_EQ(GetExtensionReportingTitle(),
+ EXPECT_EQ(GetExtensionReportingSubtitle(),
l10n_util::GetStringFUTF16(IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY,
device_domain()));
EXPECT_EQ(
@@ -1088,7 +1121,8 @@ TEST_F(ManagementUIHandlerTests, AllEnabledDeviceReportingInfo) {
{kManagementExtensionReportUsername, "username"},
{kManagementReportExtensions, "extension"},
{kManagementReportAndroidApplications, "android application"},
- {kManagementReportDlpEvents, "dlp events"}};
+ {kManagementReportDlpEvents, "dlp events"},
+ {kManagementReportLoginLogout, "login-logout"}};
ASSERT_PRED_FORMAT2(ReportingElementsToBeEQ, info, expected_elements);
}
@@ -1112,7 +1146,8 @@ TEST_F(ManagementUIHandlerTests,
{kManagementCrostiniContainerConfiguration, "crostini"},
{kManagementExtensionReportUsername, "username"},
{kManagementReportExtensions, "extension"},
- {kManagementReportAndroidApplications, "android application"}};
+ {kManagementReportAndroidApplications, "android application"},
+ {kManagementReportLoginLogout, "login-logout"}};
ASSERT_PRED_FORMAT2(ReportingElementsToBeEQ, info, expected_elements);
}
@@ -1185,7 +1220,8 @@ TEST_F(ManagementUIHandlerTests, ReportDeviceXdrEventsEnabled) {
const base::Value::List info = SetUpForReportingInfo();
const std::map<std::string, std::string> expected_elements = {
{kManagementReportActivityTimes, "device activity"},
- {kManagementReportAppInfoAndActivity, "app info and activity"}};
+ {kManagementReportAppInfoAndActivity, "app info and activity"},
+ {kManagementReportLoginLogout, "login-logout"}};
ASSERT_PRED_FORMAT2(ReportingElementsToBeEQ, info, expected_elements);
}
@@ -1214,6 +1250,16 @@ TEST_F(ManagementUIHandlerTests, ReportAppUsage) {
ASSERT_PRED_FORMAT2(ReportingElementsToBeEQ, info, expected_elements);
}
+TEST_F(ManagementUIHandlerTests, ReportLegacyTechReport) {
+ ResetTestConfig(false);
+ GetTestConfig().legacy_tech_reporting_enabled = true;
+ const base::Value::List info = SetUpForReportingInfo();
+ const std::map<std::string, std::string> expected_elements = {
+ {kManagementLegacyTechReport, "legacy-tech"}};
+
+ ASSERT_PRED_FORMAT2(ReportingElementsToBeEQ, info, expected_elements);
+}
+
TEST_F(ManagementUIHandlerTests,
ShowPrivacyDisclosureForSecureDnsWithIdentifiers) {
ResetTestConfig();
@@ -1324,18 +1370,20 @@ TEST_F(ManagementUIHandlerTests, HideProxyServerDisclosureForDirectProxy) {
#endif
TEST_F(ManagementUIHandlerTests, ExtensionReportingInfoNoPolicySetNoMessage) {
+ ResetTestConfig();
+ SetUpProfileAndHandler();
auto reporting_info =
- handler_.GetExtensionReportingInfo(/*can_collect_signals=*/false);
+ handler_.GetReportingInfo(/*can_collect_signals=*/false);
EXPECT_EQ(reporting_info.size(), 0u);
}
TEST_F(ManagementUIHandlerTests, CloudReportingPolicy) {
- policy::PolicyMap chrome_policies;
- const policy::PolicyNamespace chrome_policies_namespace =
- policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string());
+ ResetTestConfig();
+ policy::PolicyMap policies;
EXPECT_CALL(policy_service_, GetPolicies(_))
- .WillRepeatedly(ReturnRef(chrome_policies));
- SetPolicyValue(policy::key::kCloudReportingEnabled, true, chrome_policies);
+ .WillRepeatedly(ReturnRef(policies));
+ local_state_.SetBoolean(enterprise_reporting::kCloudReportingEnabled, true);
+ SetUpProfileAndHandler();
std::set<std::string> expected_messages = {
kManagementExtensionReportMachineName, kManagementExtensionReportUsername,
@@ -1344,32 +1392,56 @@ TEST_F(ManagementUIHandlerTests, CloudReportingPolicy) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
expected_messages.insert(kManagementDeviceSignalsDisclosure);
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
- ASSERT_PRED_FORMAT2(MessagesToBeEQ, handler_.GetExtensionReportingInfo(),
+
+ ASSERT_PRED_FORMAT2(MessagesToBeEQ, handler_.GetReportingInfo(),
expected_messages);
}
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
TEST_F(ManagementUIHandlerTests,
CloudReportingPolicyWithoutDeviceSignalsConsent) {
- policy::PolicyMap chrome_policies;
- const policy::PolicyNamespace chrome_policies_namespace =
- policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string());
+ ResetTestConfig();
+ SetUpProfileAndHandler();
+ policy::PolicyMap policies;
EXPECT_CALL(policy_service_, GetPolicies(_))
- .WillRepeatedly(ReturnRef(chrome_policies));
- SetPolicyValue(policy::key::kCloudReportingEnabled, true, chrome_policies);
+ .WillRepeatedly(ReturnRef(policies));
+ local_state_.SetBoolean(enterprise_reporting::kCloudReportingEnabled, true);
std::set<std::string> expected_messages = {
kManagementExtensionReportMachineName, kManagementExtensionReportUsername,
kManagementExtensionReportVersion,
kManagementExtensionReportExtensionsPlugin};
- ASSERT_PRED_FORMAT2(
- MessagesToBeEQ,
- handler_.GetExtensionReportingInfo(/*can_collect_signals=*/false),
- expected_messages);
+ ASSERT_PRED_FORMAT2(MessagesToBeEQ,
+ handler_.GetReportingInfo(/*can_collect_signals=*/false),
+ expected_messages);
}
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+TEST_F(ManagementUIHandlerTests, LegacyTechReport) {
+ ResetTestConfig();
+ policy::PolicyMap policies;
+ EXPECT_CALL(policy_service_, GetPolicies(_))
+ .WillRepeatedly(ReturnRef(policies));
+ SetUpProfileAndHandler();
+
+ base::Value::List allowlist;
+ allowlist.Append(base::Value("www.example.com"));
+ profile_->GetTestingPrefService()->SetManagedPref(
+ enterprise_reporting::kCloudLegacyTechReportAllowlist,
+ std::make_unique<base::Value>(std::move(allowlist)));
+
+ std::set<std::string> expected_messages = {kManagementLegacyTechReport};
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+ expected_messages.insert(kManagementDeviceSignalsDisclosure);
+#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+
+ ASSERT_PRED_FORMAT2(MessagesToBeEQ, handler_.GetReportingInfo(),
+ expected_messages);
+}
+
TEST_F(ManagementUIHandlerTests, ExtensionReportingInfoPoliciesMerge) {
+ ResetTestConfig();
+ SetUpProfileAndHandler();
policy::PolicyMap on_prem_reporting_extension_beta_policies;
policy::PolicyMap on_prem_reporting_extension_stable_policies;
@@ -1405,12 +1477,7 @@ TEST_F(ManagementUIHandlerTests, ExtensionReportingInfoPoliciesMerge) {
EXPECT_CALL(policy_service_,
GetPolicies(on_prem_reporting_extension_beta_policy_namespace))
.WillOnce(ReturnRef(on_prem_reporting_extension_beta_policies));
- policy::PolicyMap chrome_policies;
- EXPECT_CALL(policy_service_,
- GetPolicies(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
- std::string())))
- .WillOnce(ReturnRef(chrome_policies));
- SetPolicyValue(policy::key::kCloudReportingEnabled, true, chrome_policies);
+ local_state_.SetBoolean(enterprise_reporting::kCloudReportingEnabled, true);
std::set<std::string> expected_messages = {
kManagementExtensionReportMachineNameAddress,
@@ -1418,12 +1485,12 @@ TEST_F(ManagementUIHandlerTests, ExtensionReportingInfoPoliciesMerge) {
kManagementExtensionReportVersion,
kManagementExtensionReportExtensionsPlugin,
kManagementExtensionReportUserBrowsingData,
- kManagementExtensionReportPerfCrash};
+ kManagementExtensionReportPerfCrash,
+ kManagementLegacyTechReport};
- ASSERT_PRED_FORMAT2(
- MessagesToBeEQ,
- handler_.GetExtensionReportingInfo(/*can_collect_signals=*/false),
- expected_messages);
+ ASSERT_PRED_FORMAT2(MessagesToBeEQ,
+ handler_.GetReportingInfo(/*can_collect_signals=*/false),
+ expected_messages);
}
TEST_F(ManagementUIHandlerTests, ManagedWebsitiesInfoNoPolicySet) {
diff --git a/chromium/chrome/browser/ui/webui/management/management_ui_pwa_browsertest.cc b/chromium/chrome/browser/ui/webui/management/management_ui_pwa_browsertest.cc
new file mode 100644
index 00000000000..96f2e0f68e4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/management/management_ui_pwa_browsertest.cc
@@ -0,0 +1,102 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/json/json_reader.h"
+#include "base/value_iterators.h"
+#include "chrome/browser/apps/app_service/app_icon/app_icon_source.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
+#include "chrome/browser/ui/webui/management/management_ui.h"
+#include "chrome/browser/ui/webui/management/management_ui_handler.h"
+#include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_test_utils.h"
+#include "chrome/browser/web_applications/web_app_constants.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+constexpr char kTestApp[] = "https://test.test/";
+
+class ManagementUIPWATest : public web_app::WebAppControllerBrowserTest {
+ public:
+ ManagementUIPWATest() { BuildAndInitFeatureList(); }
+
+ protected:
+ void BuildAndInitFeatureList() {
+ scoped_feature_list_.InitWithFeatures(
+ /*enabled_features=*/{features::kDesktopPWAsEnforceWebAppSettingsPolicy,
+ features::kDesktopPWAsRunOnOsLogin},
+ /*disabled_features=*/{});
+ }
+
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(ManagementUIPWATest, RunOnOsLoginApplicationsReported) {
+ // Set up policy values and install PWAs
+ profile()->GetPrefs()->SetList(
+ prefs::kWebAppSettings,
+ base::Value::List().Append(
+ base::Value::Dict()
+ .Set(web_app::kManifestId, kTestApp)
+ .Set(web_app::kRunOnOsLogin, web_app::kRunWindowed)));
+
+ const web_app::AppId& app_id = InstallPWA(GURL(kTestApp));
+
+ // Check that applications contains given app
+ ASSERT_TRUE(
+ ui_test_utils::NavigateToURL(browser(), GURL("chrome://management")));
+
+ const std::string javascript =
+ "window.ManagementBrowserProxyImpl.getInstance()"
+ " .getApplications()"
+ " .then(result => "
+ " JSON.stringify(result));";
+
+ content::WebContents* contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ std::string actual_json =
+ content::EvalJs(contents, javascript).ExtractString();
+
+ absl::optional<base::Value> actual_value =
+ base::JSONReader::Read(actual_json);
+
+ ASSERT_TRUE(actual_value.has_value());
+
+ const std::string app_name = web_app::WebAppProvider::GetForTest(profile())
+ ->registrar_unsafe()
+ .GetAppShortName(app_id);
+
+ base::Value::List expected_value;
+ base::Value::Dict app_info;
+ app_info.Set("name", app_name);
+ GURL icon = apps::AppIconSource::GetIconURL(
+ app_id, extension_misc::EXTENSION_ICON_SMALLISH);
+ app_info.Set("icon", icon.spec());
+ base::Value::List permission_messages;
+ permission_messages.Append(
+ l10n_util::GetStringUTF16(IDS_MANAGEMENT_APPLICATIONS_RUN_ON_OS_LOGIN));
+ app_info.Set("permissions", std::move(permission_messages));
+ expected_value.Append(std::move(app_info));
+
+ EXPECT_EQ(actual_value.value(), expected_value);
+
+ base::Value::List& values = actual_value->GetList();
+ base::Value& actual_app = values[0];
+
+ ASSERT_EQ(*actual_app.GetDict().FindString("name"), app_name);
+}
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/media_router/cast_feedback_ui.cc b/chromium/chrome/browser/ui/webui/media_router/cast_feedback_ui.cc
index 086d571407f..76ce9d4b015 100644
--- a/chromium/chrome/browser/ui/webui/media_router/cast_feedback_ui.cc
+++ b/chromium/chrome/browser/ui/webui/media_router/cast_feedback_ui.cc
@@ -131,13 +131,27 @@ CastFeedbackUI::CastFeedbackUI(content::WebUI* web_ui)
JSONStringValueSerializer serializer(&log_data);
serializer.set_pretty_print(true);
- if (!serializer.Serialize(router->GetState()))
+ if (!serializer.Serialize(router->GetState())) {
log_data.clear();
+ }
LoggerImpl* const logger = router->GetLogger();
if (logger) {
log_data += logger->GetLogsAsJson();
+ }
+
+ MediaRouterDebugger& debugger = router->GetDebugger();
+ if (debugger.ShouldFetchMirroringStats()) {
+ std::string mirroring_stats_json;
+ JSONStringValueSerializer mirroring_stats_serializer(&mirroring_stats_json);
+ mirroring_stats_serializer.set_pretty_print(true);
+ if (mirroring_stats_serializer.Serialize(debugger.GetMirroringStats())) {
+ log_data += mirroring_stats_json;
+ }
+ }
+ // If there is any log data, add it to the `source`.
+ if (!log_data.empty()) {
source->AddString("logData", log_data);
}
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.cc
index 8bcb247fc69..6d807ac87d8 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.cc
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.cc
@@ -55,6 +55,11 @@ void MediaRouterInternalsWebUIMessageHandler::RegisterMessages() {
&MediaRouterInternalsWebUIMessageHandler::HandleGetLogs,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "getMirroringStats",
+ base::BindRepeating(
+ &MediaRouterInternalsWebUIMessageHandler::HandleGetMirroringStats,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"setMirroringStatsEnabled",
base::BindRepeating(&MediaRouterInternalsWebUIMessageHandler::
HandleSetMirroringStatsEnabled,
@@ -115,6 +120,13 @@ void MediaRouterInternalsWebUIMessageHandler::OnProviderState(
}
}
+void MediaRouterInternalsWebUIMessageHandler::HandleGetMirroringStats(
+ const base::Value::List& args) {
+ AllowJavascript();
+ const base::Value& callback_id = args[0];
+ ResolveJavascriptCallback(callback_id, debugger_->GetMirroringStats());
+}
+
void MediaRouterInternalsWebUIMessageHandler::HandleSetMirroringStatsEnabled(
const base::Value::List& args) {
AllowJavascript();
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h b/chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h
index 343c49c7b0d..ec81bc916fa 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h
@@ -37,6 +37,7 @@ class MediaRouterInternalsWebUIMessageHandler
void HandleGetProviderState(const base::Value::List& args);
void HandleGetLogs(const base::Value::List& args);
+ void HandleGetMirroringStats(const base::Value::List& args);
void HandleSetMirroringStatsEnabled(const base::Value::List& args);
void HandleIsMirroringStatsEnabled(const base::Value::List& args);
diff --git a/chromium/chrome/browser/ui/webui/memory_internals_ui.cc b/chromium/chrome/browser/ui/webui/memory_internals_ui.cc
index 89a55aaefd2..75e911c485c 100644
--- a/chromium/chrome/browser/ui/webui/memory_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/memory_internals_ui.cc
@@ -10,7 +10,7 @@
#include <utility>
#include <vector>
-#include "base/allocator/buildflags.h"
+#include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/DEPS b/chromium/chrome/browser/ui/webui/nearby_internals/DEPS
new file mode 100644
index 00000000000..4cfd59f1543
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/cross_device/logging",
+]
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_contact_handler.cc b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_contact_handler.cc
index 4bcc2245587..2bb13a68800 100644
--- a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_contact_handler.cc
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_contact_handler.cc
@@ -10,10 +10,10 @@
#include "base/functional/bind.h"
#include "base/json/json_writer.h"
#include "base/time/time.h"
-#include "chrome/browser/nearby_sharing/logging/logging.h"
#include "chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
+#include "components/cross_device/logging/logging.h"
namespace {
@@ -105,7 +105,7 @@ void NearbyInternalsContactHandler::OnJavascriptAllowed() {
if (service_) {
observation_.Observe(service_->GetContactManager());
} else {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
}
}
@@ -125,7 +125,7 @@ void NearbyInternalsContactHandler::HandleDownloadContacts(
if (service_) {
service_->GetContactManager()->DownloadContacts();
} else {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
}
}
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc
index 44c8370e978..75fc9a3b311 100644
--- a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_http_handler.cc
@@ -14,10 +14,10 @@
#include "chrome/browser/nearby_sharing/client/nearby_share_http_notifier.h"
#include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h"
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h"
-#include "chrome/browser/nearby_sharing/logging/logging.h"
#include "chrome/browser/nearby_sharing/logging/proto_to_dictionary_conversion.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
+#include "components/cross_device/logging/logging.h"
namespace {
@@ -100,7 +100,7 @@ void NearbyInternalsHttpHandler::OnJavascriptAllowed() {
if (service_) {
observation_.Observe(service_->GetHttpNotifier());
} else {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
}
}
@@ -119,7 +119,7 @@ void NearbyInternalsHttpHandler::UpdateDevice(const base::Value::List& args) {
if (service_) {
service_->GetLocalDeviceDataManager()->DownloadDeviceData();
} else {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
}
}
@@ -130,7 +130,7 @@ void NearbyInternalsHttpHandler::ListPublicCertificates(
if (service_) {
service_->GetCertificateManager()->DownloadPublicCertificates();
} else {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
}
}
@@ -141,7 +141,7 @@ void NearbyInternalsHttpHandler::ListContactPeople(
if (service_) {
service_->GetContactManager()->DownloadContacts();
} else {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
}
}
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.cc b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.cc
index bce692a1a0d..424a2574fcd 100644
--- a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.cc
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.cc
@@ -11,8 +11,10 @@
#include "base/values.h"
namespace {
+
// Keys in the JSON representation of a log message
const char kLogMessageTextKey[] = "text";
+const char kLogMessageFeatureKey[] = "feature";
const char kLogMessageTimeKey[] = "time";
const char kLogMessageFileKey[] = "file";
const char kLogMessageLineKey[] = "line";
@@ -21,9 +23,10 @@ const char kLogMessageSeverityKey[] = "severity";
// Converts |log_message| to a raw dictionary value used as a JSON argument to
// JavaScript functions.
base::Value::Dict LogMessageToDictionary(
- const LogBuffer::LogMessage& log_message) {
+ const CrossDeviceLogBuffer::LogMessage& log_message) {
base::Value::Dict dictionary;
dictionary.Set(kLogMessageTextKey, log_message.text);
+ dictionary.Set(kLogMessageFeatureKey, int(log_message.feature));
dictionary.Set(kLogMessageTimeKey,
base::TimeFormatTimeOfDayWithMilliseconds(log_message.time));
dictionary.Set(kLogMessageFileKey, log_message.file);
@@ -45,7 +48,7 @@ void NearbyInternalsLogsHandler::RegisterMessages() {
}
void NearbyInternalsLogsHandler::OnJavascriptAllowed() {
- observation_.Observe(LogBuffer::GetInstance());
+ observation_.Observe(CrossDeviceLogBuffer::GetInstance());
}
void NearbyInternalsLogsHandler::OnJavascriptDisallowed() {
@@ -57,17 +60,17 @@ void NearbyInternalsLogsHandler::HandleGetLogMessages(
AllowJavascript();
const base::Value& callback_id = args[0];
base::Value::List list;
- for (const auto& log : *LogBuffer::GetInstance()->logs()) {
+ for (const auto& log : *CrossDeviceLogBuffer::GetInstance()->logs()) {
list.Append(LogMessageToDictionary(log));
}
ResolveJavascriptCallback(callback_id, list);
}
-void NearbyInternalsLogsHandler::OnLogBufferCleared() {
+void NearbyInternalsLogsHandler::OnCrossDeviceLogBufferCleared() {
FireWebUIListener("log-buffer-cleared");
}
void NearbyInternalsLogsHandler::OnLogMessageAdded(
- const LogBuffer::LogMessage& log_message) {
+ const CrossDeviceLogBuffer::LogMessage& log_message) {
FireWebUIListener("log-message-added", LogMessageToDictionary(log_message));
}
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.h b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.h
index aac25039c1b..7933453c63f 100644
--- a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.h
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_logs_handler.h
@@ -7,14 +7,14 @@
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/values.h"
-#include "chrome/browser/nearby_sharing/logging/log_buffer.h"
-#include "chrome/browser/nearby_sharing/logging/logging.h"
+#include "components/cross_device/logging/log_buffer.h"
+#include "components/cross_device/logging/logging.h"
#include "content/public/browser/web_ui_message_handler.h"
// WebUIMessageHandler for the NS_LOG Macro to pass logging messages to the
// chrome://nearby-internals logging tab.
class NearbyInternalsLogsHandler : public content::WebUIMessageHandler,
- public LogBuffer::Observer {
+ public CrossDeviceLogBuffer::Observer {
public:
NearbyInternalsLogsHandler();
NearbyInternalsLogsHandler(const NearbyInternalsLogsHandler&) = delete;
@@ -28,17 +28,21 @@ class NearbyInternalsLogsHandler : public content::WebUIMessageHandler,
void OnJavascriptDisallowed() override;
private:
- // LogBuffer::Observer
- void OnLogMessageAdded(const LogBuffer::LogMessage& log_message) override;
- void OnLogBufferCleared() override;
+ int FeatureEnumToInt(Feature feature);
+
+ // CrossDeviceLogBuffer::Observer
+ void OnLogMessageAdded(
+ const CrossDeviceLogBuffer::LogMessage& log_message) override;
+ void OnCrossDeviceLogBufferCleared() override;
// Message handler callback that returns the Log Buffer in dictionary form.
void HandleGetLogMessages(const base::Value::List& args);
// Message handler callback that clears the Log Buffer.
- void ClearLogBuffer(const base::Value::List& args);
+ void ClearCrossDeviceLogBuffer(const base::Value::List& args);
- base::ScopedObservation<LogBuffer, LogBuffer::Observer> observation_{this};
+ base::ScopedObservation<CrossDeviceLogBuffer, CrossDeviceLogBuffer::Observer>
+ observation_{this};
base::WeakPtrFactory<NearbyInternalsLogsHandler> weak_ptr_factory_{this};
};
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_prefs_handler.cc b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_prefs_handler.cc
index 187554f4662..09ffb0956b3 100644
--- a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_prefs_handler.cc
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_prefs_handler.cc
@@ -5,8 +5,8 @@
#include "chrome/browser/ui/webui/nearby_internals/nearby_internals_prefs_handler.h"
#include "base/functional/bind.h"
#include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
-#include "chrome/browser/nearby_sharing/logging/logging.h"
#include "chrome/browser/profiles/profile.h"
+#include "components/cross_device/logging/logging.h"
namespace {
@@ -47,6 +47,6 @@ void NearbyInternalsPrefsHandler::HandleClearNearbyPrefs(
// Add log message so users who trigger the Clear Pref button on
// chrome://nearby-internals know that the Nearby prefs have been cleared.
- NS_LOG(INFO)
+ CD_LOG(INFO, Feature::NS)
<< "Nearby Share has been disabled and Nearby prefs have been cleared.";
}
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.cc b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.cc
index 34bc406b56b..02ada751fde 100644
--- a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.cc
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.cc
@@ -4,8 +4,11 @@
#include "chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.h"
#include "chrome/browser/ash/nearby/presence/nearby_presence_service_factory.h"
-#include "chrome/browser/nearby_sharing/logging/logging.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/ash/components/nearby/presence/credentials/prefs.h"
#include "chromeos/ash/components/nearby/presence/nearby_presence_service.h"
+#include "components/cross_device/logging/logging.h"
+#include "components/prefs/pref_service.h"
namespace {
@@ -14,6 +17,43 @@ const char kDeviceNameKey[] = "name";
const char kDeviceIdKey[] = "id";
const char kTypeKey[] = "type";
const char kEndpointKey[] = "endpoint_id";
+const char kActionsKey[] = "actions";
+
+// ActionType strings representations.
+const char kActiveUnlockAction[] = "Active Unlock";
+const char kNearbyShareAction[] = "Nearby Share";
+const char kInstantTetheringAction[] = "Instant Tethering";
+const char kPhoneHubAction[] = "Phone Hub";
+const char kPresenceManagerAction[] = "Presence Manager";
+const char kFinderAction[] = "Finder";
+const char kFastPairSassAction[] = "Fast Pair Sass";
+const char kTapToTransferAction[] = "Tap To Transfer";
+const char kLastAction[] = "Invalid Action";
+
+std::string PresenceActionToString(
+ ash::nearby::presence::NearbyPresenceService::Action action_enum) {
+ switch (action_enum) {
+ case ash::nearby::presence::NearbyPresenceService::Action::kActiveUnlock:
+ return kActiveUnlockAction;
+ case ash::nearby::presence::NearbyPresenceService::Action::kNearbyShare:
+ return kNearbyShareAction;
+ case ash::nearby::presence::NearbyPresenceService::Action::
+ kInstantTethering:
+ return kInstantTetheringAction;
+ case ash::nearby::presence::NearbyPresenceService::Action::kPhoneHub:
+ return kPhoneHubAction;
+ case ash::nearby::presence::NearbyPresenceService::Action::kPresenceManager:
+ return kPresenceManagerAction;
+ case ash::nearby::presence::NearbyPresenceService::Action::kFinder:
+ return kFinderAction;
+ case ash::nearby::presence::NearbyPresenceService::Action::kFastPairSass:
+ return kFastPairSassAction;
+ case ash::nearby::presence::NearbyPresenceService::Action::kTapToTransfer:
+ return kTapToTransferAction;
+ case ash::nearby::presence::NearbyPresenceService::Action::kLast:
+ return kLastAction;
+ }
+}
// Converts |presence_device| to a raw dictionary value used as a JSON argument
// to JavaScript functions.
@@ -32,6 +72,17 @@ base::Value::Dict PresenceDeviceToDictionary(
}
dictionary.Set(kEndpointKey, presence_device.GetEndpointId());
+ std::string actions_list;
+ for (auto action : presence_device.GetActions()) {
+ actions_list += PresenceActionToString(action);
+ actions_list += ", ";
+ }
+
+ // Remove the trailing comma and whitespace.
+ actions_list.pop_back();
+ actions_list.pop_back();
+
+ dictionary.Set(kActionsKey, actions_list);
return dictionary;
}
@@ -54,6 +105,11 @@ void NearbyInternalsPresenceHandler::RegisterMessages() {
&NearbyInternalsPresenceHandler::HandleStartPresenceScan,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "StopPresenceScan",
+ base::BindRepeating(
+ &NearbyInternalsPresenceHandler::HandleStopPresenceScan,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"SyncPresenceCredentials",
base::BindRepeating(
&NearbyInternalsPresenceHandler::HandleSyncPresenceCredentials,
@@ -63,6 +119,11 @@ void NearbyInternalsPresenceHandler::RegisterMessages() {
base::BindRepeating(
&NearbyInternalsPresenceHandler::HandleFirstTimePresenceFlow,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "ConnectToPresenceDevice",
+ base::BindRepeating(
+ &NearbyInternalsPresenceHandler::HandleConnectToPresenceDevice,
+ base::Unretained(this)));
}
void NearbyInternalsPresenceHandler::OnJavascriptAllowed() {}
@@ -79,10 +140,10 @@ void NearbyInternalsPresenceHandler::HandleStartPresenceScan(
ash::nearby::presence::NearbyPresenceServiceFactory::GetForBrowserContext(
context_);
if (service) {
- NS_LOG(VERBOSE) << __func__
- << ": NearbyPresenceService was retrieved successfully";
+ CD_LOG(VERBOSE, Feature::NP)
+ << __func__ << ": NearbyPresenceService was retrieved successfully";
ash::nearby::presence::NearbyPresenceService::ScanFilter filter(
- ash::nearby::presence::NearbyPresenceService::IdentityType::kPrivate,
+ ash::nearby::presence::NearbyPresenceService::IdentityType::kPublic,
/*actions=*/{});
service->StartScan(
filter, /*scan_delegate=*/this,
@@ -91,15 +152,20 @@ void NearbyInternalsPresenceHandler::HandleStartPresenceScan(
}
}
+void NearbyInternalsPresenceHandler::HandleStopPresenceScan(
+ const base::Value::List& args) {
+ scan_session_.reset();
+}
+
void NearbyInternalsPresenceHandler::HandleSyncPresenceCredentials(
const base::Value::List& args) {
ash::nearby::presence::NearbyPresenceService* service =
ash::nearby::presence::NearbyPresenceServiceFactory::GetForBrowserContext(
context_);
if (service) {
- NS_LOG(VERBOSE) << __func__
- << ": NearbyPresenceService was retrieved successfully";
- // TODO(b/276307539): Call NPS function to sync credentials.
+ CD_LOG(VERBOSE, Feature::NP)
+ << __func__ << ": NearbyPresenceService was retrieved successfully";
+ service->UpdateCredentials();
}
}
@@ -109,26 +175,44 @@ void NearbyInternalsPresenceHandler::HandleFirstTimePresenceFlow(
ash::nearby::presence::NearbyPresenceServiceFactory::GetForBrowserContext(
context_);
if (service) {
- NS_LOG(VERBOSE) << __func__
- << ": NearbyPresenceService was retrieved successfully";
- // TODO(b/276307539): Call NPS function to initiate first time flow.
+ CD_LOG(VERBOSE, Feature::NP)
+ << __func__ << ": NearbyPresenceService was retrieved successfully";
+ auto* pref_service = Profile::FromBrowserContext(context_)->GetPrefs();
+
+ // Reset the state that indicates that first time registration was
+ // completed for testing. This will trigger the first time flow in
+ // `NearbyPresenceService::Initialize()`, in the case that this was already
+ // set on the device for manual testing.
+ pref_service->SetBoolean(ash::nearby::presence::prefs::
+ kNearbyPresenceFirstTimeRegistrationComplete,
+ false);
+ service->Initialize(
+ base::BindOnce(&NearbyInternalsPresenceHandler::
+ OnNearbyPresenceCredentialManagerInitialized,
+ weak_ptr_factory_.GetWeakPtr()));
}
}
void NearbyInternalsPresenceHandler::OnScanStarted(
std::unique_ptr<ash::nearby::presence::NearbyPresenceService::ScanSession>
scan_session,
- ash::nearby::presence::mojom::StatusCode status) {
- if (status == ash::nearby::presence::mojom::StatusCode::kOk) {
+ ash::nearby::presence::NearbyPresenceService::StatusCode status) {
+ if (status ==
+ ash::nearby::presence::NearbyPresenceService::StatusCode::kAbslOk) {
scan_session_ = std::move(scan_session);
- NS_LOG(VERBOSE) << __func__
- << ": ScanSession remote successfully returned and bound.";
+ CD_LOG(VERBOSE, Feature::NP)
+ << __func__ << ": ScanSession remote successfully returned and bound.";
} else {
// TODO(b/276307539): Pass error status back to WebUI.
return;
}
}
+void NearbyInternalsPresenceHandler::
+ OnNearbyPresenceCredentialManagerInitialized() {
+ CD_LOG(VERBOSE, Feature::NP) << __func__;
+}
+
void NearbyInternalsPresenceHandler::OnPresenceDeviceFound(
const ash::nearby::presence::NearbyPresenceService::PresenceDevice&
presence_device) {
@@ -154,3 +238,11 @@ void NearbyInternalsPresenceHandler::OnScanSessionInvalidated() {
scan_session_.reset();
HandleStartPresenceScan(/*args=*/{});
}
+
+void NearbyInternalsPresenceHandler::HandleConnectToPresenceDevice(
+ const base::Value::List& args) {
+ // TODO(b/276642472): Add connect functionality.
+ CD_LOG(VERBOSE, Feature::NP)
+ << __func__ << ": Connection attempt for device with endpoint id: "
+ << args[0].GetString();
+}
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.h b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.h
index 710f2abbab2..7ea5072f9e2 100644
--- a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.h
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_presence_handler.h
@@ -46,13 +46,17 @@ class NearbyInternalsPresenceHandler
void Initialize(const base::Value::List& args);
void HandleStartPresenceScan(const base::Value::List& args);
+ void HandleStopPresenceScan(const base::Value::List& args);
void HandleSyncPresenceCredentials(const base::Value::List& args);
void HandleFirstTimePresenceFlow(const base::Value::List& args);
void OnScanStarted(
std::unique_ptr<ash::nearby::presence::NearbyPresenceService::ScanSession>
scan_session,
- ash::nearby::presence::mojom::StatusCode status);
+ ash::nearby::presence::NearbyPresenceService::StatusCode status);
+ void OnNearbyPresenceCredentialManagerInitialized();
+
+ void HandleConnectToPresenceDevice(const base::Value::List& args);
private:
const raw_ptr<content::BrowserContext> context_;
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.cc b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.cc
index 95ec6e2a2f8..00522d4eed2 100644
--- a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.cc
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.cc
@@ -11,10 +11,12 @@
#include "base/functional/bind.h"
#include "base/time/time.h"
#include "chrome/browser/nearby_sharing/attachment.h"
-#include "chrome/browser/nearby_sharing/logging/logging.h"
+#include "chrome/browser/nearby_sharing/nearby_notification_manager.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
+#include "chrome/browser/nearby_sharing/share_target.h"
#include "chrome/browser/nearby_sharing/text_attachment.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_share_target_types.mojom.h"
+#include "components/cross_device/logging/logging.h"
namespace {
@@ -38,6 +40,10 @@ const char kIsScanning[] = "isScanning";
const char kIsSending[] = "isSending";
const char kIsTransferring[] = "isTransferring";
+// KFields used in ShowReceiveNotification.
+const char kShareTargetFakeFullName[] = "Daniel's Rotom";
+const char kTextAttachmentFakeBodyText[] = "Long text that should be truncated";
+
// TriggerEvents in alphabetical order.
enum class TriggerEvent {
kAccept,
@@ -296,6 +302,11 @@ void NearbyInternalsUiTriggerHandler::RegisterMessages() {
"getStates",
base::BindRepeating(&NearbyInternalsUiTriggerHandler::GetState,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showNearbyShareReceivedNotification",
+ base::BindRepeating(
+ &NearbyInternalsUiTriggerHandler::ShowReceivedNotification,
+ base::Unretained(this)));
}
void NearbyInternalsUiTriggerHandler::InitializeContents(
@@ -308,7 +319,7 @@ void NearbyInternalsUiTriggerHandler::RegisterSendSurfaceForeground(
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
@@ -326,7 +337,7 @@ void NearbyInternalsUiTriggerHandler::RegisterSendSurfaceBackground(
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
@@ -344,7 +355,7 @@ void NearbyInternalsUiTriggerHandler::UnregisterSendSurface(
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
@@ -360,7 +371,7 @@ void NearbyInternalsUiTriggerHandler::RegisterReceiveSurfaceForeground(
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
@@ -378,7 +389,7 @@ void NearbyInternalsUiTriggerHandler::RegisterReceiveSurfaceBackground(
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
@@ -396,7 +407,7 @@ void NearbyInternalsUiTriggerHandler::UnregisterReceiveSurface(
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
@@ -462,15 +473,15 @@ void NearbyInternalsUiTriggerHandler::SendText(const base::Value::List& args) {
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
std::string share_target_id = args[1].GetString();
auto it = id_to_share_target_map_.find(share_target_id);
if (it == id_to_share_target_map_.end()) {
- NS_LOG(ERROR) << "Invalid ShareTarget ID " << share_target_id
- << " for SendText.";
+ CD_LOG(ERROR, Feature::NS)
+ << "Invalid ShareTarget ID " << share_target_id << " for SendText.";
return;
}
@@ -491,15 +502,15 @@ void NearbyInternalsUiTriggerHandler::Accept(const base::Value::List& args) {
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
std::string share_target_id = args[0].GetString();
auto it = id_to_share_target_map_.find(share_target_id);
if (it == id_to_share_target_map_.end()) {
- NS_LOG(ERROR) << "Invalid ShareTarget ID " << share_target_id
- << " for Accept.";
+ CD_LOG(ERROR, Feature::NS)
+ << "Invalid ShareTarget ID " << share_target_id << " for Accept.";
return;
}
@@ -513,15 +524,15 @@ void NearbyInternalsUiTriggerHandler::Open(const base::Value::List& args) {
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
std::string share_target_id = args[0].GetString();
auto it = id_to_share_target_map_.find(share_target_id);
if (it == id_to_share_target_map_.end()) {
- NS_LOG(ERROR) << "Invalid ShareTarget ID " << share_target_id
- << " for Open.";
+ CD_LOG(ERROR, Feature::NS)
+ << "Invalid ShareTarget ID " << share_target_id << " for Open.";
return;
}
@@ -534,15 +545,15 @@ void NearbyInternalsUiTriggerHandler::Reject(const base::Value::List& args) {
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
std::string share_target_id = args[0].GetString();
auto it = id_to_share_target_map_.find(share_target_id);
if (it == id_to_share_target_map_.end()) {
- NS_LOG(ERROR) << "Invalid ShareTarget ID " << share_target_id
- << " for Reject.";
+ CD_LOG(ERROR, Feature::NS)
+ << "Invalid ShareTarget ID " << share_target_id << " for Reject.";
return;
}
@@ -556,15 +567,15 @@ void NearbyInternalsUiTriggerHandler::Cancel(const base::Value::List& args) {
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
std::string share_target_id = args[0].GetString();
auto it = id_to_share_target_map_.find(share_target_id);
if (it == id_to_share_target_map_.end()) {
- NS_LOG(ERROR) << "Invalid ShareTarget ID " << share_target_id
- << " for Cancel.";
+ CD_LOG(ERROR, Feature::NS)
+ << "Invalid ShareTarget ID " << share_target_id << " for Cancel.";
return;
}
@@ -578,7 +589,7 @@ void NearbyInternalsUiTriggerHandler::GetState(const base::Value::List& args) {
NearbySharingService* service_ =
NearbySharingServiceFactory::GetForBrowserContext(context_);
if (!service_) {
- NS_LOG(ERROR) << "No NearbyShareService instance to call.";
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
return;
}
@@ -590,3 +601,33 @@ void NearbyInternalsUiTriggerHandler::GetState(const base::Value::List& args) {
service_->IsReceivingFile(), service_->IsSendingFile(),
service_->IsConnecting(), service_->IsInHighVisibility()));
}
+
+void NearbyInternalsUiTriggerHandler::ShowReceivedNotification(
+ const base::Value::List& args) {
+ NearbySharingService* service =
+ NearbySharingServiceFactory::GetForBrowserContext(context_);
+ if (!service) {
+ CD_LOG(ERROR, Feature::NS) << "No NearbyShareService instance to call.";
+ return;
+ }
+
+ NearbyNotificationManager* manager = service->GetNotificationManager();
+
+ if (!manager) {
+ CD_LOG(ERROR, Feature::NS)
+ << "No NearbyNotificationManager instance to call.";
+ return;
+ }
+
+ // Create a share target with a fake text attachment.
+ TextAttachment attachment(TextAttachment::Type::kText,
+ kTextAttachmentFakeBodyText,
+ /*title=*/absl::nullopt,
+ /*mime_type=*/absl::nullopt);
+ ShareTarget target;
+ target.is_incoming = true;
+ target.device_name = kShareTargetFakeFullName;
+ attachment.MoveToShareTarget(target);
+
+ manager->ShowSuccess(target);
+}
diff --git a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.h b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.h
index 118234d689a..89daed03b28 100644
--- a/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.h
+++ b/chromium/chrome/browser/ui/webui/nearby_internals/nearby_internals_ui_trigger_handler.h
@@ -105,6 +105,10 @@ class NearbyInternalsUiTriggerHandler : public content::WebUIMessageHandler,
// the receive surface to be unregistered.
void UnregisterReceiveSurface(const base::Value::List& args);
+ // Message handler callback that calls ShowSuccess in the
+ // NearbySharingService's NearbyNotificationManager.
+ void ShowReceivedNotification(const base::Value::List& args);
+
// Message handler callback that calls IsScanning, IsTransferring,
// IsReceivingFile, IsSendingFile, IsConnecting, and IsInHighVisibility in the
// NearbySharingService and passes booleans to JavaScript to eventually be
diff --git a/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc b/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
index 2ed16041496..542f73345e5 100644
--- a/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
+++ b/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
@@ -93,6 +93,18 @@ NearbyShareDialogUI::NearbyShareDialogUI(content::WebUI* web_ui)
network::mojom::CSPDirectiveName::WorkerSrc,
"worker-src blob: chrome://resources 'self';");
+ html_source->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::TrustedTypes,
+ "trusted-types static-types "
+ // Required by lottie.
+ "lottie-worker-script-loader webui-test-script "
+ // Required by parse-html-subset.
+ "parse-html-subset sanitize-inner-html "
+ // Required by lit-html.
+ "lit-html "
+ // Required by polymer.
+ "polymer-html-literal polymer-template-event-attribute-policy;");
+
html_source->AddBoolean(
"isOnePageOnboardingEnabled",
base::FeatureList::IsEnabled(features::kNearbySharingOnePageOnboarding));
diff --git a/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h b/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
index b70fb5d9b87..30ad58390d2 100644
--- a/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
+++ b/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
@@ -102,7 +102,7 @@ class NearbyShareDialogUI : public ui::MojoWebUIController,
// A pointer to the Sharesheet controller is provided by
// |NearbyShareAction::LaunchAction| when this WebUI controller is created. It
// is used to close the Sharesheet in |HandleClose|.
- raw_ptr<sharesheet::SharesheetController, ExperimentalAsh>
+ raw_ptr<sharesheet::SharesheetController, DanglingUntriaged | ExperimentalAsh>
sharesheet_controller_ = nullptr;
std::vector<std::unique_ptr<Attachment>> attachments_;
diff --git a/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui_browsertest.cc
index d3ef44315a0..b6a9d91c042 100644
--- a/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui_browsertest.cc
@@ -67,7 +67,8 @@ class NearbyShareDialogUITest : public InProcessBrowserTest {
};
std::string BuildCloseScript(CloseReason reason) {
- return base::StringPrintf("chrome.send('close',[%d]);", reason);
+ return base::StringPrintf("chrome.send('close',[%d]);",
+ static_cast<int>(reason));
}
} // namespace
diff --git a/chromium/chrome/browser/ui/webui/nearby_share/shared_resources.cc b/chromium/chrome/browser/ui/webui/nearby_share/shared_resources.cc
index 56e70d0a597..0477eac463e 100644
--- a/chromium/chrome/browser/ui/webui/nearby_share/shared_resources.cc
+++ b/chromium/chrome/browser/ui/webui/nearby_share/shared_resources.cc
@@ -32,6 +32,8 @@ void RegisterNearbySharedStrings(content::WebUIDataSource* data_source) {
{"nearbyShareContactVisibilityAll", IDS_NEARBY_VISIBLITY_ALL_CONTACTS},
{"nearbyShareContactVisibilityAllDescription",
IDS_NEARBY_VISIBLITY_ALL_CONTACTS_DESCRIPTION},
+ {"nearbyShareContactVisiblityContactsButton",
+ IDS_NEARBY_VISIBILITY_CONTACTS_BUTTON},
{"nearbyShareContactVisibilityDownloadFailed",
IDS_NEARBY_CONTACT_VISIBILITY_DOWNLOAD_FAILED},
{"nearbyShareContactVisibilityDownloading",
@@ -45,13 +47,23 @@ void RegisterNearbySharedStrings(content::WebUIDataSource* data_source) {
IDS_NEARBY_VISIBLITY_HIDDEN_DESCRIPTION},
{"nearbyShareContactVisibilityOwnAll",
IDS_NEARBY_CONTACT_VISIBILITY_OWN_ALL},
+ {"nearbyShareContactVisibilityOwnAllSelfShare",
+ IDS_NEARBY_CONTACT_VISIBILITY_OWN_ALL_SELF_SHARE},
{"nearbyShareContactVisibilityOwnNone",
IDS_NEARBY_CONTACT_VISIBILITY_OWN_NONE},
{"nearbyShareContactVisibilityOwnSome",
IDS_NEARBY_CONTACT_VISIBILITY_OWN_SOME},
+ {"nearbyShareContactVisibilityOwnSomeSelfShare",
+ IDS_NEARBY_CONTACT_VISIBILITY_OWN_SOME_SELF_SHARE},
+ {"nearbyShareContactVisibilityOwnYourDevices",
+ IDS_NEARBY_CONTACT_VISIBILITY_OWN_YOUR_DEVICES},
{"nearbyShareContactVisibilitySome", IDS_NEARBY_VISIBLITY_SOME_CONTACTS},
{"nearbyShareContactVisibilitySomeDescription",
IDS_NEARBY_VISIBLITY_SOME_CONTACTS_DESCRIPTION},
+ {"nearbyShareContactVisibilityYourDevices",
+ IDS_NEARBY_VISIBILITY_YOUR_DEVICES},
+ {"nearbyShareContactVisibilityYourDevicesDescription",
+ IDS_NEARBY_VISIBILITY_YOUR_DEVICES_DESCRIPTION},
{"nearbyShareContactVisibilityUnknown", IDS_NEARBY_VISIBLITY_UNKNOWN},
{"nearbyShareContactVisibilityUnknownDescription",
IDS_NEARBY_VISIBLITY_UNKNOWN_DESCRIPTION},
@@ -95,7 +107,10 @@ void RegisterNearbySharedStrings(content::WebUIDataSource* data_source) {
{"nearbySharePreviewMultipleFileTitle",
IDS_NEARBY_PREVIEW_TITLE_MULTIPLE_FILE},
{"nearbyShareSecureConnectionId", IDS_NEARBY_SECURE_CONNECTION_ID},
- {"nearbyShareSettingsHelpCaption", IDS_NEARBY_SETTINGS_HELP_CAPTION},
+ {"nearbyShareSettingsHelpCaptionBottom",
+ IDS_NEARBY_SETTINGS_HELP_CAPTION_BOTTOM},
+ {"nearbyShareSettingsHelpCaptionTop",
+ IDS_NEARBY_SETTINGS_HELP_CAPTION_TOP},
{"nearbyShareVisibilityPageManageContacts",
IDS_NEARBY_VISIBILITY_PAGE_MANAGE_CONTACTS},
{"nearbyShareVisibilityPageSubtitle",
diff --git a/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
index c3e05d7d8c9..94a62cecaf9 100644
--- a/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -14,6 +14,9 @@
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/types/expected.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/webui_util.h"
@@ -35,14 +38,18 @@
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
#include "net/dns/public/host_resolver_results.h"
#include "net/dns/public/resolve_error_info.h"
+#include "net/extras/shared_dictionary/shared_dictionary_isolation_key.h"
+#include "net/extras/shared_dictionary/shared_dictionary_usage_info.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/resources/grit/webui_resources.h"
+#include "url/origin.h"
#include "url/scheme_host_port.h"
using content::BrowserThread;
@@ -193,6 +200,21 @@ class NetInternalsMessageHandler : public content::WebUIMessageHandler {
const absl::optional<net::AddressList>&,
const absl::optional<net::HostResolverEndpointResults>&,
NetInternalsResolveHostClient* dns_lookup_client);
+ void OnClearSharedDictionary(const base::Value::List& list);
+ void OnClearSharedDictionaryCacheForIsolationKey(
+ const base::Value::List& list);
+ void OnGetSharedDictionaryUsageInfo(const base::Value::List& list);
+ void OnGetSharedDictionaryInfo(const base::Value::List& list);
+
+ void OnClearSharedDictionaryDone(const std::string& callback_id);
+ void OnClearSharedDictionaryForIsolationKeyDone(
+ const std::string& callback_id);
+ void OnGetSharedDictionaryUsageInfoDone(
+ const std::string& callback_id,
+ const std::vector<net::SharedDictionaryUsageInfo>& usage_info);
+ void OnGetSharedDictionaryInfoDone(
+ const std::string& callback_id,
+ std::vector<network::mojom::SharedDictionaryInfoPtr> usage_info);
raw_ptr<content::WebUI> web_ui_;
std::set<std::unique_ptr<NetInternalsResolveHostClient>,
@@ -242,6 +264,26 @@ void NetInternalsMessageHandler::RegisterMessages() {
"flushSocketPools",
base::BindRepeating(&NetInternalsMessageHandler::OnFlushSocketPools,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "clearSharedDictionary",
+ base::BindRepeating(&NetInternalsMessageHandler::OnClearSharedDictionary,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "clearSharedDictionaryCacheForIsolationKey",
+ base::BindRepeating(&NetInternalsMessageHandler::
+ OnClearSharedDictionaryCacheForIsolationKey,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "getSharedDictionaryUsageInfo",
+ base::BindRepeating(
+ &NetInternalsMessageHandler::OnGetSharedDictionaryUsageInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getSharedDictionaryInfo",
+ base::BindRepeating(
+ &NetInternalsMessageHandler::OnGetSharedDictionaryInfo,
+ base::Unretained(this)));
}
void NetInternalsMessageHandler::OnJavascriptDisallowed() {
@@ -290,6 +332,60 @@ void NetInternalsMessageHandler::OnClearHostResolverCache(
GetNetworkContext()->ClearHostCache(/*filter=*/nullptr, base::NullCallback());
}
+void NetInternalsMessageHandler::OnClearSharedDictionary(
+ const base::Value::List& list) {
+ const std::string* callback_id = list[0].GetIfString();
+ DCHECK(callback_id);
+
+ GetNetworkContext()->ClearSharedDictionaryCache(
+ base::Time::Min(), base::Time::Max(), /*filter=*/nullptr,
+ base::BindOnce(&NetInternalsMessageHandler::OnClearSharedDictionaryDone,
+ weak_factory_.GetWeakPtr(), *callback_id));
+}
+
+void NetInternalsMessageHandler::OnClearSharedDictionaryCacheForIsolationKey(
+ const base::Value::List& list) {
+ const std::string* callback_id = list[0].GetIfString();
+ const std::string* frame_origin = list[1].GetIfString();
+ const std::string* top_frame_site = list[2].GetIfString();
+ DCHECK(callback_id);
+ DCHECK(frame_origin);
+ DCHECK(top_frame_site);
+
+ GetNetworkContext()->ClearSharedDictionaryCacheForIsolationKey(
+ net::SharedDictionaryIsolationKey(
+ url::Origin::Create(GURL(*frame_origin)),
+ net::SchemefulSite(GURL(*top_frame_site))),
+ base::BindOnce(&NetInternalsMessageHandler::
+ OnClearSharedDictionaryForIsolationKeyDone,
+ weak_factory_.GetWeakPtr(), *callback_id));
+}
+
+void NetInternalsMessageHandler::OnGetSharedDictionaryUsageInfo(
+ const base::Value::List& list) {
+ const std::string* callback_id = list[0].GetIfString();
+ DCHECK(callback_id);
+ GetNetworkContext()->GetSharedDictionaryUsageInfo(base::BindOnce(
+ &NetInternalsMessageHandler::OnGetSharedDictionaryUsageInfoDone,
+ weak_factory_.GetWeakPtr(), *callback_id));
+}
+
+void NetInternalsMessageHandler::OnGetSharedDictionaryInfo(
+ const base::Value::List& list) {
+ const std::string* callback_id = list[0].GetIfString();
+ const std::string* frame_origin = list[1].GetIfString();
+ const std::string* top_frame_site = list[2].GetIfString();
+ DCHECK(callback_id);
+ DCHECK(frame_origin);
+ DCHECK(top_frame_site);
+ GetNetworkContext()->GetSharedDictionaryInfo(
+ net::SharedDictionaryIsolationKey(
+ url::Origin::Create(GURL(*frame_origin)),
+ net::SchemefulSite(GURL(*top_frame_site))),
+ base::BindOnce(&NetInternalsMessageHandler::OnGetSharedDictionaryInfoDone,
+ weak_factory_.GetWeakPtr(), *callback_id));
+}
+
void NetInternalsMessageHandler::OnDomainSecurityPolicyDelete(
const base::Value::List& list) {
// |list| should be: [<domain to query>].
@@ -383,6 +479,55 @@ void NetInternalsMessageHandler::OnResolveHostDone(
ResolveJavascriptCallback(base::Value(callback_id), std::move(result));
}
+void NetInternalsMessageHandler::OnGetSharedDictionaryUsageInfoDone(
+ const std::string& callback_id,
+ const std::vector<net::SharedDictionaryUsageInfo>& usage_info) {
+ base::Value::List result_list;
+ for (const auto& usage : usage_info) {
+ base::Value::Dict dict;
+ dict.Set("frame_origin", usage.isolation_key.frame_origin().Serialize());
+ dict.Set("top_frame_site",
+ usage.isolation_key.top_frame_site().Serialize());
+ dict.Set("total_size_bytes",
+ base::Value(base::NumberToString(usage.total_size_bytes)));
+ result_list.Append(std::move(dict));
+ }
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(callback_id), std::move(result_list));
+}
+
+void NetInternalsMessageHandler::OnGetSharedDictionaryInfoDone(
+ const std::string& callback_id,
+ std::vector<network::mojom::SharedDictionaryInfoPtr> dictionaries) {
+ base::Value::List dict_list;
+ for (const auto& item : dictionaries) {
+ base::Value::Dict dict;
+ dict.Set("match", item->match);
+ dict.Set("dictionary_url", item->dictionary_url.spec());
+ dict.Set("response_time", base::TimeFormatHTTP(item->response_time));
+ dict.Set("expiration", base::NumberToString(item->expiration.InSeconds()));
+ dict.Set("last_used_time", base::TimeFormatHTTP(item->last_used_time));
+ dict.Set("size", base::NumberToString(item->size));
+ dict.Set("hash", base::ToLowerASCII(base::HexEncode(
+ item->hash.data, sizeof(item->hash.data))));
+ dict_list.Append(std::move(dict));
+ }
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(callback_id), std::move(dict_list));
+}
+
+void NetInternalsMessageHandler::OnClearSharedDictionaryDone(
+ const std::string& callback_id) {
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(callback_id), true);
+}
+
+void NetInternalsMessageHandler::OnClearSharedDictionaryForIsolationKeyDone(
+ const std::string& callback_id) {
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(callback_id), true);
+}
+
// g_network_context_for_testing is used only for testing.
network::mojom::NetworkContext* g_network_context_for_testing = nullptr;
diff --git a/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
index 2baafbe0685..3bb4a1d08f1 100644
--- a/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/command_line.h"
+#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
@@ -14,9 +15,11 @@
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h"
+#include "base/test/values_test_util.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
@@ -38,6 +41,7 @@
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/base/address_list.h"
+#include "net/base/hash_value.h"
#include "net/base/net_errors.h"
#include "net/base/network_isolation_key.h"
#include "net/dns/mock_host_resolver.h"
@@ -54,6 +58,12 @@ using content::WebUIMessageHandler;
namespace {
+base::Time ToTime(const char* time_string) {
+ base::Time time;
+ CHECK(base::Time::FromString(time_string, &time));
+ return time;
+}
+
// Notifies the NetInternalsTest.Task JS object of the DNS lookup result once
// it's complete. Owns itself.
class DnsLookupClient : public network::mojom::ResolveHostClient {
@@ -99,6 +109,10 @@ class DnsLookupClient : public network::mojom::ResolveHostClient {
};
class NetworkContextForTesting : public network::TestNetworkContext {
+ public:
+ NetworkContextForTesting() = default;
+ ~NetworkContextForTesting() override = default;
+
// This is a mock network context for testing.
// Only "*.com" is registered to this resolver. And especially for
// http2/http3/multihost.com, results include endpoint_results_with_metadata
@@ -169,6 +183,70 @@ class NetworkContextForTesting : public network::TestNetworkContext {
endpoint_results);
}
}
+
+ void ClearSharedDictionaryCache(
+ base::Time start_time,
+ base::Time end_time,
+ network::mojom::ClearDataFilterPtr filter,
+ ClearSharedDictionaryCacheCallback callback) override {
+ // We just cleas all dictionary for testing.
+ dictionaries_.clear();
+ std::move(callback).Run();
+ }
+
+ void ClearSharedDictionaryCacheForIsolationKey(
+ const net::SharedDictionaryIsolationKey& isolation_key,
+ ClearSharedDictionaryCacheForIsolationKeyCallback callback) override {
+ dictionaries_.erase(isolation_key);
+ std::move(callback).Run();
+ }
+
+ void GetSharedDictionaryUsageInfo(
+ GetSharedDictionaryUsageInfoCallback callback) override {
+ std::vector<net::SharedDictionaryUsageInfo> info;
+ for (const auto& it : dictionaries_) {
+ uint64_t total_size_bytes = 0;
+ for (const auto& it2 : it.second) {
+ total_size_bytes += it2->size;
+ }
+ info.emplace_back(net::SharedDictionaryUsageInfo{
+ .isolation_key = it.first, .total_size_bytes = total_size_bytes});
+ }
+ std::move(callback).Run(info);
+ }
+
+ void GetSharedDictionaryInfo(
+ const net::SharedDictionaryIsolationKey& isolation_key,
+ GetSharedDictionaryInfoCallback callback) override {
+ auto it = dictionaries_.find(isolation_key);
+ if (it == dictionaries_.end()) {
+ std::move(callback).Run({});
+ return;
+ }
+ std::vector<network::mojom::SharedDictionaryInfoPtr> dicts;
+ for (const auto& it2 : it->second) {
+ dicts.emplace_back(it2.Clone());
+ }
+ std::move(callback).Run(std::move(dicts));
+ }
+
+ void RegisterTestSharedDictionary(
+ const net::SharedDictionaryIsolationKey& isolation_key,
+ network::mojom::SharedDictionaryInfoPtr dictionary) {
+ auto it = dictionaries_.find(isolation_key);
+ if (it == dictionaries_.end()) {
+ std::vector<network::mojom::SharedDictionaryInfoPtr> dicts;
+ dicts.emplace_back(std::move(dictionary));
+ dictionaries_.insert(std::make_pair(isolation_key, std::move(dicts)));
+ return;
+ }
+ it->second.emplace_back(std::move(dictionary));
+ }
+
+ private:
+ std::map<net::SharedDictionaryIsolationKey,
+ std::vector<network::mojom::SharedDictionaryInfoPtr>>
+ dictionaries_;
};
} // namespace
@@ -209,6 +287,9 @@ class NetInternalsTest::MessageHandler : public content::WebUIMessageHandler {
void SetNetworkContextForTesting(const base::Value::List& list);
void ResetNetworkContextForTesting(const base::Value::List& list);
+ // Register a test shared dictionary for testing.
+ void RgisterTestSharedDictionary(const base::Value::List& list);
+
Browser* browser() { return net_internals_test_->browser(); }
raw_ptr<NetInternalsTest> net_internals_test_;
@@ -245,6 +326,11 @@ void NetInternalsTest::MessageHandler::RegisterMessages() {
base::BindRepeating(
&NetInternalsTest::MessageHandler::ResetNetworkContextForTesting,
weak_factory_.GetWeakPtr()));
+ RegisterMessage(
+ "registerTestSharedDictionary",
+ base::BindRepeating(
+ &NetInternalsTest::MessageHandler::RgisterTestSharedDictionary,
+ weak_factory_.GetWeakPtr()));
}
void NetInternalsTest::MessageHandler::RegisterMessage(
@@ -313,6 +399,25 @@ void NetInternalsTest::MessageHandler::ResetNetworkContextForTesting(
NetInternalsUI::SetNetworkContextForTesting(nullptr);
}
+void NetInternalsTest::MessageHandler::RgisterTestSharedDictionary(
+ const base::Value::List& list) {
+ const std::string* dictionary_json_string = list[0].GetIfString();
+ CHECK(dictionary_json_string);
+ base::Value::Dict dict = base::test::ParseJsonDict(*dictionary_json_string);
+ net::SHA256HashValue hash_value;
+ base::HexStringToSpan(*dict.FindString("hash"), hash_value.data);
+ network_context_for_testing_.RegisterTestSharedDictionary(
+ net::SharedDictionaryIsolationKey(
+ url::Origin::Create(GURL(*dict.FindString("frame_origin"))),
+ net::SchemefulSite(GURL(*dict.FindString("top_frame_site")))),
+ network::mojom::SharedDictionaryInfo::New(
+ *dict.FindString("match"), GURL(*dict.FindString("dictionary_url")),
+ ToTime(dict.FindString("response_time")->c_str()),
+ base::Seconds(*dict.FindInt("expiration")),
+ ToTime(dict.FindString("last_used_time")->c_str()),
+ *dict.FindInt("size"), hash_value));
+}
+
////////////////////////////////////////////////////////////////////////////////
// NetInternalsTest
////////////////////////////////////////////////////////////////////////////////
diff --git a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
index 0a7ce1d9eff..3bc29251485 100644
--- a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
+++ b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
@@ -60,8 +60,6 @@ struct BackgroundImage {
string? repeat_y;
string? position_x;
string? position_y;
- // The CSS styling property set on the background image's scrim.
- string? scrim_display;
// Source of background image.
NtpBackgroundImageSource image_source;
};
@@ -367,4 +365,6 @@ interface Page {
SetModulesFreVisibility(bool visible);
// Sets NTP homepage promo.
SetPromo(Promo? promo);
+ // Shows a toast with information about Chrome Webstore themes.
+ ShowWebstoreToast();
};
diff --git a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index 600ca6c687a..d12c86a246d 100644
--- a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -70,6 +70,7 @@
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/theme_provider.h"
+#include "ui/base/ui_base_features.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
@@ -98,10 +99,9 @@ std::vector<std::string> GetSurveyEligibleModuleIds() {
// custom background images, not just CWS themes.
bool ShouldForceDarkForegroundColorsForLogo(const ThemeService* theme_service) {
const auto* theme_supplier = theme_service->GetThemeSupplier();
- if (!theme_supplier ||
- theme_supplier->get_theme_type() !=
- ui::ColorProviderManager::ThemeInitializerSupplier::ThemeType::
- kExtension) {
+ if (!theme_supplier || theme_supplier->get_theme_type() !=
+ ui::ColorProviderKey::ThemeInitializerSupplier::
+ ThemeType::kExtension) {
return false;
}
static constexpr auto kPrideThemeExtensionIdsDarkForeground =
@@ -131,8 +131,6 @@ new_tab_page::mojom::ThemePtr MakeTheme(
? ntp_custom_background_service->GetCustomBackground()
: absl::nullopt;
theme->background_color = color_provider.GetColor(kColorNewTabPageBackground);
- const bool remove_scrim =
- base::FeatureList::IsEnabled(ntp_features::kNtpRemoveScrim);
const bool theme_has_custom_image =
theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND);
SkColor text_color;
@@ -143,23 +141,28 @@ new_tab_page::mojom::ThemePtr MakeTheme(
most_visited->background_color =
color_provider.GetColor(kColorNewTabPageMostVisitedTileBackground);
} else if (theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
- text_color = color_provider.GetColor(
- remove_scrim ? kColorNewTabPageTextUnthemed : kColorNewTabPageText);
- if (remove_scrim) {
- theme->logo_color =
- color_provider.GetColor(kColorNewTabPageLogoUnthemedLight);
- } else if (theme_provider->GetDisplayProperty(
- ThemeProperties::NTP_LOGO_ALTERNATE) == 1) {
- theme->logo_color = color_provider.GetColor(kColorNewTabPageLogo);
- }
- most_visited->background_color = color_provider.GetColor(
- kColorNewTabPageMostVisitedTileBackgroundUnthemed);
+ text_color = color_provider.GetColor(kColorNewTabPageTextUnthemed);
+ theme->logo_color =
+ color_provider.GetColor(kColorNewTabPageLogoUnthemedLight);
+
+ // TODO(crbug.com/1375760): Post GM3 launch, we can remove the
+ // kColorNewTabPageMostVisitedTileBackgroundUnthemed color and related
+ // logic.
+ most_visited->background_color =
+ features::IsChromeWebuiRefresh2023()
+ ? color_provider.GetColor(kColorNewTabPageMostVisitedTileBackground)
+ : color_provider.GetColor(
+ kColorNewTabPageMostVisitedTileBackgroundUnthemed);
} else {
text_color = color_provider.GetColor(kColorNewTabPageText);
if (theme_provider->GetDisplayProperty(
- ThemeProperties::NTP_LOGO_ALTERNATE) == 1) {
+ ThemeProperties::NTP_LOGO_ALTERNATE) == 1 ||
+ (features::IsChromeWebuiRefresh2023() &&
+ !theme_service->GetIsGrayscale() &&
+ theme_service->GetUserColor().has_value())) {
theme->logo_color = color_provider.GetColor(kColorNewTabPageLogo);
}
+
most_visited->background_color =
color_provider.GetColor(kColorNewTabPageMostVisitedTileBackground);
}
@@ -167,7 +170,6 @@ new_tab_page::mojom::ThemePtr MakeTheme(
most_visited->use_white_tile_icon =
color_utils::IsDark(most_visited->background_color);
most_visited->is_dark = !color_utils::IsDark(text_color);
- most_visited->use_title_pill = false;
theme->text_color = text_color;
theme->is_dark = !color_utils::IsDark(text_color);
theme->theme_realbox_icons =
@@ -184,7 +186,6 @@ new_tab_page::mojom::ThemePtr MakeTheme(
new_tab_page::mojom::NtpBackgroundImageSource::kThirdPartyTheme;
}
theme->is_custom_background = false;
- most_visited->use_title_pill = !remove_scrim;
auto theme_id = theme_service->GetThemeID();
background_image->url = GURL(base::StrCat(
{"chrome-untrusted://theme/IDR_THEME_NTP_BACKGROUND?", theme_id}));
@@ -247,12 +248,6 @@ new_tab_page::mojom::ThemePtr MakeTheme(
background_image = nullptr;
}
- if (remove_scrim && background_image) {
- // If a background image is defined and the scrim removal flag is active
- // disable the scrim.
- background_image->scrim_display = "none";
- }
-
// The special case handling that forces a dark Google logo should only be
// applied when the user does not have a custom background selected and has
// installed a CWS theme with a bundled background image. The first condition
@@ -453,7 +448,12 @@ NewTabPageHandler::NewTabPageHandler(
ntp_custom_background_service_observation_.Observe(
ntp_custom_background_service_.get());
promo_service_observation_.Observe(promo_service_.get());
- OnThemeChanged();
+ if (base::FeatureList::IsEnabled(
+ ntp_features::kNtpBackgroundImageErrorDetection)) {
+ ntp_custom_background_service_->VerifyCustomBackgroundImageURL();
+ } else {
+ OnThemeChanged();
+ }
pref_change_registrar_.Init(profile_->GetPrefs());
pref_change_registrar_.Add(
@@ -465,6 +465,11 @@ NewTabPageHandler::NewTabPageHandler(
base::BindRepeating(&NewTabPageHandler::UpdateDisabledModules,
base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kSeedColorChangeCount,
+ base::BindRepeating(&NewTabPageHandler::MaybeShowWebstoreToast,
+ base::Unretained(this)));
+
if (customize_chrome::IsSidePanelEnabled()) {
auto* customize_chrome_tab_helper =
CustomizeChromeTabHelper::FromWebContents(web_contents_);
@@ -860,9 +865,6 @@ void NewTabPageHandler::IncrementCustomizeChromeButtonOpenCount() {
void NewTabPageHandler::MaybeShowCustomizeChromeFeaturePromo() {
CHECK(profile_);
CHECK(profile_->GetPrefs());
- const auto customize_chrome_button_open_count =
- profile_->GetPrefs()->GetInteger(
- prefs::kNtpCustomizeChromeButtonOpenCount);
// If a sign-in dialog is being currently displayed, the promo should not be
// shown to avoid conflict. The sign-in dialog would be shown as soon as the
@@ -870,9 +872,22 @@ void NewTabPageHandler::MaybeShowCustomizeChromeFeaturePromo() {
bool is_signin_modal_dialog_open =
customize_chrome_feature_promo_helper_->IsSigninModalDialogOpen(
web_contents_.get());
- if (customize_chrome_button_open_count == 0 && !is_signin_modal_dialog_open) {
+ if (is_signin_modal_dialog_open) {
+ return;
+ }
+
+ if (features::IsChromeRefresh2023()) {
customize_chrome_feature_promo_helper_
->MaybeShowCustomizeChromeFeaturePromo(web_contents_.get());
+ } else {
+ const auto customize_chrome_button_open_count =
+ profile_->GetPrefs()->GetInteger(
+ prefs::kNtpCustomizeChromeButtonOpenCount);
+
+ if (customize_chrome_button_open_count == 0) {
+ customize_chrome_feature_promo_helper_
+ ->MaybeShowCustomizeChromeFeaturePromo(web_contents_.get());
+ }
}
}
@@ -1355,3 +1370,9 @@ void NewTabPageHandler::NotifyCustomizeChromeSidePanelVisibilityChanged(
bool is_open) {
page_->SetCustomizeChromeSidePanelVisibility(is_open);
}
+
+void NewTabPageHandler::MaybeShowWebstoreToast() {
+ if (profile_->GetPrefs()->GetInteger(prefs::kSeedColorChangeCount) <= 3) {
+ page_->ShowWebstoreToast();
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
index 182721a33f3..4aee1e514fd 100644
--- a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
+++ b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
@@ -196,6 +196,7 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler,
bool IsCustomLinksEnabled() const;
bool IsShortcutsVisible() const;
void NotifyCustomizeChromeSidePanelVisibilityChanged(bool is_open);
+ void MaybeShowWebstoreToast();
ChooseLocalCustomBackgroundCallback choose_local_custom_background_callback_;
raw_ptr<NtpBackgroundService> ntp_background_service_;
diff --git a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
index 73fdffa9b96..f580672da86 100644
--- a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
@@ -60,6 +60,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/theme_provider.h"
+#include "ui/base/ui_base_features.h"
#include "ui/color/color_mixer.h"
#include "ui/color/color_provider_source.h"
#include "ui/color/color_recipe.h"
@@ -94,6 +95,7 @@ class MockPage : public new_tab_page::mojom::Page {
MOCK_METHOD(void, SetModulesFreVisibility, (bool));
MOCK_METHOD(void, SetCustomizeChromeSidePanelVisibility, (bool));
MOCK_METHOD(void, SetPromo, (new_tab_page::mojom::PromoPtr));
+ MOCK_METHOD(void, ShowWebstoreToast, ());
mojo::Receiver<new_tab_page::mojom::Page> receiver_{this};
};
@@ -120,8 +122,8 @@ class MockColorProviderSource : public ui::ColorProviderSource {
}
protected:
- ui::ColorProviderManager::Key GetColorProviderKey() const override {
- return ui::ColorProviderManager::Key();
+ ui::ColorProviderKey GetColorProviderKey() const override {
+ return ui::ColorProviderKey();
}
private:
@@ -145,6 +147,7 @@ class MockNtpCustomBackgroundService : public NtpCustomBackgroundService {
explicit MockNtpCustomBackgroundService(Profile* profile)
: NtpCustomBackgroundService(profile) {}
MOCK_METHOD(void, RefreshBackgroundIfNeeded, ());
+ MOCK_METHOD(void, VerifyCustomBackgroundImageURL, ());
MOCK_METHOD(absl::optional<CustomBackground>, GetCustomBackground, ());
MOCK_METHOD(void, AddObserver, (NtpCustomBackgroundServiceObserver*));
};
@@ -276,9 +279,17 @@ class NewTabPageHandlerTest : public testing::Test {
EXPECT_CALL(*mock_promo_service_, AddObserver)
.Times(1)
.WillOnce(testing::SaveArg<0>(&promo_service_observer_));
- EXPECT_CALL(mock_page_, SetTheme).Times(1);
- EXPECT_CALL(mock_ntp_custom_background_service_, RefreshBackgroundIfNeeded)
- .Times(1);
+ if (!base::FeatureList::IsEnabled(
+ ntp_features::kNtpBackgroundImageErrorDetection)) {
+ EXPECT_CALL(mock_page_, SetTheme).Times(1);
+ EXPECT_CALL(mock_ntp_custom_background_service_,
+ RefreshBackgroundIfNeeded)
+ .Times(1);
+ } else {
+ EXPECT_CALL(mock_ntp_custom_background_service_,
+ VerifyCustomBackgroundImageURL)
+ .Times(1);
+ }
webui::SetThemeProviderForTesting(&mock_theme_provider_);
web_contents_->SetColorProviderSource(&mock_color_provider_source_);
const std::vector<std::pair<const std::string, int>> module_id_names = {
@@ -376,24 +387,26 @@ class NewTabPageHandlerThemeTest
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
- if (RemoveScrim()) {
- enabled_features.push_back(ntp_features::kNtpRemoveScrim);
+ if (CustomizeChromeSidePanel()) {
+ enabled_features.push_back(features::kCustomizeChromeSidePanel);
} else {
- disabled_features.push_back(ntp_features::kNtpRemoveScrim);
+ disabled_features.push_back(features::kCustomizeChromeSidePanel);
}
- if (CustomizeChromeSidePanel()) {
- enabled_features.push_back(ntp_features::kCustomizeChromeSidePanel);
+ if (BackgroundImageErrorDetection()) {
+ enabled_features.push_back(
+ ntp_features::kNtpBackgroundImageErrorDetection);
} else {
- disabled_features.push_back(ntp_features::kCustomizeChromeSidePanel);
+ disabled_features.push_back(
+ ntp_features::kNtpBackgroundImageErrorDetection);
}
feature_list_.InitWithFeatures(std::move(enabled_features),
std::move(disabled_features));
}
- bool RemoveScrim() const { return std::get<0>(GetParam()); }
- bool CustomizeChromeSidePanel() const { return std::get<1>(GetParam()); }
+ bool CustomizeChromeSidePanel() const { return std::get<0>(GetParam()); }
+ bool BackgroundImageErrorDetection() const { return std::get<1>(GetParam()); }
private:
base::test::ScopedFeatureList feature_list_;
@@ -465,28 +478,14 @@ TEST_P(NewTabPageHandlerThemeTest, SetTheme) {
EXPECT_EQ("no-repeat", theme->background_image->repeat_y);
EXPECT_EQ("center", theme->background_image->position_x);
EXPECT_EQ("top", theme->background_image->position_y);
- if (!RemoveScrim()) {
- EXPECT_EQ(SkColorSetRGB(0, 0, 2), theme->text_color);
- EXPECT_EQ(SkColorSetRGB(0, 0, 4), theme->logo_color);
- EXPECT_EQ(RemoveScrim(),
- theme->background_image->scrim_display.has_value());
- } else {
- EXPECT_EQ(SkColorSetRGB(0, 0, 3), theme->text_color);
- EXPECT_EQ(SkColorSetRGB(0, 0, 5), theme->logo_color);
- EXPECT_TRUE(theme->background_image->scrim_display.has_value());
- EXPECT_EQ("none", theme->background_image->scrim_display.value());
- }
+ EXPECT_EQ(SkColorSetRGB(0, 0, 3), theme->text_color);
+ EXPECT_EQ(SkColorSetRGB(0, 0, 5), theme->logo_color);
EXPECT_FALSE(theme->background_image_attribution_1.has_value());
EXPECT_FALSE(theme->background_image_attribution_2.has_value());
EXPECT_FALSE(theme->background_image_attribution_url.has_value());
EXPECT_FALSE(theme->background_image_collection_id.has_value());
ASSERT_TRUE(theme->most_visited);
EXPECT_EQ(SkColorSetRGB(0, 0, 8), theme->most_visited->background_color);
- if (RemoveScrim()) {
- EXPECT_FALSE(theme->most_visited->use_title_pill);
- } else {
- EXPECT_TRUE(theme->most_visited->use_title_pill);
- }
EXPECT_TRUE(theme->most_visited->use_white_tile_icon);
EXPECT_EQ(false, theme->most_visited->is_dark);
}
@@ -527,6 +526,7 @@ TEST_P(NewTabPageHandlerThemeTest, SetCustomBackground) {
mock_page_.FlushForTesting();
ASSERT_TRUE(theme);
+ EXPECT_EQ(SkColorSetRGB(0, 0, 4), theme->most_visited->background_color);
if (CustomizeChromeSidePanel()) {
EXPECT_FALSE(theme->is_custom_background);
EXPECT_FALSE(theme->background_image_attribution_1.has_value());
@@ -550,14 +550,6 @@ TEST_P(NewTabPageHandlerThemeTest, SetCustomBackground) {
kFirstPartyThemeWithoutDailyRefresh,
theme->background_image->image_source);
}
- if (RemoveScrim()) {
- EXPECT_TRUE(theme->background_image->scrim_display.has_value());
- EXPECT_EQ("none", theme->background_image->scrim_display.value());
- } else {
- EXPECT_FALSE(theme->background_image->scrim_display.has_value());
- }
-
- EXPECT_EQ(SkColorSetRGB(0, 0, 4), theme->most_visited->background_color);
}
TEST_P(NewTabPageHandlerThemeTest, SetDailyRefresh) {
@@ -1181,3 +1173,17 @@ TEST_F(NewTabPageHandlerTest,
mock_page_.FlushForTesting();
}
+
+TEST_F(NewTabPageHandlerTest, ShowWebstoreToast) {
+ profile_->GetPrefs()->SetInteger(prefs::kSeedColorChangeCount, 1);
+
+ EXPECT_CALL(mock_page_, ShowWebstoreToast).Times(1);
+ mock_page_.FlushForTesting();
+}
+
+TEST_F(NewTabPageHandlerTest, DoNotShowWebstoreToastOnCountExceeded) {
+ profile_->GetPrefs()->SetInteger(prefs::kSeedColorChangeCount, 4);
+
+ EXPECT_CALL(mock_page_, ShowWebstoreToast).Times(0);
+ mock_page_.FlushForTesting();
+}
diff --git a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index b740d1c4e9b..ff24f9dde69 100644
--- a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -16,6 +16,7 @@
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "base/values.h"
+#include "chrome/browser/browser_features.h"
#include "chrome/browser/buildflags.h"
#include "chrome/browser/cart/cart_handler.h"
#include "chrome/browser/image_service/image_service_factory.h"
@@ -27,6 +28,7 @@
#include "chrome/browser/new_tab_page/modules/new_tab_page_modules.h"
#include "chrome/browser/new_tab_page/modules/photos/photos_handler.h"
#include "chrome/browser/new_tab_page/modules/recipes/recipes_handler.h"
+#include "chrome/browser/new_tab_page/modules/v2/history_clusters/history_clusters_page_handler_v2.h"
#include "chrome/browser/new_tab_page/new_tab_page_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/background/ntp_custom_background_service_factory.h"
@@ -198,6 +200,13 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
ntp_features::kNtpHandleMostVisitedNavigationExplicitly));
source->AddBoolean(
+ "prerenderEnabled",
+ base::FeatureList::IsEnabled(features::kNewTabPageTriggerForPrerender2));
+ source->AddInteger(
+ "prerenderStartTimeThreshold",
+ features::kNewTabPagePrerenderStartDelayOnMouseHoverByMiliSeconds.Get());
+
+ source->AddBoolean(
"oneGoogleBarEnabled",
base::FeatureList::IsEnabled(ntp_features::kNtpOneGoogleBar));
source->AddBoolean("shortcutsEnabled",
@@ -227,16 +236,28 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
ntp_features::kNtpModulesLoad));
source->AddInteger("modulesLoadTimeout",
ntp_features::GetModulesLoadTimeout().InMilliseconds());
+ source->AddInteger("modulesMaxColumnCount",
+ ntp_features::GetModulesMaxColumnCount());
+ source->AddInteger(
+ "multipleLoadedModulesMaxModuleInstanceCount",
+ ntp_features::GetMultipleLoadedModulesMaxModuleInstanceCount());
source->AddBoolean("mostVisitedReflowOnOverflowEnabled",
base::FeatureList::IsEnabled(
ntp_features::kNtpMostVisitedReflowOnOverflow));
source->AddBoolean(
"historyClustersModuleEnabled",
base::FeatureList::IsEnabled(ntp_features::kNtpHistoryClustersModule));
+ source->AddBoolean(
+ "historyClustersSuggestionChipHeaderEnabled",
+ base::FeatureList::IsEnabled(
+ ntp_features::kNtpHistoryClustersModuleSuggestionChipHeader));
source->AddBoolean("historyClustersModuleLoadEnabled",
base::FeatureList::IsEnabled(
ntp_features::kNtpHistoryClustersModuleLoad) &&
HasCredentials(profile));
+ source->AddBoolean("historyClustersImagesEnabled",
+ !base::FeatureList::IsEnabled(
+ ntp_features::kNtpHistoryClustersModuleTextOnly));
static constexpr webui::LocalizedString kStrings[] = {
{"doneButton", IDS_DONE},
@@ -256,7 +277,7 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
{"linkEditedMsg", IDS_NTP_CONFIRM_MSG_SHORTCUT_EDITED},
{"linkRemove", IDS_NTP_CUSTOM_LINKS_REMOVE},
{"linkRemovedMsg", IDS_NTP_CONFIRM_MSG_SHORTCUT_REMOVED},
- {"moreActions", IDS_SETTINGS_MORE_ACTIONS},
+ {"shortcutMoreActions", IDS_NTP_CUSTOM_LINKS_MORE_ACTIONS},
{"nameField", IDS_NTP_CUSTOM_LINKS_NAME},
{"restoreDefaultLinks", IDS_NTP_CONFIRM_MSG_RESTORE_DEFAULTS},
{"restoreThumbnailsShort", IDS_NEW_TAB_RESTORE_THUMBNAILS_SHORT_LINK},
@@ -268,6 +289,7 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
{"backgroundsMenuItem", IDS_NTP_CUSTOMIZE_MENU_BACKGROUND_LABEL},
{"cancelButton", IDS_CANCEL},
{"colorPickerLabel", IDS_NTP_CUSTOMIZE_COLOR_PICKER_LABEL},
+ {"hueSliderTitle", IDS_NTP_CUSTOMIZE_COLOR_HUE_SLIDER_TITLE},
{"customBackgroundDisabled",
IDS_NTP_CUSTOMIZE_MENU_BACKGROUND_DISABLED_LABEL},
{"customizeButton", IDS_NTP_CUSTOMIZE_BUTTON_LABEL},
@@ -367,6 +389,7 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
{"moduleInfoButtonTitle", IDS_NTP_MODULES_INFO_BUTTON_TITLE},
{"modulesDismissButtonText", IDS_NTP_MODULES_DISMISS_BUTTON_TEXT},
{"modulesDisableButtonText", IDS_NTP_MODULES_DISABLE_BUTTON_TEXT},
+ {"modulesDisableButtonTextV2", IDS_NTP_MODULES_DISABLE_BUTTON_TEXT_V2},
{"modulesCustomizeButtonText", IDS_NTP_MODULES_CUSTOMIZE_BUTTON_TEXT},
{"modulesRecipeInfo", IDS_NTP_MODULES_RECIPE_INFO},
{"modulesRecipeExtendedInfo", IDS_NTP_MODULES_RECIPE_EXTENDED_INFO},
@@ -387,25 +410,18 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
{"modulesCartLower", IDS_NTP_MODULES_CART_LOWER},
{"modulesCartLowerThese", IDS_NTP_MODULES_CART_LOWER_THESE},
{"modulesCartLowerYour", IDS_NTP_MODULES_CART_LOWER_YOUR},
+ {"modulesDisableToastMessage",
+ IDS_NTP_MODULES_HISTORY_CLUSTERS_DISABLE_TOAST_MESSAGE},
{"modulesDriveSentence", IDS_NTP_MODULES_DRIVE_SENTENCE},
+ {"modulesDriveSentenceV2", IDS_NTP_MODULES_DRIVE_SENTENCE_V2},
{"modulesDriveSentence2", IDS_NTP_MODULES_DRIVE_SENTENCE2},
{"modulesDriveFilesSentence", IDS_NTP_MODULES_DRIVE_FILES_SENTENCE},
{"modulesDriveFilesLower", IDS_NTP_MODULES_DRIVE_FILES_LOWER},
{"modulesDummyLower", IDS_NTP_MODULES_DUMMY_LOWER},
{"modulesDriveTitle", IDS_NTP_MODULES_DRIVE_TITLE},
+ {"modulesDriveTitleV2", IDS_NTP_MODULES_DRIVE_TITLE_V2},
{"modulesDriveInfo", IDS_NTP_MODULES_DRIVE_INFO},
{"modulesDummyTitle", IDS_NTP_MODULES_DUMMY_TITLE},
- {"modulesDummy2Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy3Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy4Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy5Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy6Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy7Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy8Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy9Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy10Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy11Title", IDS_NTP_MODULES_DUMMY2_TITLE},
- {"modulesDummy12Title", IDS_NTP_MODULES_DUMMY2_TITLE},
{"modulesFeedTitle", IDS_NTP_MODULES_FEED_TITLE},
{"modulesKaleidoscopeTitle", IDS_NTP_MODULES_KALEIDOSCOPE_TITLE},
{"modulesPhotosInfo", IDS_NTP_MODULES_PHOTOS_INFO},
@@ -483,14 +499,19 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
IDS_NTP_MODULES_FIRST_RUN_EXPERIENCE_OPT_OUT},
{"modulesFirstRunExperienceOptOutToast",
IDS_NTP_MODULES_FIRST_RUN_EXPERIENCE_OPT_OUT_TOAST},
- {"modulesJourneysResumeJourney", IDS_NTP_MODULES_RESUME_YOUR_JOURNEY},
{"modulesJourneysShowAll", IDS_NTP_MODULES_SHOW_ALL},
{"modulesJourneysInfo", IDS_NTP_MODULES_HISTORY_CLUSTERS_INFO},
- {"disableQuestsModuleToastName",
- IDS_NTP_MODULES_HISTORY_CLUSTERS_SENTENCE2},
- {"disableQuestsModuleToastMessage",
- IDS_NTP_MODULES_DISABLE_TOAST_MESSAGE},
+ {"modulesHistoryWithDiscountInfo",
+ IDS_NTP_MODULES_HISTORY_CLUSTERS_WITH_DISCOUNT_INFO},
+ {"modulesThisTypeOfCardText",
+ IDS_NTP_MODULES_HISTORY_CLUSTERS_DISABLE_TOAST_NAME},
{"modulesJourneyDisable", IDS_NTP_MODULES_HISTORY_CLUSTERS_DISABLE_TEXT},
+ {"modulesJourneysDismissButton",
+ IDS_NTP_MODULES_HISTORY_CLUSTERS_DISMISS_BUTTON},
+ {"modulesJourneysDoneButton",
+ IDS_NTP_MODULES_HISTORY_CLUSTERS_DONE_BUTTON},
+ {"modulesJourneysShowAllButton",
+ IDS_NTP_MODULES_HISTORY_CLUSTERS_SHOW_ALL_BUTTON},
{"modulesJourneysShowAllAcc", IDS_ACCNAME_SHOW_ALL},
{"modulesJourneysSearchSuggAcc", IDS_ACCNAME_SEARCH_SUGG},
{"modulesJourneysBookmarked",
@@ -504,9 +525,16 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
IDS_NTP_MODULES_QUEST_CART_TILE_LABEL_SINGULAR},
{"modulesJourneysCartTileLabelDefault",
IDS_NTP_MODULES_QUEST_CART_TILE_LABEL_DEFAULT},
+ {"modulesJourneysResumeJourney",
+ IDS_NTP_MODULES_HISTORY_CLUSTERS_RESUME_BROWSING},
+ {"modulesMoreActions", IDS_NTP_MODULES_MORE_ACTIONS},
// Middle slot promo.
{"undoDismissPromoButtonToast", IDS_NTP_UNDO_DISMISS_PROMO_BUTTON_TOAST},
+
+ // Webstore toast.
+ {"webstoreThemesToastMessage", IDS_NTP_WEBSTORE_TOAST_MESSAGE},
+ {"webstoreThemesToastButtonText", IDS_NTP_WEBSTORE_TOAST_BUTTON_TEXT},
};
source->AddLocalizedStrings(kStrings);
@@ -514,18 +542,9 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
ntp_features::kNtpWideModules));
if (base::FeatureList::IsEnabled(history_clusters::kRenameJourneys)) {
- source->AddLocalizedString(
- "modulesJourneysResumeJourney",
- IDS_NTP_MODULES_HISTORY_CLUSTERS_RESUME_BROWSING);
source->AddLocalizedString("modulesJourneysInfo",
IDS_NTP_MODULES_HISTORY_CLUSTERS_INFO2);
source->AddLocalizedString(
- "disableQuestsModuleToastName",
- IDS_NTP_MODULES_HISTORY_CLUSTERS_DISABLE_TOAST_NAME);
- source->AddLocalizedString(
- "disableQuestsModuleToastMessage",
- IDS_NTP_MODULES_HISTORY_CLUSTERS_DISABLE_TOAST_MESSAGE);
- source->AddLocalizedString(
"modulesJourneyDisable",
IDS_NTP_MODULES_HISTORY_CLUSTERS_DISABLE_DROPDOWN_TEXT);
}
@@ -578,9 +597,6 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
"moduleRecipeExtendedExperimentEnabled",
!splitExperimentGroup.empty() && (splitExperimentGroup[0] == "historical" || splitExperimentGroup[0] == "mix"));
- source->AddBoolean("removeScrim", base::FeatureList::IsEnabled(
- ntp_features::kNtpRemoveScrim));
-
source->AddBoolean("modulesChromeCartInHistoryClustersModuleEnabled",
base::FeatureList::IsEnabled(
ntp_features::kNtpChromeCartInHistoryClusterModule));
@@ -591,6 +607,12 @@ content::WebUIDataSource* CreateAndAddNewTabPageUiHtmlSource(Profile* profile) {
base::FeatureList::IsEnabled(
ntp_features::kNtpChromeCartInHistoryClusterModule));
+ source->AddBoolean("historyClustersModuleDiscountsEnabled",
+ base::FeatureList::IsEnabled(
+ ntp_features::kNtpHistoryClustersModuleDiscounts));
+
+ webui::SetupChromeRefresh2023(source);
+
RealboxHandler::SetupWebUIDataSource(source, profile);
webui::SetupWebUIDataSource(
@@ -759,7 +781,7 @@ void NewTabPageUI::BindInterface(
mojo::PendingReceiver<omnibox::mojom::PageHandler> pending_page_handler) {
realbox_handler_ = std::make_unique<RealboxHandler>(
std::move(pending_page_handler), profile_, web_contents(),
- &metrics_reporter_, /*is_omnibox_popup_handler=*/false);
+ &metrics_reporter_, /*omnibox_controller=*/nullptr);
}
void NewTabPageUI::BindInterface(
@@ -840,6 +862,13 @@ void NewTabPageUI::BindInterface(
}
void NewTabPageUI::BindInterface(
+ mojo::PendingReceiver<ntp::history_clusters_v2::mojom::PageHandler>
+ pending_page_handler) {
+ history_clusters_handler_v2_ = std::make_unique<HistoryClustersPageHandlerV2>(
+ std::move(pending_page_handler), web_contents());
+}
+
+void NewTabPageUI::BindInterface(
mojo::PendingReceiver<page_image_service::mojom::PageImageServiceHandler>
pending_page_handler) {
base::WeakPtr<page_image_service::ImageService> image_service_weak;
diff --git a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
index 4f7dcbd7241..430d90f090b 100644
--- a/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
+++ b/chromium/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -17,6 +17,7 @@
#include "chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom.h"
#include "chrome/browser/new_tab_page/modules/photos/photos.mojom.h"
#include "chrome/browser/new_tab_page/modules/recipes/recipes.mojom.h"
+#include "chrome/browser/new_tab_page/modules/v2/history_clusters/history_clusters_v2.mojom.h"
#include "components/user_education/webui/help_bubble_handler.h"
#include "ui/webui/resources/cr_components/help_bubble/help_bubble.mojom.h"
#include "ui/webui/resources/js/browser_command/browser_command.mojom.h"
@@ -84,6 +85,7 @@ namespace ntp {
class FeedHandler;
}
class HistoryClustersPageHandler;
+class HistoryClustersPageHandlerV2;
class HelpBubbleHandler;
class NewTabPageUI
: public ui::MojoWebUIController,
@@ -192,6 +194,13 @@ class NewTabPageUI
mojo::PendingReceiver<ntp::history_clusters::mojom::PageHandler>
pending_page_handler);
+ // Instantiates the implementor of the
+ // ntp::history_clusters_v2::mojom::PageHandler mojo interface passing to it
+ // the pending receiver that will be internally bound.
+ void BindInterface(
+ mojo::PendingReceiver<ntp::history_clusters_v2::mojom::PageHandler>
+ pending_page_handler);
+
void BindInterface(
mojo::PendingReceiver<page_image_service::mojom::PageImageServiceHandler>
pending_page_handler);
@@ -277,6 +286,7 @@ class NewTabPageUI
#endif
std::unique_ptr<CartHandler> cart_handler_;
std::unique_ptr<HistoryClustersPageHandler> history_clusters_handler_;
+ std::unique_ptr<HistoryClustersPageHandlerV2> history_clusters_handler_v2_;
std::unique_ptr<page_image_service::ImageServiceHandler>
image_service_handler_;
raw_ptr<Profile> profile_;
diff --git a/chromium/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc b/chromium/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
index 00db93d6cc9..3d804255fae 100644
--- a/chromium/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
+++ b/chromium/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
@@ -173,8 +173,7 @@ void UntrustedSource::StartDataRequest(
params.count("repeatX") == 1 ? params["repeatX"] : "no-repeat",
params.count("repeatY") == 1 ? params["repeatY"] : "no-repeat",
params.count("positionX") == 1 ? params["positionX"] : "center",
- params.count("positionY") == 1 ? params["positionY"] : "center",
- params.count("scrimDisplay") == 1 ? params["scrimDisplay"] : "inherit",
+ params.count("positionY") == 1 ? params["positionY"] : "center", "none",
std::move(callback));
return;
}
@@ -256,11 +255,6 @@ void UntrustedSource::OnOneGoogleBarDataUpdated() {
replacements["endOfBodyHtml"] = data->end_of_body_html;
replacements["endOfBodyScript"] = data->end_of_body_script;
- replacements["ogbUnprotectedTextSelector"] =
- ntp_features::kNtpOgbUnprotectedTextSelectorParam.Get();
- replacements["ogbButtonSelector"] =
- ntp_features::kNtpOgbButtonSelectorParam.Get();
-
html = FormatTemplate(IDR_NEW_TAB_PAGE_UNTRUSTED_ONE_GOOGLE_BAR_HTML,
replacements);
}
diff --git a/chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc b/chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc
index 321b54433f5..82438ac55cd 100644
--- a/chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc
+++ b/chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc
@@ -62,7 +62,6 @@ void NewTabPageThirdPartyHandler::NotifyAboutTheme() {
color_provider.GetColor(kColorNewTabPageMostVisitedTileBackground);
most_visited->use_white_tile_icon =
color_utils::IsDark(most_visited->background_color);
- most_visited->use_title_pill = false;
theme->text_color = color_provider.GetColor(kColorNewTabPageText);
most_visited->is_dark = !color_utils::IsDark(theme->text_color);
theme->color_background = color_utils::SkColorToRgbaString(GetThemeColor(
@@ -75,8 +74,6 @@ void NewTabPageThirdPartyHandler::NotifyAboutTheme() {
theme->has_custom_background =
theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND);
theme->id = profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
- most_visited->use_title_pill =
- !base::FeatureList::IsEnabled(ntp_features::kNtpRemoveScrim);
}
theme->most_visited = std::move(most_visited);
page_->SetTheme(std::move(theme));
diff --git a/chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc b/chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc
index e8e795bc815..08af8214df4 100644
--- a/chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc
+++ b/chromium/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <utility>
+#include "chrome/browser/browser_features.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/themes/theme_service.h"
@@ -43,7 +44,6 @@ using content::BrowserContext;
using content::WebContents;
namespace {
-
void CreateAndAddNewTabPageThirdPartyUiHtmlSource(Profile* profile,
WebContents* web_contents) {
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
@@ -102,6 +102,13 @@ void CreateAndAddNewTabPageThirdPartyUiHtmlSource(Profile* profile,
base::FeatureList::IsEnabled(
ntp_features::kNtpHandleMostVisitedNavigationExplicitly));
+ source->AddBoolean(
+ "prerenderEnabled",
+ base::FeatureList::IsEnabled(features::kNewTabPageTriggerForPrerender2));
+ source->AddInteger(
+ "prerenderStartTimeThreshold",
+ features::kNewTabPagePrerenderStartDelayOnMouseHoverByMiliSeconds.Get());
+
// Needed by <cr-most-visited> but not used in
// chrome://new-tab-page-third-party/.
source->AddString("addLinkTitle", "");
@@ -113,7 +120,7 @@ void CreateAndAddNewTabPageThirdPartyUiHtmlSource(Profile* profile,
source->AddString("linkCantEdit", "");
source->AddString("linkDone", "");
source->AddString("linkEditedMsg", "");
- source->AddString("moreActions", "");
+ source->AddString("shortcutMoreActions", "");
source->AddString("nameField", "");
source->AddString("restoreDefaultLinks", "");
source->AddString("shortcutAlreadyExists", "");
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_icon_webui_handler.cc b/chromium/chrome/browser/ui/webui/ntp/app_icon_webui_handler.cc
index 41faf72413e..b18f6814231 100644
--- a/chromium/chrome/browser/ui/webui/ntp/app_icon_webui_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ntp/app_icon_webui_handler.cc
@@ -14,15 +14,13 @@
#include "chrome/common/url_constants.h"
#include "extensions/browser/extension_registry.h"
#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/color_analysis.h"
namespace {
-base::Value GetDominantColorCssString(
- scoped_refptr<base::RefCountedMemory> png) {
+base::Value GetDominantColorCssString(const SkBitmap& bitmap) {
color_utils::GridSampler sampler;
- SkColor color = color_utils::CalculateKMeanColorOfPNG(png);
+ SkColor color = color_utils::CalculateKMeanColorOfBitmap(bitmap);
return base::Value(base::StringPrintf("rgb(%d, %d, %d)", SkColorGetR(color),
SkColorGetG(color),
SkColorGetB(color)));
@@ -34,7 +32,7 @@ AppIconWebUIHandler::AppIconWebUIHandler() {
extension_icon_manager_.set_observer(this);
}
-AppIconWebUIHandler::~AppIconWebUIHandler() {}
+AppIconWebUIHandler::~AppIconWebUIHandler() = default;
void AppIconWebUIHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
@@ -59,13 +57,7 @@ void AppIconWebUIHandler::HandleGetAppIconDominantColor(
void AppIconWebUIHandler::OnImageLoaded(const std::string& extension_id) {
gfx::Image icon = extension_icon_manager_.GetIcon(extension_id);
- // TODO(estade): would be nice to avoid a round trip through png encoding.
- std::vector<unsigned char> bits;
- if (!gfx::PNGCodec::EncodeBGRASkBitmap(*icon.ToSkBitmap(), true, &bits))
- return;
- scoped_refptr<base::RefCountedStaticMemory> bits_mem(
- new base::RefCountedStaticMemory(&bits.front(), bits.size()));
- base::Value color_value = GetDominantColorCssString(bits_mem);
+ base::Value color_value = GetDominantColorCssString(*icon.ToSkBitmap());
base::Value id(extension_id);
web_ui()->CallJavascriptFunctionUnsafe("ntp.setFaviconDominantColor", id,
color_value);
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index 82041022924..60f4f4217aa 100644
--- a/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -47,8 +47,6 @@
#include "chrome/browser/ui/extensions/extension_enable_flow.h"
#include "chrome/browser/ui/tab_dialogs.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/web_applications/web_app_dialog_manager.h"
-#include "chrome/browser/ui/web_applications/web_app_ui_manager_impl.h"
#include "chrome/browser/ui/webui/extensions/extension_basic_info.h"
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
@@ -65,6 +63,7 @@
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
+#include "chrome/browser/web_applications/web_app_ui_manager.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_features.h"
@@ -1011,7 +1010,7 @@ void AppLauncherHandler::HandleUninstallApp(const base::Value::List& args) {
weak_ptr_factory_.GetWeakPtr());
base::AutoReset<bool> auto_reset(&ignore_changes_, true);
- web_app_provider_->install_finalizer().UninstallWebApp(
+ web_app_provider_->scheduler().UninstallWebApp(
extension_id_prompting_, webapps::WebappUninstallSource::kAppsPage,
std::move(uninstall_success_callback));
} else {
@@ -1026,12 +1025,9 @@ void AppLauncherHandler::HandleUninstallApp(const base::Value::List& args) {
Browser* browser =
chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
- web_app::WebAppUiManagerImpl::Get(web_app_provider_)
- ->dialog_manager()
- .UninstallWebApp(extension_id_prompting_,
- webapps::WebappUninstallSource::kAppsPage,
- browser->window(),
- std::move(uninstall_success_callback));
+ web_app_provider_->ui_manager().PresentUserUninstallDialog(
+ extension_id_prompting_, webapps::WebappUninstallSource::kAppsPage,
+ browser->window(), std::move(uninstall_success_callback));
}
return;
}
@@ -1272,7 +1268,7 @@ void AppLauncherHandler::HandleLaunchDeprecatedAppDialog(
void AppLauncherHandler::OnFaviconForAppInstallFromLink(
std::unique_ptr<AppInstallInfo> install_info,
const favicon_base::FaviconImageResult& image_result) {
- auto web_app = std::make_unique<WebAppInstallInfo>();
+ auto web_app = std::make_unique<web_app::WebAppInstallInfo>();
web_app->title = install_info->title;
web_app->start_url = install_info->app_url;
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.h b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.h
index 237d277667d..f37f6fed61b 100644
--- a/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.h
+++ b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.h
@@ -32,6 +32,7 @@
#include "content/public/browser/web_ui_message_handler.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/extension.h"
+#include "extensions/common/extension_id.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
class ExtensionEnableFlow;
@@ -276,7 +277,7 @@ class AppLauncherHandler
// The id of the extension we are prompting the user about (either enable or
// uninstall).
- std::string extension_id_prompting_;
+ extensions::ExtensionId extension_id_prompting_;
// When true, we ignore changes to the underlying data rather than immediately
// refreshing. This is useful when making many batch updates to avoid flicker.
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc
index 741e32b3727..02859beea49 100644
--- a/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc
@@ -70,8 +70,8 @@ class TestAppLauncherHandler : public AppLauncherHandler {
}
};
-std::unique_ptr<WebAppInstallInfo> BuildWebAppInfo() {
- auto app_info = std::make_unique<WebAppInstallInfo>();
+std::unique_ptr<web_app::WebAppInstallInfo> BuildWebAppInfo() {
+ auto app_info = std::make_unique<web_app::WebAppInstallInfo>();
app_info->start_url = GURL(kTestAppUrl);
app_info->scope = GURL(kTestAppUrl);
app_info->title = kTestAppTitle;
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.cc b/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.cc
index 7ba759a9476..98756fec176 100644
--- a/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.cc
+++ b/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.cc
@@ -34,7 +34,8 @@ AppResourceCacheFactory::AppResourceCacheFactory()
AppResourceCacheFactory::~AppResourceCacheFactory() = default;
-KeyedService* AppResourceCacheFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+AppResourceCacheFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const {
- return new NTPResourceCache(static_cast<Profile*>(profile));
+ return std::make_unique<NTPResourceCache>(static_cast<Profile*>(profile));
}
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.h b/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.h
index e088829d5de..ae4bb1a41a3 100644
--- a/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.h
+++ b/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.h
@@ -27,7 +27,7 @@ class AppResourceCacheFactory : public ProfileKeyedServiceFactory {
~AppResourceCacheFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/ui/webui/ntp/new_tab_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/ntp/new_tab_ui_browsertest.cc
index c2b26ddd11b..558d32f5b4e 100644
--- a/chromium/chrome/browser/ui/webui/ntp/new_tab_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/ntp/new_tab_ui_browsertest.cc
@@ -34,8 +34,10 @@ bool HandleMessage(int severity,
int line,
size_t message_start,
const std::string& str) {
- if (severity == logging::LOG_ERROR && file && file == std::string("CONSOLE"))
+ if (severity == logging::LOGGING_ERROR && file &&
+ file == std::string("CONSOLE")) {
had_console_errors = true;
+ }
return false;
}
diff --git a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.cc b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.cc
index d00d56cab8b..ed4ce7015da 100644
--- a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.cc
+++ b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.cc
@@ -36,7 +36,8 @@ NTPResourceCacheFactory::NTPResourceCacheFactory()
NTPResourceCacheFactory::~NTPResourceCacheFactory() = default;
-KeyedService* NTPResourceCacheFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+NTPResourceCacheFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const {
- return new NTPResourceCache(static_cast<Profile*>(profile));
+ return std::make_unique<NTPResourceCache>(static_cast<Profile*>(profile));
}
diff --git a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h
index ec61148bcfb..69255801fa4 100644
--- a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h
+++ b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h
@@ -27,7 +27,7 @@ class NTPResourceCacheFactory : public ProfileKeyedServiceFactory {
~NTPResourceCacheFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};
diff --git a/chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc b/chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
index 07d033787bf..26df3610e56 100644
--- a/chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
+++ b/chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h"
+#include <atomic>
+
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/browser/ui/webui/realbox/realbox_handler.h"
@@ -16,6 +18,24 @@
#include "content/public/browser/web_ui_data_source.h"
#include "ui/webui/color_change_listener/color_change_handler.h"
+namespace {
+
+// This exists to avoid a race condition in RealboxHandler. It's better to
+// rely on a specific variable controlled by local omnibox logic than to
+// get the last active browser which could be changed by anything at any time.
+// This can be eliminated if we find a way to cleanly pass pointers into WebUI
+// construction, but currently they appear designed to avoid such things,
+// sensibly relying on the URL only.
+std::atomic<OmniboxController*> g_omnibox_controller = nullptr;
+
+} // namespace
+
+// static
+void OmniboxPopupUI::SetOmniboxController(
+ OmniboxController* omnibox_controller) {
+ g_omnibox_controller = omnibox_controller;
+}
+
OmniboxPopupUI::OmniboxPopupUI(content::WebUI* web_ui)
: ui::MojoWebUIController(web_ui, /*enable_chrome_send=*/true),
profile_(Profile::FromWebUI(web_ui)) {
@@ -44,10 +64,12 @@ WEB_UI_CONTROLLER_TYPE_IMPL(OmniboxPopupUI)
void OmniboxPopupUI::BindInterface(
mojo::PendingReceiver<omnibox::mojom::PageHandler> pending_page_handler) {
- webui_handler_ = std::make_unique<RealboxHandler>(
+ OmniboxController* controller = g_omnibox_controller;
+ g_omnibox_controller = nullptr;
+
+ handler_ = std::make_unique<RealboxHandler>(
std::move(pending_page_handler), Profile::FromWebUI(web_ui()),
- web_ui()->GetWebContents(), &metrics_reporter_,
- /*is_omnibox_popup_handler=*/true);
+ web_ui()->GetWebContents(), &metrics_reporter_, controller);
}
void OmniboxPopupUI::BindInterface(
diff --git a/chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h b/chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
index b15b23c8a0e..786167f9d3c 100644
--- a/chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
+++ b/chromium/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
@@ -15,6 +15,7 @@
#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom-forward.h"
#include "ui/webui/resources/js/metrics_reporter/metrics_reporter.mojom-forward.h"
+class OmniboxController;
class Profile;
class RealboxHandler;
@@ -25,6 +26,11 @@ class ColorChangeHandler;
// The Web UI controller for the chrome://omnibox-popup.top-chrome.
class OmniboxPopupUI : public ui::MojoWebUIController {
public:
+ // Called in advance of navigation for the webui omnibox popup,
+ // this lets the next instance of the webui handler connect with the
+ // `OmniboxController` instance owned by the `OmniboxView`.
+ static void SetOmniboxController(OmniboxController* omnibox_controller);
+
explicit OmniboxPopupUI(content::WebUI* web_ui);
OmniboxPopupUI(const OmniboxPopupUI&) = delete;
OmniboxPopupUI& operator=(const OmniboxPopupUI&) = delete;
@@ -44,10 +50,10 @@ class OmniboxPopupUI : public ui::MojoWebUIController {
mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
pending_receiver);
- RealboxHandler* webui_handler() { return webui_handler_.get(); }
+ RealboxHandler* handler() { return handler_.get(); }
private:
- std::unique_ptr<RealboxHandler> webui_handler_;
+ std::unique_ptr<RealboxHandler> handler_;
std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
raw_ptr<Profile> profile_;
MetricsReporter metrics_reporter_;
diff --git a/chromium/chrome/browser/ui/webui/password_manager/password_manager_ui.cc b/chromium/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
index e67fda9114a..19b38564ff1 100644
--- a/chromium/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
+++ b/chromium/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/ui/webui/password_manager/password_manager_ui.h"
+#include "base/feature_list.h"
#include "base/i18n/message_formatter.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
@@ -27,6 +28,7 @@
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/password_manager_resources.h"
#include "chrome/grit/password_manager_resources_map.h"
+#include "chrome/grit/theme_resources.h"
#include "components/favicon_base/favicon_url_parser.h"
#include "components/grit/components_scaled_resources.h"
#include "components/password_manager/content/common/web_ui_constants.h"
@@ -38,6 +40,7 @@
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_data_source.h"
+#include "device/fido/features.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
@@ -86,9 +89,11 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
base::make_span(kSettingsSharedResources, kSettingsSharedResourcesSize));
#endif
- static constexpr webui::LocalizedString kStrings[] = {
+ static const webui::LocalizedString kStrings[] = {
{"accountStorageToggleLabel",
- IDS_PASSWORD_MANAGER_UI_ACCOUNT_STORAGE_TOGGLE_LABEL},
+ base::FeatureList::IsEnabled(syncer::kSyncWebauthnCredentials)
+ ? IDS_PASSWORD_MANAGER_UI_ACCOUNT_STORAGE_WITH_PASSKEYS_TOGGLE_LABEL
+ : IDS_PASSWORD_MANAGER_UI_ACCOUNT_STORAGE_TOGGLE_LABEL},
{"addPassword", IDS_PASSWORD_MANAGER_UI_ADD_PASSWORD_BUTTON},
{"addPasswordFooter", IDS_PASSWORD_MANAGER_UI_ADD_PASSWORD_FOOTNOTE},
{"addPasswordStoreOptionAccount",
@@ -157,6 +162,7 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
{"deletePasswordDialogAccount",
IDS_PASSWORD_MANAGER_UI_DELETE_DIALOG_FROM_ACCOUNT_CHECKBOX_LABEL},
{"deletePasswordDialogTitle", IDS_PASSWORD_MANAGER_UI_DELETE_DIALOG_TITLE},
+ {"done", IDS_DONE},
{"disable", IDS_DISABLE},
{"displayNameCopiedToClipboard",
IDS_PASSWORD_MANAGER_UI_DISPLAY_NAME_COPIED_TO_CLIPBOARD},
@@ -165,12 +171,12 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
IDS_PASSWORD_MANAGER_UI_DISPLAY_NAME_PLACEHOLDER},
{"downloadFile", IDS_PASSWORD_MANAGER_UI_DOWNLOAD_FILE},
{"downloadLinkShow", IDS_DOWNLOAD_LINK_SHOW},
- {"edit", IDS_EDIT},
+ {"edit", IDS_EDIT2},
{"editDisclaimerDescription",
IDS_PASSWORD_MANAGER_UI_EDIT_DISCLAIMER_DESCRIPTION},
{"editDisclaimerTitle", IDS_PASSWORD_MANAGER_UI_EDIT_DISCLAIMER_TITLE},
{"editPasskeyTitle", IDS_PASSWORD_MANAGER_UI_EDIT_PASSKEY},
- {"editPassword", IDS_EDIT},
+ {"editPassword", IDS_EDIT2},
{"editPasswordFootnote", IDS_PASSWORD_MANAGER_UI_PASSWORD_EDIT_FOOTNOTE},
{"editPasswordTitle", IDS_PASSWORD_MANAGER_UI_EDIT_PASSWORD},
{"emptyNote", IDS_PASSWORD_MANAGER_UI_NO_NOTE_ADDED},
@@ -240,8 +246,15 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
{"localPasswordManager",
IDS_PASSWORD_BUBBLES_PASSWORD_MANAGER_LINK_TEXT_SAVING_ON_DEVICE},
{"manage", IDS_SETTINGS_MANAGE},
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_WIN)
{"managePasskeysLabel", IDS_PASSWORD_MANAGER_UI_MANAGE_PASSKEYS_LABEL},
+#elif BUILDFLAG(IS_MAC)
+ {"createPasskeysInICloudKeychainLabel",
+ IDS_PASSWORD_MANAGER_UI_CREATE_PASSKEYS_IN_ICLOUD_KEYCHAIN_LABEL},
+ {"createPasskeysInICloudKeychainSubLabel",
+ IDS_PASSWORD_MANAGER_UI_CREATE_PASSKEYS_IN_ICLOUD_KEYCHAIN_SUBLABEL},
+ {"managePasskeysLabel",
+ IDS_PASSWORD_MANAGER_UI_MANAGE_PASSKEYS_FROM_PROFILE_LABEL},
#endif
{"menu", IDS_MENU},
{"menuButtonLabel", IDS_SETTINGS_MENU_BUTTON_LABEL},
@@ -269,6 +282,12 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
{"passwordLabel", IDS_PASSWORD_MANAGER_UI_PASSWORD_LABEL},
{"passwordManager",
IDS_PASSWORD_BUBBLES_PASSWORD_MANAGER_LINK_TEXT_SYNCED_TO_ACCOUNT},
+ // Header for the page, always "Password Manager".
+ {"passwordManagerString", IDS_PASSWORD_MANAGER_UI_TITLE},
+ // Page title, branded. "Google Password Manager" or "Password Manager"
+ // depending on the build.
+ {"passwordManagerTitle",
+ IDS_PASSWORD_BUBBLES_PASSWORD_MANAGER_LINK_TEXT_SAVING_ON_DEVICE},
{"passwordNoteCharacterCount",
IDS_PASSWORD_MANAGER_UI_NOTE_CHARACTER_COUNT},
{"passwordNoteCharacterCountWarning",
@@ -289,6 +308,35 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
{"save", IDS_SAVE},
{"savePasswordsLabel", IDS_PASSWORD_MANAGER_UI_SAVE_PASSWORDS_TOGGLE_LABEL},
{"share", IDS_PASSWORD_MANAGER_UI_SHARE},
+ {"shareDialogTitle", IDS_PASSWORD_MANAGER_UI_SHARE_DIALOG_TITLE},
+ {"shareDialogLoadingTitle",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_LOADING_TITLE},
+ {"shareDialogSuccessTitle",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_SUCCESS_TITLE},
+ {"shareDialogCanceledTitle",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_CANCELED_TITLE},
+ {"sharePasswordFamilyPickerDescription",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_FAMILY_PICKER_DESCRIPTION},
+ {"sharePasswordConfirmationDescriptionSingleRecipient",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_CONFIRMATION_DESCRIPTION_SINGLE},
+ {"sharePasswordConfirmationDescriptionMultipleRecipients",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_CONFIRMATION_DESCRIPTION_MULTIPLE},
+ {"sharePasswordConfirmationFooterWebsite",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_CONFIRMATION_FOOTER_WEBSITE},
+ {"sharePasswordConfirmationFooterAndroidApp",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_CONFIRMATION_FOOTER_ANDROID_APP},
+ {"sharePasswordManageFamily",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_MANAGE_FAMILY},
+ {"sharePasswordMemeberUnavailable",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_MEMBER_UNAVAILABLE},
+ {"sharePasswordNotAvailable",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_NOT_AVAILABLE},
+ {"sharePasswordErrorDescription",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_ERROR_DESCRIPTION},
+ {"sharePasswordErrorTitle",
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_ERROR_TITLE},
+ {"sharePasswordGotIt", IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_GOT_IT},
+ {"sharePasswordTryAgain", IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_TRY_AGAIN},
{"searchPrompt", IDS_PASSWORD_MANAGER_UI_SEARCH_PROMPT},
{"selectFile", IDS_PASSWORD_MANAGER_UI_SELECT_FILE},
{"settings", IDS_PASSWORD_MANAGER_UI_SETTINGS},
@@ -297,7 +345,6 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
{"showPasswordA11yLabel", IDS_PASSWORD_MANAGER_UI_SHOW_PASSWORD_A11Y},
{"sitesAndAppsLabel", IDS_PASSWORD_MANAGER_UI_SITES_AND_APPS_LABEL},
{"sitesLabel", IDS_PASSWORD_MANAGER_UI_SITES_LABEL},
- {"title", IDS_PASSWORD_MANAGER_UI_TITLE},
{"trustedVaultBannerLabelOfferOptIn",
IDS_PASSWORD_MANAGER_UI_TRUSTED_VAULT_OPT_IN_TITLE},
{"trustedVaultBannerSubLabelOfferOptIn",
@@ -347,6 +394,14 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
base::ASCIIToUTF16(chrome::kPasswordManagerLearnMoreURL)));
source->AddString(
+ "sharePasswordNoMembersDescription",
+ l10n_util::GetStringFUTF16(
+ IDS_PASSWORD_MANAGER_UI_SHARE_PASSWORD_NO_MEMBERS_DESCRIPTION,
+ base::ASCIIToUTF16(chrome::kFamilyGroupSiteURL)));
+
+ source->AddString("familyGroupSiteURL", chrome::kFamilyGroupSiteURL);
+
+ source->AddString(
"checkupUrl",
base::UTF8ToUTF16(
password_manager::GetPasswordCheckupURL(
@@ -359,6 +414,11 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
ShouldBiometricAuthenticationForFillingToggleBeVisible(
g_browser_process->local_state()));
#endif
+#if BUILDFLAG(IS_MAC)
+ source->AddBoolean(
+ "createPasskeysInICloudKeychainToggleVisible",
+ base::FeatureList::IsEnabled(device::kWebAuthnICloudKeychain));
+#endif
source->AddBoolean("enablePasswordsImportM2",
base::FeatureList::IsEnabled(
@@ -449,6 +509,7 @@ content::WebUIDataSource* CreateAndAddPasswordsUIHTMLSource(
profile, std::make_unique<FaviconSource>(
profile, chrome::FaviconUrlFormat::kFavicon2));
+ webui::SetupChromeRefresh2023(source);
return source;
}
diff --git a/chromium/chrome/browser/ui/webui/password_manager/promo_card.cc b/chromium/chrome/browser/ui/webui/password_manager/promo_card.cc
index 416360c1ea7..3bce4e3bb66 100644
--- a/chromium/chrome/browser/ui/webui/password_manager/promo_card.cc
+++ b/chromium/chrome/browser/ui/webui/password_manager/promo_card.cc
@@ -12,8 +12,8 @@
#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/sync_service_factory.h"
-#include "chrome/browser/ui/user_education/user_education_service.h"
-#include "chrome/browser/ui/user_education/user_education_service_factory.h"
+#include "chrome/browser/user_education/user_education_service.h"
+#include "chrome/browser/user_education/user_education_service_factory.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/chromium_strings.h"
@@ -168,7 +168,7 @@ PasswordCheckupPromo::PasswordCheckupPromo(
extensions::PasswordsPrivateDelegate* delegate)
: PromoCardInterface(kCheckupPromoId, prefs) {
CHECK(delegate);
- delegate_ = delegate;
+ delegate_ = delegate->AsWeakPtr();
}
PasswordCheckupPromo::~PasswordCheckupPromo() = default;
@@ -178,7 +178,13 @@ std::string PasswordCheckupPromo::GetPromoID() const {
}
bool PasswordCheckupPromo::ShouldShowPromo() const {
- if (delegate_->GetCredentialGroups().empty()) {
+ // Don't show promo if checkup is disabled by policy.
+ if (!prefs_->GetBoolean(
+ password_manager::prefs::kPasswordLeakDetectionEnabled)) {
+ return false;
+ }
+ // Don't show promo if there are no saved passwords.
+ if (!delegate_ || delegate_->GetCredentialGroups().empty()) {
return false;
}
// If promo card was dismissed or shown already for kPromoDisplayLimit times,
@@ -252,7 +258,7 @@ bool PasswordManagerShortcutPromo::ShouldShowPromo() const {
return false;
}
- auto* service = UserEducationServiceFactory::GetForProfile(profile_);
+ auto* service = UserEducationServiceFactory::GetForBrowserContext(profile_);
if (service) {
auto* tutorial_service = &service->tutorial_service();
if (tutorial_service && tutorial_service->IsRunningTutorial()) {
@@ -269,10 +275,8 @@ std::u16string PasswordManagerShortcutPromo::GetTitle() const {
}
std::u16string PasswordManagerShortcutPromo::GetDescription() const {
- return l10n_util::GetStringFUTF16(
- IDS_PASSWORD_MANAGER_UI_SHORTCUT_PROMO_CARD_DESCRIPTION,
- l10n_util::GetStringUTF16(
- IDS_PASSWORD_BUBBLES_PASSWORD_MANAGER_LINK_TEXT_SAVING_ON_DEVICE));
+ return l10n_util::GetStringUTF16(
+ IDS_PASSWORD_MANAGER_UI_SHORTCUT_PROMO_CARD_DESCRIPTION);
}
std::u16string PasswordManagerShortcutPromo::GetActionButtonText() const {
diff --git a/chromium/chrome/browser/ui/webui/password_manager/promo_card.h b/chromium/chrome/browser/ui/webui/password_manager/promo_card.h
index 44b7560037d..0fd82c36432 100644
--- a/chromium/chrome/browser/ui/webui/password_manager/promo_card.h
+++ b/chromium/chrome/browser/ui/webui/password_manager/promo_card.h
@@ -67,8 +67,6 @@ class PromoCardInterface {
int number_of_times_shown_ = 0;
base::Time last_time_shown_;
bool was_dismissed_ = false;
-
- private:
raw_ptr<PrefService> prefs_;
};
@@ -88,7 +86,7 @@ class PasswordCheckupPromo : public PromoCardInterface {
std::u16string GetDescription() const override;
std::u16string GetActionButtonText() const override;
- raw_ptr<extensions::PasswordsPrivateDelegate> delegate_ = nullptr;
+ base::WeakPtr<extensions::PasswordsPrivateDelegate> delegate_;
};
// Promoting web version of Password Manager. Has a link to the website in the
diff --git a/chromium/chrome/browser/ui/webui/password_manager/promo_card_unittest.cc b/chromium/chrome/browser/ui/webui/password_manager/promo_card_unittest.cc
index db6436c43ce..a97c68d6c4f 100644
--- a/chromium/chrome/browser/ui/webui/password_manager/promo_card_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/password_manager/promo_card_unittest.cc
@@ -25,7 +25,6 @@
#include "components/password_manager/core/browser/affiliation/fake_affiliation_service.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/test_password_store.h"
-#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_registry.h"
#include "components/prefs/pref_registry_simple.h"
@@ -200,6 +199,11 @@ TEST_F(PromoCardBaseTest, GetAllPromoCards) {
ASSERT_THAT(pref_service()->GetList(prefs::kPasswordManagerPromoCardsList),
IsEmpty());
+ // Enforce delegate creation before retrieving promo cards.
+ scoped_refptr<extensions::PasswordsPrivateDelegate> delegate =
+ extensions::PasswordsPrivateDelegateFactory::GetForBrowserContext(
+ profile(), true);
+
std::vector<std::unique_ptr<PromoCardInterface>> promo_cards =
PromoCardInterface::GetAllPromoCardsForProfile(profile());
const base::Value::List& list =
@@ -216,7 +220,6 @@ class PromoCardCheckupTest : public PromoCardBaseTest {
public:
void SetUp() override {
PromoCardBaseTest::SetUp();
- feature_list_.InitAndEnableFeature(features::kPasswordsGrouping);
delegate_ =
extensions::PasswordsPrivateDelegateFactory::GetForBrowserContext(
profile(), true);
@@ -239,7 +242,6 @@ class PromoCardCheckupTest : public PromoCardBaseTest {
}
private:
- base::test::ScopedFeatureList feature_list_;
scoped_refptr<extensions::PasswordsPrivateDelegate> delegate_;
};
@@ -256,6 +258,23 @@ TEST_F(PromoCardCheckupTest, NoPromoIfNoPasswords) {
EXPECT_FALSE(promo->ShouldShowPromo());
}
+TEST_F(PromoCardCheckupTest, NoPromoIfLeakCheckDisabledByPolicy) {
+ pref_service()->SetBoolean(
+ password_manager::prefs::kPasswordLeakDetectionEnabled, false);
+ SavePassword();
+
+ ASSERT_THAT(pref_service()->GetList(prefs::kPasswordManagerPromoCardsList),
+ IsEmpty());
+ std::unique_ptr<PromoCardInterface> promo =
+ std::make_unique<PasswordCheckupPromo>(pref_service(), delegate());
+
+ EXPECT_THAT(
+ pref_service()->GetList(prefs::kPasswordManagerPromoCardsList),
+ testing::ElementsAre(PromoCardPrefInfo(PrefInfo{promo->GetPromoID()})));
+
+ EXPECT_FALSE(promo->ShouldShowPromo());
+}
+
TEST_F(PromoCardCheckupTest, PromoShownWithSavedPasswords) {
SavePassword();
diff --git a/chromium/chrome/browser/ui/webui/policy/policy_test_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/policy/policy_test_ui_browsertest.cc
new file mode 100644
index 00000000000..b348dc75b8a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy/policy_test_ui_browsertest.cc
@@ -0,0 +1,773 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/policy/chrome_browser_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_builder.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_impl.h"
+#include "chrome/browser/ui/webui/policy/policy_ui_handler.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
+#include "components/policy/core/common/features.h"
+#include "components/policy/core/common/local_test_policy_provider.h"
+#include "components/policy/core/common/management/management_service.h"
+#include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "components/version_info/channel.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "chrome/test/base/android/android_browser_test.h"
+#else
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#endif
+
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/first_run/scoped_relaunch_chrome_browser_override.h"
+#endif
+
+using testing::_;
+using testing::Return;
+
+namespace {
+class PolicyTestPageVisibilityTest
+ : public PlatformBrowserTest,
+ public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {
+ public:
+ PolicyTestPageVisibilityTest() {
+ // Enable or disable feature as needed
+ scoped_feature_list_.InitWithFeatureState(
+ policy::features::kEnablePolicyTestPage,
+ IsPolicyTestPageEnabledByFeature());
+ }
+ PolicyTestPageVisibilityTest(const PolicyTestPageVisibilityTest&) = delete;
+ PolicyTestPageVisibilityTest& operator=(const PolicyTestPageVisibilityTest&) =
+ delete;
+
+ ~PolicyTestPageVisibilityTest() override = default;
+
+ void SetUpOnMainThread() override {
+ PlatformBrowserTest::SetUpOnMainThread();
+ // Enable or disable policy as needed
+ policy::PolicyMap policy_map;
+ base::Value::List policy_list;
+ policy_list.Append(policy::key::kPolicyTestPageEnabled);
+ policy_map.Set(policy::key::kEnableExperimentalPolicies,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
+ policy::POLICY_SOURCE_PLATFORM,
+ base::Value(std::move(policy_list)), nullptr);
+ policy_map.Set(policy::key::kPolicyTestPageEnabled,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
+ policy::POLICY_SOURCE_PLATFORM,
+ base::Value(IsPolicyTestPageEnabledByPolicy()), nullptr);
+
+ provider_.UpdateChromePolicy(policy_map);
+ }
+
+ void SetUpInProcessBrowserTestFixture() override {
+ provider_.SetDefaultReturns(/*is_initialization_complete_return=*/true,
+ /*is_first_policy_load_complete_return=*/true);
+ policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
+ policy::PushProfilePolicyConnectorProviderForTesting(&provider_);
+ }
+
+ void UpdateProviderPolicyForNamespace(
+ const policy::PolicyNamespace& policy_namespace,
+ const policy::PolicyMap& policy) {
+ policy::PolicyBundle bundle;
+ bundle.Get(policy_namespace) = policy.Clone();
+ provider_.UpdatePolicy(std::move(bundle));
+ }
+
+ Profile* GetProfile() { return chrome_test_utils::GetProfile(this); }
+
+ testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
+
+ bool IsPolicyTestPageEnabledByFeature() { return std::get<0>(GetParam()); }
+ bool IsPolicyTestPageEnabledByPolicy() { return std::get<1>(GetParam()); }
+
+ // Returns true if this profile is not managed.
+ bool IsPolicyTestPageEnabledByManagedProfile() {
+ return std::get<2>(GetParam());
+ }
+
+ int GetProfileManagement() {
+ if (IsPolicyTestPageEnabledByManagedProfile()) {
+ return policy::EnterpriseManagementAuthority::NONE;
+ } else {
+ return policy::EnterpriseManagementAuthority::CLOUD;
+ }
+ }
+
+ bool GetExpectedValue() {
+ return IsPolicyTestPageEnabledByFeature() &&
+ IsPolicyTestPageEnabledByPolicy() &&
+ IsPolicyTestPageEnabledByManagedProfile();
+ }
+
+ content::WebContents* web_contents() {
+ return chrome_test_utils::GetActiveWebContents(this);
+ }
+
+ // Verifies that the policy test page at chrome://policy/test is visible if
+ // expected is true, or not visible if expected is false.
+ void VerifyTestPageVisibility(bool expected) {
+ if (expected) { // Test page should be visible
+ // getElementById returns null if the element is not found and ExecJs
+ // returns whether an error was raised, so use .children here and below as
+ // calling .children on null raises an error.
+ const std::string kJavaScript =
+ "document.getElementById('top-buttons').children;";
+ EXPECT_TRUE(content::ExecJs(web_contents(), kJavaScript));
+ } else { // Main policy page should be visible.
+ const std::string kJavaScript =
+ "document.getElementById('topbar').children;";
+ EXPECT_TRUE(content::ExecJs(web_contents(), kJavaScript));
+ }
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Verify that the chrome://policy/test page is visible only when both the flag
+// and policy are enabled, and invisible otherwise.
+IN_PROC_BROWSER_TEST_P(PolicyTestPageVisibilityTest,
+ TestPageVisibleWhenEnabled) {
+ // Enable or disable managed profile as needed.
+ policy::ScopedManagementServiceOverrideForTesting browser_management(
+ policy::ManagementServiceFactory::GetForProfile(GetProfile()),
+ GetProfileManagement());
+ ASSERT_TRUE(content::NavigateToURL(web_contents(),
+ GURL(chrome::kChromeUIPolicyTestURL)));
+ VerifyTestPageVisibility(GetExpectedValue());
+}
+
+INSTANTIATE_TEST_SUITE_P(PolicyTestPageUITestInstance,
+ PolicyTestPageVisibilityTest,
+ testing::Combine(testing::Bool(),
+ testing::Bool(),
+ testing::Bool()));
+
+namespace {
+class PolicyTestHandlerTest : public PlatformBrowserTest {
+ public:
+ PolicyTestHandlerTest() {
+ scoped_feature_list_.InitWithFeatureState(
+ policy::features::kEnablePolicyTestPage, true);
+
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+ base::StringPiece test_name =
+ ::testing::UnitTest::GetInstance()->current_test_info()->name();
+
+ if (base::StartsWith(test_name, "PRE_")) {
+ // Expect a browser relaunch late in browser shutdown.
+ mock_relaunch_callback_ = std::make_unique<::testing::StrictMock<
+ base::MockCallback<upgrade_util::RelaunchChromeBrowserCallback>>>();
+ EXPECT_CALL(*mock_relaunch_callback_, Run);
+ relaunch_chrome_override_ =
+ std::make_unique<upgrade_util::ScopedRelaunchChromeBrowserOverride>(
+ mock_relaunch_callback_->Get());
+ }
+#endif
+ }
+ PolicyTestHandlerTest(const PolicyTestHandlerTest&) = delete;
+ PolicyTestHandlerTest& operator=(const PolicyTestHandlerTest&) = delete;
+
+ ~PolicyTestHandlerTest() override = default;
+
+ content::WebContents* web_contents() {
+ return chrome_test_utils::GetActiveWebContents(this);
+ }
+
+ std::unique_ptr<PolicyUIHandler> SetUpHandler() {
+ auto handler = std::make_unique<PolicyUIHandler>();
+ web_ui()->set_web_contents(web_contents());
+ handler->set_web_ui_for_test(web_ui());
+ handler->RegisterMessages();
+ return handler;
+ }
+
+ Profile* GetProfile() { return chrome_test_utils::GetProfile(this); }
+
+ protected:
+ content::TestWebUI* web_ui() { return &web_ui_; }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+ content::TestWebUI web_ui_;
+
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+ std::unique_ptr<
+ base::MockCallback<upgrade_util::RelaunchChromeBrowserCallback>>
+ mock_relaunch_callback_;
+ std::unique_ptr<upgrade_util::ScopedRelaunchChromeBrowserOverride>
+ relaunch_chrome_override_;
+#endif
+};
+} // namespace
+
+IN_PROC_BROWSER_TEST_F(PolicyTestHandlerTest,
+ HandleSetAndRevertLocalTestPolicies) {
+ if (!policy::LocalTestPolicyProvider::IsAllowed(chrome::GetChannel())) {
+ return;
+ }
+ std::unique_ptr<PolicyUIHandler> handler = SetUpHandler();
+ const std::string jsonString =
+ R"([
+ {"level": 0,"scope": 0,"source": 0,
+ "name": "AutofillAddressEnabled","value": false},
+ {"level": 1,"scope": 1,"source": 2,
+ "name": "CloudReportingEnabled","value": true}
+ ])";
+
+ base::Value::List list_args;
+
+ list_args.Append("setLocalTestPolicies");
+ list_args.Append(jsonString);
+
+ web_ui()->HandleReceivedMessage("setLocalTestPolicies", list_args);
+
+ base::RunLoop().RunUntilIdle();
+
+ const policy::PolicyNamespace chrome_namespace(policy::POLICY_DOMAIN_CHROME,
+ std::string());
+ policy::PolicyService* policy_service =
+ GetProfile()->GetProfilePolicyConnector()->policy_service();
+
+ // Check correct policies applied
+ const policy::PolicyMap* policy_map =
+ &policy_service->GetPolicies(chrome_namespace);
+ ASSERT_TRUE(policy_map);
+
+ {
+ const policy::PolicyMap::Entry* entry =
+ policy_map->Get(policy::key::kAutofillAddressEnabled);
+ ASSERT_TRUE(entry);
+ const base::Value* value = entry->value(base::Value::Type::BOOLEAN);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(base::Value(false), *value);
+ EXPECT_EQ(entry->level, policy::POLICY_LEVEL_RECOMMENDED);
+ EXPECT_EQ(entry->scope, policy::POLICY_SCOPE_USER);
+ EXPECT_EQ(entry->source, policy::POLICY_SOURCE_ENTERPRISE_DEFAULT);
+ }
+
+ {
+ const policy::PolicyMap::Entry* entry =
+ policy_map->Get(policy::key::kCloudReportingEnabled);
+ ASSERT_TRUE(entry);
+ const base::Value* value = entry->value(base::Value::Type::BOOLEAN);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(base::Value(true), *value);
+ EXPECT_EQ(entry->level, policy::POLICY_LEVEL_MANDATORY);
+ EXPECT_EQ(entry->scope, policy::POLICY_SCOPE_MACHINE);
+ EXPECT_EQ(entry->source, policy::POLICY_SOURCE_CLOUD);
+ }
+
+ list_args.clear();
+ list_args.Append("revertLocalTestPolicies");
+
+ web_ui()->HandleReceivedMessage("revertLocalTestPolicies", list_args);
+
+ base::RunLoop().RunUntilIdle();
+
+ policy_map = &policy_service->GetPolicies(chrome_namespace);
+ ASSERT_TRUE(policy_map);
+
+ // Verify local test policies are no longer applied
+ {
+ const policy::PolicyMap::Entry* entry =
+ policy_map->Get(policy::key::kAutofillAddressEnabled);
+ EXPECT_FALSE(entry);
+ }
+
+ {
+ const policy::PolicyMap::Entry* entry =
+ policy_map->Get(policy::key::kCloudReportingEnabled);
+ EXPECT_FALSE(entry);
+ }
+
+ handler.reset();
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyTestHandlerTest, FilterSensitivePolicies) {
+ if (!policy::LocalTestPolicyProvider::IsAllowed(chrome::GetChannel())) {
+ return;
+ }
+ std::unique_ptr<PolicyUIHandler> handler = SetUpHandler();
+ const std::string jsonString =
+ R"([
+ {"level": 0,"scope": 0,"source": 0,
+ "name": "DefaultSearchProviderEnabled","value": false}
+ ])";
+
+ base::Value::List list_args;
+
+ list_args.Append("setLocalTestPolicies");
+ list_args.Append(jsonString);
+
+ web_ui()->HandleReceivedMessage("setLocalTestPolicies", list_args);
+
+ base::RunLoop().RunUntilIdle();
+
+ const policy::PolicyNamespace chrome_namespace(policy::POLICY_DOMAIN_CHROME,
+ std::string());
+ policy::PolicyService* policy_service =
+ GetProfile()->GetProfilePolicyConnector()->policy_service();
+
+ const policy::PolicyMap* policy_map =
+ &policy_service->GetPolicies(chrome_namespace);
+ ASSERT_TRUE(policy_map);
+
+ // Check sensitive policies not applied
+ const policy::PolicyMap::Entry* entry =
+ policy_map->Get(policy::key::kDefaultSearchProviderEnabled);
+ EXPECT_FALSE(entry);
+
+ handler.reset();
+}
+
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+// TODO(b:293632195) Implement test on android and chromeos
+// Test that test policies stored in the pref kLocalTestPoliciesForNextStartup
+// are applied after restart.
+IN_PROC_BROWSER_TEST_F(PolicyTestHandlerTest,
+ PRE_PRE_ApplyTestPoliciesAfterRestart) {
+ std::unique_ptr<PolicyUIHandler> handler = SetUpHandler();
+
+ // Set the value of AccessCodeCastDeviceDuration to 100.
+ const std::string jsonString =
+ R"([
+ {"level": 0,"scope": 0,"source": 0,
+ "name": "AccessCodeCastDeviceDuration","value": 100}
+ ])";
+
+ base::Value::List list_args;
+
+ list_args.Append("restartBrowser");
+ list_args.Append(jsonString);
+
+ // Restart the browser.
+ web_ui()->HandleReceivedMessage("restartBrowser", list_args);
+
+ handler.reset();
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyTestHandlerTest,
+ PRE_ApplyTestPoliciesAfterRestart) {
+ const policy::PolicyNamespace chrome_namespace(policy::POLICY_DOMAIN_CHROME,
+ std::string());
+ policy::PolicyService* policy_service =
+ GetProfile()->GetProfilePolicyConnector()->policy_service();
+ const policy::PolicyMap* policy_map =
+ &policy_service->GetPolicies(chrome_namespace);
+
+ ASSERT_TRUE(policy_map);
+
+ // Check that the AccessCodeCastDeviceDuration policy is in the policy map and
+ // its value is 100.
+ const policy::PolicyMap::Entry* entry =
+ policy_map->Get(policy::key::kAccessCodeCastDeviceDuration);
+ EXPECT_TRUE(entry);
+ EXPECT_EQ(entry->value(base::Value::Type::INTEGER)->GetInt(), 100);
+
+ // Restart the browser.
+ chrome::AttemptRestart();
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyTestHandlerTest, ApplyTestPoliciesAfterRestart) {
+ const policy::PolicyNamespace chrome_namespace(policy::POLICY_DOMAIN_CHROME,
+ std::string());
+ policy::PolicyService* policy_service =
+ GetProfile()->GetProfilePolicyConnector()->policy_service();
+ const policy::PolicyMap* policy_map =
+ &policy_service->GetPolicies(chrome_namespace);
+
+ ASSERT_TRUE(policy_map);
+
+ // Check that the AccessCodeCastDeviceDuration policy is not in the policy
+ // map.
+ const policy::PolicyMap::Entry* entry =
+ policy_map->Get(policy::key::kAccessCodeCastDeviceDuration);
+ EXPECT_FALSE(entry);
+}
+#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+
+namespace {
+class PolicyTestUITest : public PlatformBrowserTest {
+ public:
+ PolicyTestUITest() {
+ // Enable kEnablePolicyTestPage feature.
+ scoped_feature_list_.InitWithFeatureState(
+ policy::features::kEnablePolicyTestPage, true);
+ }
+ PolicyTestUITest(const PolicyTestUITest&) = delete;
+ PolicyTestUITest& operator=(const PolicyTestUITest&) = delete;
+
+ ~PolicyTestUITest() override = default;
+
+ void SetUpOnMainThread() override {
+ PlatformBrowserTest::SetUpOnMainThread();
+ // Enable kPolicyTestPageEnabled policy.
+ policy::PolicyMap policy_map;
+ base::Value::List policy_list;
+ policy_list.Append(policy::key::kPolicyTestPageEnabled);
+ policy_map.Set(policy::key::kEnableExperimentalPolicies,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
+ policy::POLICY_SOURCE_PLATFORM,
+ base::Value(std::move(policy_list)), nullptr);
+ policy_map.Set(policy::key::kPolicyTestPageEnabled,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
+ policy::POLICY_SOURCE_PLATFORM, base::Value(true), nullptr);
+
+ provider_.UpdateChromePolicy(policy_map);
+
+ // Set profile to unmanaged so chrome://policy/test is accessible.
+ policy::ScopedManagementServiceOverrideForTesting browser_management(
+ policy::ManagementServiceFactory::GetForProfile(GetProfile()),
+ policy::EnterpriseManagementAuthority::NONE);
+ }
+
+ void SetUpInProcessBrowserTestFixture() override {
+ provider_.SetDefaultReturns(/*is_initialization_complete_return=*/true,
+ /*is_first_policy_load_complete_return=*/true);
+ policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
+ policy::PushProfilePolicyConnectorProviderForTesting(&provider_);
+ }
+
+ void UpdateProviderPolicyForNamespace(
+ const policy::PolicyNamespace& policy_namespace,
+ const policy::PolicyMap& policy) {
+ policy::PolicyBundle bundle;
+ bundle.Get(policy_namespace) = policy.Clone();
+ provider_.UpdatePolicy(std::move(bundle));
+ }
+
+ Profile* GetProfile() { return chrome_test_utils::GetProfile(this); }
+
+ content::WebContents* web_contents() {
+ return chrome_test_utils::GetActiveWebContents(this);
+ }
+
+ /* Helper methods for executing JS strings. */
+ content::EvalJsResult GetNumberOfRows() {
+ const std::string getNumRowsJs =
+ R"(
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelectorAll('policy-test-row')
+ .length;
+ )";
+ return content::EvalJs(web_contents(), getNumRowsJs);
+ }
+
+ bool ClickAddPolicy() {
+ const std::string clickAddPolicyJs =
+ R"(
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('#add-policy-btn')
+ .click()
+ )";
+ return content::ExecJs(web_contents(), clickAddPolicyJs);
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+ testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
+};
+} // namespace
+
+IN_PROC_BROWSER_TEST_F(PolicyTestUITest, TestRowAddedWhenAddPolicyClicked) {
+ ASSERT_TRUE(content::NavigateToURL(web_contents(),
+ GURL(chrome::kChromeUIPolicyTestURL)));
+ EXPECT_EQ(GetNumberOfRows(), 1);
+ EXPECT_TRUE(ClickAddPolicy());
+ EXPECT_EQ(GetNumberOfRows(), 2);
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyTestUITest, TestRowDeletedWhenRemoveClicked) {
+ ASSERT_TRUE(content::NavigateToURL(web_contents(),
+ GURL(chrome::kChromeUIPolicyTestURL)));
+ EXPECT_EQ(GetNumberOfRows(), 1);
+ const std::string clickRemoveJs =
+ R"(
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.remove-btn')
+ .click();
+ )";
+ EXPECT_TRUE(content::ExecJs(web_contents(), clickRemoveJs));
+ EXPECT_EQ(GetNumberOfRows(), 0);
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyTestUITest, TestPresetAutofill) {
+ ASSERT_TRUE(content::NavigateToURL(web_contents(),
+ GURL(chrome::kChromeUIPolicyTestURL)));
+ const std::string getSelectedPresetId =
+ R"(
+ const presetDropdown =
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.preset');
+ presetDropdown
+ .options[presetDropdown.selectedIndex]
+ .id;
+ )";
+ EXPECT_EQ(content::EvalJs(web_contents(), getSelectedPresetId), "custom");
+ const std::string getSourceValueJs =
+ R"(
+ const sourceDropdown =
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.source');
+ sourceDropdown
+ .options[sourceDropdown.selectedIndex]
+ .id;
+ )";
+ EXPECT_EQ(content::EvalJs(web_contents(), getSourceValueJs),
+ "sourceEnterpriseDefault");
+ const std::string changePresetToCbcmJs =
+ R"(
+ const presetDropdown =
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.preset');
+ presetDropdown.selectedIndex = 1;
+ presetDropdown
+ .dispatchEvent(new Event('change'));
+ )";
+ EXPECT_TRUE(content::ExecJs(web_contents(), changePresetToCbcmJs));
+ EXPECT_EQ(content::EvalJs(web_contents(), getSelectedPresetId), "cbcm");
+ EXPECT_EQ(content::EvalJs(web_contents(), getSourceValueJs), "sourceCloud");
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyTestUITest, TestPolicyNameChangesInputType) {
+ ASSERT_TRUE(content::NavigateToURL(web_contents(),
+ GURL(chrome::kChromeUIPolicyTestURL)));
+ const std::string getValueInputType =
+ R"(
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.value')
+ .type
+ )";
+ EXPECT_EQ(content::EvalJs(web_contents(), getValueInputType), "text");
+
+ // Select an integer-valued policy name and assert that the type has changed
+ // to number.
+ const std::string selectIntegerPolicyNameJs =
+ R"(
+ const nameDropdown =
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.name');
+ nameDropdown.value = 'CloudReportingUploadFrequency';
+ nameDropdown.dispatchEvent(new Event('change'));
+ )";
+ EXPECT_TRUE(content::ExecJs(web_contents(), selectIntegerPolicyNameJs));
+ EXPECT_EQ(content::EvalJs(web_contents(), getValueInputType), "number");
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyTestUITest, TestErrorStateWhenNameNotSelected) {
+ ASSERT_TRUE(content::NavigateToURL(web_contents(),
+ GURL(chrome::kChromeUIPolicyTestURL)));
+ const std::string getNameDropdownInErrorStateJs =
+ R"(
+ document.querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.name')
+ .classList
+ .contains('error');
+ )";
+ EXPECT_EQ(content::EvalJs(web_contents(), getNameDropdownInErrorStateJs),
+ false);
+ const std::string clickApplyPoliciesJs =
+ R"(document.querySelector('#apply-policies').click())";
+ EXPECT_TRUE(content::ExecJs(web_contents(), clickApplyPoliciesJs));
+ EXPECT_EQ(content::EvalJs(web_contents(), getNameDropdownInErrorStateJs),
+ true);
+
+ // Select a policy name and assert that the name dropdown is not in the error
+ // class, both before and after applying.
+ const std::string selectPolicyNameJs =
+ R"(
+ const nameDropdown =
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.name');
+ nameDropdown.value = 'CloudReportingUploadFrequency';
+ nameDropdown.dispatchEvent(new Event('focus'));
+ )";
+ EXPECT_TRUE(content::ExecJs(web_contents(), selectPolicyNameJs));
+ EXPECT_EQ(content::EvalJs(web_contents(), getNameDropdownInErrorStateJs),
+ false);
+ EXPECT_TRUE(content::ExecJs(web_contents(), clickApplyPoliciesJs));
+ EXPECT_EQ(content::EvalJs(web_contents(), getNameDropdownInErrorStateJs),
+ false);
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyTestUITest, TestIncorrectValueTypeRaisesError) {
+ ASSERT_TRUE(content::NavigateToURL(web_contents(),
+ GURL(chrome::kChromeUIPolicyTestURL)));
+ EXPECT_TRUE(ClickAddPolicy());
+ const std::string getValueCellInErrorStateJs =
+ R"(
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.value')
+ .classList
+ .contains('error');
+ )";
+ EXPECT_EQ(content::EvalJs(web_contents(), getValueCellInErrorStateJs), false);
+
+ // Select a policy name that expects an array value input, attempt to apply
+ // policies with an integer value and assert that the value cell is added to
+ // the error class.
+ const std::string selectNameAndIncorrectValueAndApplyJs =
+ R"(
+ const nameDropdown =
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.name');
+ nameDropdown.value = 'CookiesAllowedForUrls';
+ nameDropdown.dispatchEvent(new Event('change'));
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.value')
+ .value = '123';
+ document.querySelector('#apply-policies').click();
+ )";
+ EXPECT_TRUE(
+ content::ExecJs(web_contents(), selectNameAndIncorrectValueAndApplyJs));
+ EXPECT_EQ(content::EvalJs(web_contents(), getValueCellInErrorStateJs), true);
+
+ // Try applying with an array value and assert that the value cell is not in
+ // the error class before or after applying.
+ const std::string focusOnValueCellJs =
+ R"(
+ const valueCell =
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.value');
+ valueCell.dispatchEvent(new Event('focus'));
+ )";
+ EXPECT_TRUE(content::ExecJs(web_contents(), focusOnValueCellJs));
+ EXPECT_EQ(content::EvalJs(web_contents(), getValueCellInErrorStateJs), false);
+ const std::string applyWithValidValueJs =
+ R"(
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.value')
+ .value = '[]';
+ document.querySelector('#apply-policies').click();
+ )";
+ EXPECT_TRUE(content::ExecJs(web_contents(), applyWithValidValueJs));
+ EXPECT_EQ(content::EvalJs(web_contents(), getValueCellInErrorStateJs), false);
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyTestUITest, TestClearPoliciesButton) {
+ ASSERT_TRUE(content::NavigateToURL(web_contents(),
+ GURL(chrome::kChromeUIPolicyTestURL)));
+ for (int i = 0; i < 4; i++) {
+ EXPECT_TRUE(ClickAddPolicy());
+ }
+ EXPECT_EQ(GetNumberOfRows(), 5);
+ const std::string selectPolicyNameJs =
+ R"(
+ const nameDropdown =
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.name');
+ nameDropdown.value = 'CloudReportingUploadFrequency';
+ )";
+ EXPECT_TRUE(content::ExecJs(web_contents(), selectPolicyNameJs));
+ const std::string getSelectedPolicyNameJs =
+ R"(
+ const nameDropdown =
+ document
+ .querySelector('policy-test-table')
+ .shadowRoot
+ .querySelector('policy-test-row')
+ .shadowRoot
+ .querySelector('.name');
+ nameDropdown.value;
+ )";
+ EXPECT_EQ(content::EvalJs(web_contents(), getSelectedPolicyNameJs),
+ "CloudReportingUploadFrequency");
+ const std::string clickClearJs =
+ R"(document.querySelector('#clear-policies').click())";
+ EXPECT_TRUE(content::ExecJs(web_contents(), clickClearJs));
+ EXPECT_EQ(GetNumberOfRows(), 1);
+ EXPECT_EQ(content::EvalJs(web_contents(), getSelectedPolicyNameJs), "");
+}
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/policy/policy_ui.cc b/chromium/chrome/browser/ui/webui/policy/policy_ui.cc
index 96fe0f7d9c2..112e8ba4ff6 100644
--- a/chromium/chrome/browser/ui/webui/policy/policy_ui.cc
+++ b/chromium/chrome/browser/ui/webui/policy/policy_ui.cc
@@ -1,3 +1,4 @@
+
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,8 +7,13 @@
#include <memory>
+#include "base/json/json_writer.h"
+#include "base/strings/stringprintf.h"
+#include "base/system/sys_info.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
+#include "chrome/browser/policy/value_provider/chrome_policies_value_provider.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/policy/policy_ui_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
@@ -16,33 +22,30 @@
#include "components/grit/policy_resources.h"
#include "components/grit/policy_resources_map.h"
#include "components/policy/core/common/features.h"
+#include "components/policy/core/common/management/management_service.h"
+#include "components/policy/core/common/policy_loader_common.h"
+#include "components/policy/core/common/policy_logger.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/policy/core/common/policy_utils.h"
+#include "components/policy/policy_constants.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "components/version_ui/version_handler_helper.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/user_agent.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/base/webui/web_ui_util.h"
-#if BUILDFLAG(IS_ANDROID)
-#include "base/json/json_writer.h"
-#include "base/strings/stringprintf.h"
-#include "base/system/sys_info.h"
-#include "components/policy/core/common/policy_logger.h"
-#include "components/version_info/version_info.h"
-#include "components/version_ui/version_handler_helper.h"
-#include "content/public/common/user_agent.h"
-#endif // BUILDFLAG(IS_ANDROID)
-
namespace {
-#if BUILDFLAG(IS_ANDROID)
// Returns the operating system information to be displayed on
// chrome://policy/logs page.
std::string GetOsInfo() {
- // The base format for the OS version and build
+#if BUILDFLAG(IS_ANDROID)
+ // The base format for the OS version and build.
constexpr char kOSVersionAndBuildFormat[] = "Android %s %s";
return base::StringPrintf(
kOSVersionAndBuildFormat,
@@ -50,6 +53,11 @@ std::string GetOsInfo() {
(content::GetAndroidOSInfo(content::IncludeAndroidBuildNumber::Include,
content::IncludeAndroidModel::Include))
.c_str());
+#else
+ return base::StringPrintf("%s %s",
+ base::SysInfo::OperatingSystemName().c_str(),
+ base::SysInfo::OperatingSystemVersion().c_str());
+#endif // BUILDFLAG (IS_ANDROID)
}
// Returns the version information to be displayed on the chrome://policy/logs
@@ -64,7 +72,6 @@ base::Value::Dict GetVersionInfo() {
return version_info;
}
-#endif
void CreateAndAddPolicyUIHtmlSource(Profile* profile) {
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
@@ -108,6 +115,7 @@ void CreateAndAddPolicyUIHtmlSource(Profile* profile) {
{"labelUsername", IDS_POLICY_LABEL_USERNAME},
{"labelManagedBy", IDS_POLICY_LABEL_MANAGED_BY},
{"labelVersion", IDS_POLICY_LABEL_VERSION},
+ {"moreActions", IDS_POLICY_MORE_ACTIONS},
{"noPoliciesSet", IDS_POLICY_NO_POLICIES_SET},
{"offHoursActive", IDS_POLICY_OFFHOURS_ACTIVE},
{"offHoursNotActive", IDS_POLICY_OFFHOURS_NOT_ACTIVE},
@@ -133,6 +141,7 @@ void CreateAndAddPolicyUIHtmlSource(Profile* profile) {
#if !BUILDFLAG(IS_CHROMEOS)
{"uploadReport", IDS_UPLOAD_REPORT},
#endif // !BUILDFLAG(IS_CHROMEOS)
+ {"viewLogs", IDS_VIEW_POLICY_LOGS},
};
source->AddLocalizedStrings(kStrings);
@@ -149,7 +158,6 @@ void CreateAndAddPolicyUIHtmlSource(Profile* profile) {
};
source->AddLocalizedStrings(kPolicyLogsStrings);
-#if BUILDFLAG(IS_ANDROID)
source->AddBoolean(
"loggingEnabled",
policy::PolicyLogger::GetInstance()->IsPolicyLoggingEnabled());
@@ -163,16 +171,104 @@ void CreateAndAddPolicyUIHtmlSource(Profile* profile) {
source->AddResourcePath("logs/", IDR_POLICY_LOGS_POLICY_LOGS_HTML);
source->AddResourcePath("logs", IDR_POLICY_LOGS_POLICY_LOGS_HTML);
-#endif // BUILDFLAG(IS_ANDROID)
- if (policy::utils::IsPolicyTestingEnabled(profile->GetPrefs())) {
+ // Test page should only load if testing is enabled and the profile is not
+ // managed by cloud.
+ if (policy::utils::IsPolicyTestingEnabled(profile->GetPrefs()) &&
+ !policy::ManagementServiceFactory::GetForProfile(profile)
+ ->HasManagementAuthority(
+ policy::EnterpriseManagementAuthority::CLOUD)) {
// Localized strings for chrome://policy/test.
static constexpr webui::LocalizedString kPolicyTestStrings[] = {
{"testTitle", IDS_POLICY_TEST_TITLE},
+ {"testRestart", IDS_POLICY_TEST_RESTART_AND_APPLY},
+ {"testApply", IDS_POLICY_TEST_APPLY},
+ {"testImport", IDS_POLICY_TEST_IMPORT},
+ {"testDesc", IDS_POLICY_TEST_DESC},
+ {"testRevertAppliedPolicies", IDS_POLICY_TEST_REVERT},
+ {"testClearPolicies", IDS_CLEAR},
+ {"testTableName", IDS_POLICY_HEADER_NAME},
+ {"testTableSource", IDS_POLICY_HEADER_SOURCE},
+ {"testTableScope", IDS_POLICY_TEST_TABLE_SCOPE},
+ {"testTableLevel", IDS_POLICY_HEADER_LEVEL},
+ {"testTableValue", IDS_POLICY_LABEL_VALUE},
+ {"testTableRemove", IDS_REMOVE},
+ {"testAdd", IDS_POLICY_TEST_ADD},
+ {"testNameSelect", IDS_POLICY_SELECT_NAME},
+ {"testTablePreset", IDS_POLICY_TEST_TABLE_PRESET},
+ {"testTablePresetCustom", IDS_POLICY_TEST_PRESET_CUSTOM},
+ {"testTablePresetLocalMachine", IDS_POLICY_TEST_PRESET_LOCAL_MACHINE},
+ {"testTablePresetCloudAccount", IDS_POLICY_TEST_PRESET_CLOUD_ACCOUNT},
+ {"testUserAffiliated", IDS_POLICY_TEST_USER_AFFILIATED},
};
+
source->AddLocalizedStrings(kPolicyTestStrings);
source->AddResourcePath("test/", IDR_POLICY_TEST_POLICY_TEST_HTML);
source->AddResourcePath("test", IDR_POLICY_TEST_POLICY_TEST_HTML);
+
+ // Create a string policy_names_to_types_str mapping policy names to their
+ // input types.
+ policy::Schema chrome_schema =
+ policy::Schema::Wrap(policy::GetChromeSchemaData());
+ ChromePoliciesValueProvider value_provider(profile);
+ base::Value::List policy_names =
+ (*value_provider.GetNames().FindDict("chrome"))
+ .FindList("policyNames")
+ ->Clone();
+
+ policy_names.EraseIf([&](auto& policy) {
+ return policy::IsPolicyNameSensitive(policy.GetString());
+ });
+
+ std::string policy_names_to_types_str = "{";
+ for (auto& policy_name : policy_names) {
+ base::Value::Type policy_type =
+ chrome_schema.GetKnownProperty(policy_name.GetString()).type();
+ std::string policy_type_string;
+ switch (policy_type) {
+ case base::Value::Type::BOOLEAN:
+ policy_type_string = "boolean";
+ break;
+ case base::Value::Type::DICT:
+ policy_type_string = "dictionary";
+ break;
+ case base::Value::Type::INTEGER:
+ policy_type_string = "integer";
+ break;
+ case base::Value::Type::LIST:
+ policy_type_string = "list";
+ break;
+ case base::Value::Type::STRING:
+ policy_type_string = "string";
+ break;
+ default:
+ break;
+ }
+ policy_names_to_types_str +=
+ "\"" + policy_name.GetString() + "\":\"" + policy_type_string + "\",";
+ }
+ policy_names_to_types_str.pop_back(); // remove last divider
+ policy_names_to_types_str += "}";
+ source->AddString("policyNamesToTypes", policy_names_to_types_str);
+
+ // Strings for policy levels, scopes and sources.
+ static constexpr webui::LocalizedString kPolicyTestTypes[] = {
+ {"scopeUser", IDS_POLICY_SCOPE_USER},
+ {"scopeDevice", IDS_POLICY_SCOPE_DEVICE},
+ {"levelRecommended", IDS_POLICY_LEVEL_RECOMMENDED},
+ {"levelMandatory", IDS_POLICY_LEVEL_MANDATORY},
+ {"sourceEnterpriseDefault", IDS_POLICY_SOURCE_ENTERPRISE_DEFAULT},
+ {"sourceCommandLine", IDS_POLICY_SOURCE_COMMAND_LINE},
+ {"sourceCloud", IDS_POLICY_SOURCE_CLOUD},
+ {"sourceActiveDirectory", IDS_POLICY_SOURCE_ACTIVE_DIRECTORY},
+ {"sourcePlatform", IDS_POLICY_SOURCE_PLATFORM},
+ {"sourceMerged", IDS_POLICY_SOURCE_MERGED},
+ {"sourceCloudFromAsh", IDS_POLICY_SOURCE_CLOUD_FROM_ASH},
+ {"sourceRestrictedManagedGuestSessionOverride",
+ IDS_POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE},
+ };
+
+ source->AddLocalizedStrings(kPolicyTestTypes);
}
webui::SetupWebUIDataSource(
diff --git a/chromium/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
index 0fbbb2a8d5c..c628905e973 100644
--- a/chromium/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
@@ -280,7 +280,7 @@ class TestSelectFileDialog : public ui::SelectFileDialog {
return false;
}
- void ListenerDestroyed() override {}
+ void ListenerDestroyed() override { listener_ = nullptr; }
bool HasMultipleFileTypeChoicesImpl() override { return false; }
@@ -379,7 +379,12 @@ void PolicyUITest::VerifyReportButton(bool visible) {
const std::string kJavaScript = "getReportButtonVisibility();";
std::string ret =
content::EvalJs(web_contents(), kJavaScript).ExtractString();
+
+#if !BUILDFLAG(IS_CHROMEOS)
EXPECT_EQ(visible, ret != "none");
+#else
+ EXPECT_FALSE(ret != "none");
+#endif
}
#if !BUILDFLAG(IS_ANDROID)
@@ -618,7 +623,7 @@ class PolicyUIStatusTest : public MixinBasedInProcessBrowserTest {
bool PolicyUIStatusTest::ReadStatusFor(
const std::string& policy_legend,
base::flat_map<std::string, std::string>* policy_status) {
- // Retrieve the text contents of the status table with specified legend.
+ // Retrieve the text contents of the status table with specified heading.
const std::string javascript = R"JS(
(function() {
function readStatus() {
@@ -633,14 +638,16 @@ bool PolicyUIStatusTest::ReadStatusFor(
const policies = getPolicyFieldsets();
const statuses = {};
for (let i = 0; i < policies.length; ++i) {
- const legend = policies[i].querySelector('legend').textContent;
+ const statusHeading = policies[i]
+ .querySelector('.status-box-heading').textContent;
const entries = {};
const rows = policies[i]
.querySelectorAll('.status-entry div:nth-child(2)');
for (let j = 0; j < rows.length; ++j) {
- entries[rows[j].className] = rows[j].textContent.trim();
+ entries[rows[j].className.split(' ')[0]] = rows[j].textContent
+ .trim();
}
- statuses[legend.trim()] = entries;
+ statuses[statusHeading.trim()] = entries;
}
return JSON.stringify(statuses);
};
@@ -933,12 +940,8 @@ IN_PROC_BROWSER_TEST_F(PolicyUITest, ReportButton) {
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
policy::POLICY_SOURCE_CLOUD, base::Value(true), nullptr);
provider_.UpdateChromePolicy(policy_map);
-#if !BUILDFLAG(IS_CHROMEOS)
VerifyReportButton(/*visible=*/true);
-#else
- // Always hide on Chrome OS.
- VerifyReportButton(/*visible=*/false);
-#endif
+
// Hide while policy is off.
policy_map.Set(policy::key::kCloudReportingEnabled,
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
@@ -947,6 +950,29 @@ IN_PROC_BROWSER_TEST_F(PolicyUITest, ReportButton) {
VerifyReportButton(/*visible=*/false);
}
+IN_PROC_BROWSER_TEST_F(PolicyUITest, ReportButtonWithProfileReporting) {
+ ASSERT_TRUE(
+ content::NavigateToURL(web_contents(), GURL(chrome::kChromeUIPolicyURL)));
+
+ // Hide by default.
+ VerifyReportButton(/*visible=*/false);
+
+ // Turn on with the policy
+ policy::PolicyMap policy_map;
+ policy_map.Set(policy::key::kCloudProfileReportingEnabled,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
+ policy::POLICY_SOURCE_CLOUD, base::Value(true), nullptr);
+ provider_.UpdateChromePolicy(policy_map);
+ VerifyReportButton(/*visible=*/true);
+
+ // Hide while policy is off.
+ policy_map.Set(policy::key::kCloudProfileReportingEnabled,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
+ policy::POLICY_SOURCE_CLOUD, base::Value(false), nullptr);
+ provider_.UpdateChromePolicy(policy_map);
+ VerifyReportButton(/*visible=*/false);
+}
+
#if !BUILDFLAG(IS_CHROMEOS)
class PolicyPrecedenceUITest
: public PolicyUITest,
diff --git a/chromium/chrome/browser/ui/webui/policy/policy_ui_handler.cc b/chromium/chrome/browser/ui/webui/policy/policy_ui_handler.cc
index a289111b267..0187f6fbba4 100644
--- a/chromium/chrome/browser/ui/webui/policy/policy_ui_handler.cc
+++ b/chromium/chrome/browser/ui/webui/policy/policy_ui_handler.cc
@@ -10,6 +10,7 @@
#include <string>
#include <utility>
+#include "base/barrier_closure.h"
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/files/file_util.h"
@@ -30,7 +31,10 @@
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/enterprise/reporting/cloud_profile_reporting_service.h"
+#include "chrome/browser/enterprise/reporting/cloud_profile_reporting_service_factory.h"
#include "chrome/browser/enterprise/util/affiliation.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/browser/policy/policy_ui_utils.h"
#include "chrome/browser/policy/policy_value_and_status_aggregator.h"
@@ -51,10 +55,15 @@
#include "components/policy/core/browser/policy_conversions.h"
#include "components/policy/core/browser/webui/json_generation.h"
#include "components/policy/core/browser/webui/policy_webui_constants.h"
+#include "components/policy/core/browser/webui/statistics_collector.h"
#include "components/policy/core/common/cloud/cloud_policy_manager.h"
#include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
#include "components/policy/core/common/cloud/cloud_policy_util.h"
+#include "components/policy/core/common/local_test_policy_loader.h"
+#include "components/policy/core/common/local_test_policy_provider.h"
#include "components/policy/core/common/policy_details.h"
+#include "components/policy/core/common/policy_logger.h"
+#include "components/policy/core/common/policy_pref_names.h"
#include "components/policy/core/common/policy_scheduler.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/core/common/remote_commands/remote_commands_service.h"
@@ -65,6 +74,8 @@
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "extensions/buildflags/buildflags.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -73,10 +84,6 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
-#if BUILDFLAG(IS_ANDROID)
-#include "components/policy/core/common/policy_logger.h"
-#endif // BUILDFLAG(IS_ANDROID)
-
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/core/device_cloud_policy_store_ash.h"
@@ -93,7 +100,7 @@
namespace {
// Key under which extension policies are grouped in JSON policy exports.
-const char kExtensionsKey[] = "extensions";
+constexpr char kExtensionsKey[] = "extensions";
} // namespace
@@ -103,6 +110,9 @@ PolicyUIHandler::~PolicyUIHandler() {
if (export_policies_select_file_dialog_) {
export_policies_select_file_dialog_->ListenerDestroyed();
}
+ policy::RecordPolicyUIButtonUsage(reload_policies_count_,
+ export_to_json_count_, copy_to_json_count_,
+ upload_report_count_);
}
void PolicyUIHandler::AddCommonLocalizedStringsToSource(
@@ -179,13 +189,29 @@ void PolicyUIHandler::RegisterMessages() {
"copyPoliciesJSON",
base::BindRepeating(&PolicyUIHandler::HandleCopyPoliciesJson,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setLocalTestPolicies",
+ base::BindRepeating(&PolicyUIHandler::HandleSetLocalTestPolicies,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "revertLocalTestPolicies",
+ base::BindRepeating(&PolicyUIHandler::HandleRevertLocalTestPolicies,
+ base::Unretained(this)));
-#if BUILDFLAG(IS_ANDROID)
web_ui()->RegisterMessageCallback(
"getPolicyLogs",
base::BindRepeating(&PolicyUIHandler::HandleGetPolicyLogs,
base::Unretained(this)));
-#endif // BUILDFLAG(IS_ANDROID)
+
+ web_ui()->RegisterMessageCallback(
+ "restartBrowser",
+ base::BindRepeating(&PolicyUIHandler::HandleRestartBrowser,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "setUserAffiliation",
+ base::BindRepeating(&PolicyUIHandler::HandleSetUserAffiliated,
+ base::Unretained(this)));
#if !BUILDFLAG(IS_CHROMEOS)
web_ui()->RegisterMessageCallback(
@@ -218,6 +244,7 @@ void PolicyUIHandler::FileSelectionCanceled(void* params) {
}
void PolicyUIHandler::HandleExportPoliciesJson(const base::Value::List& args) {
+ export_to_json_count_ += 1;
#if BUILDFLAG(IS_ANDROID)
// TODO(crbug.com/1228691): Unify download logic between all platforms to
// use the WebUI download solution (and remove the Android check).
@@ -233,8 +260,9 @@ void PolicyUIHandler::HandleExportPoliciesJson(const base::Value::List& args) {
#else
// If the "select file" dialog window is already opened, we don't want to open
// it again.
- if (export_policies_select_file_dialog_)
+ if (export_policies_select_file_dialog_) {
return;
+ }
content::WebContents* webcontents = web_ui()->GetWebContents();
@@ -266,6 +294,7 @@ void PolicyUIHandler::HandleListenPoliciesUpdates(
}
void PolicyUIHandler::HandleReloadPolicies(const base::Value::List& args) {
+ reload_policies_count_ += 1;
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Allow user to manually fetch remote commands. Useful for testing or when
// the invalidation service is not working properly.
@@ -283,8 +312,9 @@ void PolicyUIHandler::HandleReloadPolicies(const base::Value::List& args) {
if (manager) {
policy::RemoteCommandsService* const remote_commands_service =
manager->core()->remote_commands_service();
- if (remote_commands_service)
+ if (remote_commands_service) {
remote_commands_service->FetchRemoteCommands();
+ }
}
}
#endif
@@ -292,40 +322,104 @@ void PolicyUIHandler::HandleReloadPolicies(const base::Value::List& args) {
}
void PolicyUIHandler::HandleCopyPoliciesJson(const base::Value::List& args) {
+ copy_to_json_count_ += 1;
std::string policies_json = GetPoliciesAsJson();
ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
scw.WriteText(base::UTF8ToUTF16(policies_json));
}
-#if BUILDFLAG(IS_ANDROID)
+void PolicyUIHandler::HandleSetLocalTestPolicies(
+ const base::Value::List& args) {
+ std::string json_policies_string = args[1].GetString();
+
+ policy::LocalTestPolicyProvider* local_test_provider =
+ static_cast<policy::LocalTestPolicyProvider*>(
+ g_browser_process->browser_policy_connector()
+ ->local_test_policy_provider());
+
+ CHECK(local_test_provider);
+
+ Profile::FromWebUI(web_ui())
+ ->GetProfilePolicyConnector()
+ ->UseLocalTestPolicyProvider();
+
+ local_test_provider->LoadJsonPolicies(json_policies_string);
+ AllowJavascript();
+ ResolveJavascriptCallback(args[0], true);
+}
+
+void PolicyUIHandler::HandleRevertLocalTestPolicies(
+ const base::Value::List& args) {
+ Profile::FromWebUI(web_ui())
+ ->GetProfilePolicyConnector()
+ ->RevertUseLocalTestPolicyProvider();
+}
+
+void PolicyUIHandler::HandleRestartBrowser(const base::Value::List& args) {
+ CHECK(args.size() == 2);
+ std::string policies = args[1].GetString();
+
+ // Set policies to preference
+ PrefService* prefs = g_browser_process->local_state();
+ prefs->SetString(policy::policy_prefs::kLocalTestPoliciesForNextStartup,
+ policies);
+
+ // Restart browser
+ chrome::AttemptRestart();
+}
+
+void PolicyUIHandler::HandleSetUserAffiliated(const base::Value::List& args) {
+ CHECK_EQ(static_cast<int>(args.size()), 2);
+ bool affiliated = args[1].GetBool();
+
+ auto* local_test_provider = static_cast<policy::LocalTestPolicyProvider*>(
+ g_browser_process->browser_policy_connector()
+ ->local_test_policy_provider());
+ local_test_provider->SetUserAffiliated(affiliated);
+ AllowJavascript();
+ ResolveJavascriptCallback(args[0], true);
+}
+
void PolicyUIHandler::HandleGetPolicyLogs(const base::Value::List& args) {
DCHECK(policy::PolicyLogger::GetInstance()->IsPolicyLoggingEnabled());
AllowJavascript();
ResolveJavascriptCallback(args[0],
policy::PolicyLogger::GetInstance()->GetAsList());
}
-#endif // BUILDFLAG(IS_ANDROID)
#if !BUILDFLAG(IS_CHROMEOS)
void PolicyUIHandler::HandleUploadReport(const base::Value::List& args) {
+ upload_report_count_ += 1;
DCHECK_EQ(1u, args.size());
std::string callback_id = args[0].GetString();
auto* report_scheduler = g_browser_process->browser_policy_connector()
->chrome_browser_cloud_management_controller()
->report_scheduler();
+
+ auto* profile_report_scheduler =
+ enterprise_reporting::CloudProfileReportingServiceFactory::GetForProfile(
+ Profile::FromWebUI(web_ui()))
+ ->report_scheduler();
+ CHECK(profile_report_scheduler);
+
if (report_scheduler) {
- report_scheduler->UploadFullReport(
+ const auto on_report_uploaded = base::BarrierClosure(
+ 2, base::BindOnce(&PolicyUIHandler::OnReportUploaded,
+ weak_factory_.GetWeakPtr(), callback_id));
+ report_scheduler->UploadFullReport(on_report_uploaded);
+ profile_report_scheduler->UploadFullReport(on_report_uploaded);
+ } else {
+ profile_report_scheduler->UploadFullReport(
base::BindOnce(&PolicyUIHandler::OnReportUploaded,
weak_factory_.GetWeakPtr(), callback_id));
- } else {
- OnReportUploaded(callback_id);
}
}
#endif // !BUILDFLAG(IS_CHROMEOS)
void PolicyUIHandler::SendPolicies() {
- if (!IsJavascriptAllowed())
+ if (!IsJavascriptAllowed()) {
return;
+ }
FireWebUIListener(
"policies-updated",
base::Value(
@@ -335,8 +429,9 @@ void PolicyUIHandler::SendPolicies() {
}
void PolicyUIHandler::SendStatus() {
- if (!IsJavascriptAllowed())
+ if (!IsJavascriptAllowed()) {
return;
+ }
FireWebUIListener(
"status-updated",
@@ -345,8 +440,9 @@ void PolicyUIHandler::SendStatus() {
#if !BUILDFLAG(IS_CHROMEOS)
void PolicyUIHandler::OnReportUploaded(const std::string& callback_id) {
- if (!IsJavascriptAllowed())
+ if (!IsJavascriptAllowed()) {
return;
+ }
ResolveJavascriptCallback(base::Value(callback_id),
/*response=*/base::Value());
SendStatus();
diff --git a/chromium/chrome/browser/ui/webui/policy/policy_ui_handler.h b/chromium/chrome/browser/ui/webui/policy/policy_ui_handler.h
index a6a320f00ef..7edd476d969 100644
--- a/chromium/chrome/browser/ui/webui/policy/policy_ui_handler.h
+++ b/chromium/chrome/browser/ui/webui/policy/policy_ui_handler.h
@@ -47,6 +47,8 @@ class PolicyUIHandler : public content::WebUIMessageHandler,
// policy::PolicyValueAndStatusAggregator::Observer implementation.
void OnPolicyValueAndStatusChanged() override;
+ void set_web_ui_for_test(content::WebUI* web_ui) { set_web_ui(web_ui); }
+
protected:
// ui::SelectFileDialog::Listener implementation.
void FileSelected(const base::FilePath& path,
@@ -59,14 +61,17 @@ class PolicyUIHandler : public content::WebUIMessageHandler,
void HandleListenPoliciesUpdates(const base::Value::List& args);
void HandleReloadPolicies(const base::Value::List& args);
void HandleCopyPoliciesJson(const base::Value::List& args);
+ void HandleSetLocalTestPolicies(const base::Value::List& args);
+ void HandleRevertLocalTestPolicies(const base::Value::List& args);
+ void HandleRestartBrowser(const base::Value::List& args);
+ void HandleSetUserAffiliated(const base::Value::List& args);
+
#if !BUILDFLAG(IS_CHROMEOS)
void HandleUploadReport(const base::Value::List& args);
#endif
-#if BUILDFLAG(IS_ANDROID)
// Handler functions for chrome://policy/logs.
void HandleGetPolicyLogs(const base::Value::List& args);
-#endif // BUILDFLAG(IS_ANDROID)
// Send information about the current policy values to the UI. Information is
// sent in two parts to the UI:
@@ -104,6 +109,11 @@ class PolicyUIHandler : public content::WebUIMessageHandler,
std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
+ uint32_t reload_policies_count_ = 0;
+ uint32_t export_to_json_count_ = 0;
+ uint32_t copy_to_json_count_ = 0;
+ uint32_t upload_report_count_ = 0;
+
base::WeakPtrFactory<PolicyUIHandler> weak_factory_{this};
};
diff --git a/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc
index 0d34745e39b..8d082af8f2a 100644
--- a/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc
+++ b/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc
@@ -31,13 +31,14 @@ void AddLocalizedStrings(content::WebUIDataSource* html_source) {
{"controlledSettingExtension", IDS_CONTROLLED_SETTING_EXTENSION},
{"controlledSettingExtensionWithoutName",
IDS_CONTROLLED_SETTING_EXTENSION_WITHOUT_NAME},
+ {"controlledSettingChildRestriction",
+ IDS_CONTROLLED_SETTING_CHILD_RESTRICTION},
+ {"controlledSettingParent", IDS_CONTROLLED_SETTING_PARENT},
+
#if BUILDFLAG(IS_CHROMEOS_ASH)
{"controlledSettingShared", IDS_CONTROLLED_SETTING_SHARED},
{"controlledSettingWithOwner", IDS_CONTROLLED_SETTING_WITH_OWNER},
{"controlledSettingNoOwner", IDS_CONTROLLED_SETTING_NO_OWNER},
- {"controlledSettingParent", IDS_CONTROLLED_SETTING_PARENT},
- {"controlledSettingChildRestriction",
- IDS_CONTROLLED_SETTING_CHILD_RESTRICTION},
#endif
};
html_source->AddLocalizedStrings(localized_strings);
diff --git a/chromium/chrome/browser/ui/webui/prefs_internals_source.cc b/chromium/chrome/browser/ui/webui/prefs_internals_source.cc
index 20a2164701b..e5573df3703 100644
--- a/chromium/chrome/browser/ui/webui/prefs_internals_source.cc
+++ b/chromium/chrome/browser/ui/webui/prefs_internals_source.cc
@@ -11,6 +11,7 @@
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
+#include "components/local_state/local_state_utils.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
@@ -32,12 +33,7 @@ void PrefsInternalsSource::StartDataRequest(
const content::WebContents::Getter& wc_getter,
content::URLDataSource::GotDataCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- std::string json;
- base::Value::Dict prefs =
- profile_->GetPrefs()->GetPreferenceValues(PrefService::INCLUDE_DEFAULTS);
- CHECK(base::JSONWriter::WriteWithOptions(
- prefs, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json));
-
- std::move(callback).Run(
- base::MakeRefCounted<base::RefCountedString>(std::move(json)));
+ std::move(callback).Run(base::MakeRefCounted<base::RefCountedString>(
+ local_state_utils::GetPrefsAsJson(profile_->GetPrefs())
+ .value_or(std::string())));
}
diff --git a/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc b/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
index e566d2272ec..5c9f5537352 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
@@ -9,6 +9,7 @@
#include "base/functional/bind.h"
#include "base/functional/callback.h"
+#include "base/json/json_reader.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/memory/ref_counted_memory.h"
@@ -37,6 +38,7 @@
#include "printing/pwg_raster_settings.h"
#include "services/device/public/mojom/usb_device.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/size.h"
using extensions::DevicePermissionsManager;
using extensions::Extension;
@@ -103,6 +105,37 @@ extensions::PrinterProviderAPI* GetPrinterProviderAPI(Profile* profile) {
->GetForBrowserContext(profile);
}
+struct ExtensionPrinterSettings {
+ ExtensionPrinterSettings() = default;
+ ExtensionPrinterSettings(ExtensionPrinterSettings&&) noexcept = default;
+ ExtensionPrinterSettings& operator=(ExtensionPrinterSettings&&) noexcept =
+ default;
+ ~ExtensionPrinterSettings() = default;
+
+ std::string destination_id;
+ std::string capabilities;
+ gfx::Size page_size;
+ base::Value::Dict ticket;
+};
+
+// Parses print job `settings` for an extension printer and returns the parsed
+// output. Note that `settings` is created by the Print Preview TS code, so if
+// this function triggers a crash, that means the TS code and the C++ code are
+// out of sync.
+ExtensionPrinterSettings ParseExtensionPrinterSettings(
+ const base::Value::Dict& settings) {
+ ExtensionPrinterSettings parsed_settings;
+ parsed_settings.destination_id = *settings.FindString(kSettingDeviceName);
+ parsed_settings.capabilities = *settings.FindString(kSettingCapabilities);
+ parsed_settings.page_size.SetSize(
+ settings.FindInt(kSettingPageWidth).value_or(0),
+ settings.FindInt(kSettingPageHeight).value_or(0));
+ CHECK(!parsed_settings.page_size.IsEmpty());
+ parsed_settings.ticket =
+ *base::JSONReader::ReadDict(*settings.FindString(kSettingTicket));
+ return parsed_settings;
+}
+
} // namespace
ExtensionPrinterHandler::ExtensionPrinterHandler(Profile* profile)
@@ -167,18 +200,16 @@ void ExtensionPrinterHandler::StartPrint(
base::Value::Dict settings,
scoped_refptr<base::RefCountedMemory> print_data,
PrintCallback callback) {
+ ExtensionPrinterSettings parsed_settings =
+ ParseExtensionPrinterSettings(settings);
+
auto print_job = std::make_unique<extensions::PrinterProviderPrintJob>();
+ print_job->printer_id = std::move(parsed_settings.destination_id);
print_job->job_title = job_title;
- std::string capabilities;
- gfx::Size page_size;
- if (!ParseSettings(settings, &print_job->printer_id, &capabilities,
- &page_size, &print_job->ticket)) {
- std::move(callback).Run(base::Value("Invalid settings"));
- return;
- }
+ print_job->ticket = std::move(parsed_settings.ticket);
cloud_devices::CloudDeviceDescription printer_description;
- printer_description.InitFromString(capabilities);
+ printer_description.InitFromString(parsed_settings.capabilities);
cloud_devices::printer::ContentTypesCapability content_types;
content_types.LoadFrom(printer_description);
@@ -201,7 +232,8 @@ void ExtensionPrinterHandler::StartPrint(
print_job->content_type = kContentTypePWGRaster;
ConvertToPWGRaster(
- print_data, printer_description, ticket, page_size, std::move(print_job),
+ print_data, printer_description, ticket, parsed_settings.page_size,
+ std::move(print_job),
base::BindOnce(&ExtensionPrinterHandler::DispatchPrintJob,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
diff --git a/chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
index a0fb70b3bda..5a26f2f238e 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
@@ -271,8 +271,8 @@ class LocalPrinterHandlerDefaultTestBase : public testing::Test {
#if BUILDFLAG(ENABLE_OOP_PRINTING)
void TearDown() override {
-#if BUILDFLAG(IS_WIN)
if (UseService()) {
+#if BUILDFLAG(IS_WIN)
service_task_runner_->DeleteSoon(
FROM_HERE, std::move(sandboxed_print_backend_service_));
if (SupportFallback()) {
@@ -283,8 +283,10 @@ class LocalPrinterHandlerDefaultTestBase : public testing::Test {
data_decoder_task_runner_->DeleteSoon(FROM_HERE,
std::move(data_decoder_));
}
- }
#endif // BUILDFLAG(IS_WIN)
+ } else {
+ PrintBackend::SetPrintBackendForTesting(nullptr);
+ }
PrintBackendServiceManager::ResetForTesting();
}
diff --git a/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc b/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
index 05498ae9e57..ca963fa03cc 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
@@ -105,7 +105,7 @@ gfx::Size GetDefaultPdfMediaSizeMicrons() {
base::Value::Dict GetPdfCapabilities(
const std::string& locale,
PrinterSemanticCapsAndDefaults::Papers custom_papers) {
- using cloud_devices::printer::MediaType;
+ using cloud_devices::printer::MediaSize;
cloud_devices::CloudDeviceDescription description;
cloud_devices::printer::OrientationCapability orientation;
@@ -125,29 +125,41 @@ base::Value::Dict GetPdfCapabilities(
}
color.SaveTo(&description);
- static const MediaType kPdfMedia[] = {
- MediaType::ISO_A0, MediaType::ISO_A1, MediaType::ISO_A2,
- MediaType::ISO_A3, MediaType::ISO_A4, MediaType::ISO_A5,
- MediaType::NA_LEGAL, MediaType::NA_LETTER, MediaType::NA_LEDGER};
- const gfx::Size default_media_size = GetDefaultPdfMediaSizeMicrons();
- cloud_devices::printer::Media default_media(std::string(), std::string(),
- default_media_size);
- if (!default_media.MatchBySize() ||
- !base::Contains(kPdfMedia, default_media.type)) {
- default_media = cloud_devices::printer::Media(
- locale == "en-US" ? MediaType::NA_LETTER : MediaType::ISO_A4);
+ static constexpr MediaSize kPdfMedia[] = {
+ MediaSize::ISO_A0, MediaSize::ISO_A1, MediaSize::ISO_A2,
+ MediaSize::ISO_A3, MediaSize::ISO_A4, MediaSize::ISO_A5,
+ MediaSize::NA_LEGAL, MediaSize::NA_LETTER, MediaSize::NA_LEDGER};
+ cloud_devices::printer::Media default_media =
+ cloud_devices::printer::MediaBuilder()
+ .WithSizeAndDefaultPrintableArea(GetDefaultPdfMediaSizeMicrons())
+ .WithNameMaybeBasedOnSize(/*custom_display_name=*/"",
+ /*vendor_id=*/"")
+ .Build();
+ if (!base::Contains(kPdfMedia, default_media.size_name)) {
+ default_media =
+ cloud_devices::printer::MediaBuilder()
+ .WithStandardName(locale == "en-US" ? MediaSize::NA_LETTER
+ : MediaSize::ISO_A4)
+ .WithSizeAndPrintableAreaBasedOnStandardName()
+ .Build();
}
cloud_devices::printer::MediaCapability media;
for (const auto& pdf_media : kPdfMedia) {
- cloud_devices::printer::Media media_option(pdf_media);
+ cloud_devices::printer::Media media_option =
+ cloud_devices::printer::MediaBuilder()
+ .WithStandardName(pdf_media)
+ .WithSizeAndPrintableAreaBasedOnStandardName()
+ .Build();
media.AddDefaultOption(media_option,
- default_media.type == media_option.type);
+ default_media.size_name == media_option.size_name);
}
for (const PrinterSemanticCapsAndDefaults::Paper& paper : custom_papers) {
- cloud_devices::printer::Media media_option(paper.display_name,
- paper.vendor_id, paper.size_um,
- paper.printable_area_um);
- media.AddOption(media_option);
+ media.AddOption(cloud_devices::printer::MediaBuilder()
+ .WithCustomName(paper.display_name(), paper.vendor_id())
+ .WithSizeAndPrintableArea(paper.size_um(),
+ paper.printable_area_um())
+ .WithBorderlessVariant(paper.has_borderless_variant())
+ .Build());
}
media.SaveTo(&description);
diff --git a/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_unittest.cc
index 5c91eca41e5..b3810aa66a3 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_unittest.cc
@@ -170,15 +170,15 @@ void RecordCapability(base::OnceClosure done_closure,
base::Value::Dict GetValueFromCustomPaper(
const PrinterSemanticCapsAndDefaults::Paper& paper) {
base::Value::Dict paper_value;
- paper_value.Set("custom_display_name", paper.display_name);
- paper_value.Set("height_microns", paper.size_um.height());
- paper_value.Set("width_microns", paper.size_um.width());
- int imageable_area_left_microns = paper.printable_area_um.x();
- int imageable_area_bottom_microns = paper.printable_area_um.y();
+ paper_value.Set("custom_display_name", paper.display_name());
+ paper_value.Set("height_microns", paper.size_um().height());
+ paper_value.Set("width_microns", paper.size_um().width());
+ int imageable_area_left_microns = paper.printable_area_um().x();
+ int imageable_area_bottom_microns = paper.printable_area_um().y();
int imageable_area_right_microns =
- paper.printable_area_um.x() + paper.printable_area_um.width();
+ paper.printable_area_um().x() + paper.printable_area_um().width();
int imageable_area_top_microns =
- paper.printable_area_um.y() + paper.printable_area_um.height();
+ paper.printable_area_um().y() + paper.printable_area_um().height();
paper_value.Set("imageable_area_left_microns", imageable_area_left_microns);
paper_value.Set("imageable_area_bottom_microns",
imageable_area_bottom_microns);
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 0b842d90a92..5ad203a52f5 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -4,7 +4,6 @@
#include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
-#include <ctype.h>
#include <stddef.h>
#include <memory>
@@ -44,6 +43,7 @@
#include "chrome/browser/ui/webui/print_preview/policy_settings.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_metrics.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_utils.h"
#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/crash_keys.h"
@@ -72,6 +72,10 @@
#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
#include "chrome/browser/enterprise/connectors/analysis/print_content_analysis_utils.h"
+#if BUILDFLAG(IS_MAC)
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#endif // BUILDFLAG(IS_MAC)
#endif
#if BUILDFLAG(IS_CHROMEOS)
@@ -434,8 +438,8 @@ void PrintPreviewHandler::RegisterMessages() {
"getPreview", base::BindRepeating(&PrintPreviewHandler::HandleGetPreview,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
- "print", base::BindRepeating(&PrintPreviewHandler::HandlePrint,
- base::Unretained(this)));
+ "doPrint", base::BindRepeating(&PrintPreviewHandler::HandleDoPrint,
+ base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getPrinterCapabilities",
base::BindRepeating(&PrintPreviewHandler::HandleGetPrinterCapabilities,
@@ -704,7 +708,7 @@ void PrintPreviewHandler::HandleGetPreview(const base::Value::List& args) {
last_preview_settings_ = std::move(settings);
}
-void PrintPreviewHandler::HandlePrint(const base::Value::List& args) {
+void PrintPreviewHandler::HandleDoPrint(const base::Value::List& args) {
CHECK(args[0].is_string());
const std::string& callback_id = args[0].GetString();
CHECK(!callback_id.empty());
@@ -747,6 +751,23 @@ void PrintPreviewHandler::HandlePrint(const base::Value::List& args) {
#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
std::string device_name = *settings.FindString(kSettingDeviceName);
+ using enterprise_connectors::PrintScanningContext;
+ auto scan_context =
+ settings.FindBool(kSettingShowSystemDialog).value_or(false)
+ ? PrintScanningContext::kSystemPrintAfterPreview
+ : PrintScanningContext::kNormalPrintAfterPreview;
+
+#if BUILDFLAG(IS_MAC)
+ if (settings.FindBool(kSettingOpenPDFInPreview).value_or(false)) {
+ // This override only affects reporting of content analysis violations, and
+ // the rest of the printing stack is expected to use the same device name
+ // present in `settings` if content analysis allows printing.
+ device_name =
+ l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_OPEN_PDF_IN_PREVIEW_APP);
+ scan_context = PrintScanningContext::kOpenPdfInPreview;
+ }
+#endif // BUILDFLAG(IS_MAC)
+
auto on_verdict =
base::BindOnce(&PrintPreviewHandler::OnVerdictByEnterprisePolicy,
weak_factory_.GetWeakPtr(), user_action,
@@ -756,11 +777,11 @@ void PrintPreviewHandler::HandlePrint(const base::Value::List& args) {
weak_factory_.GetWeakPtr());
enterprise_connectors::PrintIfAllowedByPolicy(
- data, GetInitiator(), std::move(device_name), std::move(on_verdict),
- std::move(hide_preview));
+ data, GetInitiator(), std::move(device_name), scan_context,
+ std::move(on_verdict), std::move(hide_preview));
#else
- FinishHandlePrint(user_action, std::move(settings), data, callback_id);
+ FinishHandleDoPrint(user_action, std::move(settings), data, callback_id);
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
}
@@ -772,7 +793,7 @@ void PrintPreviewHandler::OnVerdictByEnterprisePolicy(
const std::string& callback_id,
bool allowed) {
if (allowed) {
- FinishHandlePrint(user_action, std::move(settings), data, callback_id);
+ FinishHandleDoPrint(user_action, std::move(settings), data, callback_id);
} else {
OnPrintResult(callback_id, base::Value("NOT_ALLOWED"));
}
@@ -783,7 +804,7 @@ void PrintPreviewHandler::OnHidePreviewDialog() {
}
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
-void PrintPreviewHandler::FinishHandlePrint(
+void PrintPreviewHandler::FinishHandleDoPrint(
UserActionBuckets user_action,
base::Value::Dict settings,
scoped_refptr<base::RefCountedMemory> data,
@@ -1001,7 +1022,9 @@ void PrintPreviewHandler::SendPrinterCapabilities(
const std::string& callback_id,
base::Value::Dict settings_info) {
// Check that |settings_info| is valid.
- if (settings_info.FindDict(kSettingCapabilities)) {
+ base::Value::Dict* settings = settings_info.FindDict(kSettingCapabilities);
+ if (settings) {
+ FilterContinuousFeedMediaSizes(*settings);
VLOG(1) << "Get printer capabilities finished";
ResolveJavascriptCallback(base::Value(callback_id), settings_info);
return;
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index c15592f9b6a..42ad2e92735 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -123,10 +123,10 @@ class PrintPreviewHandler : public content::WebUIMessageHandler {
// Initiates print after any content analysis checks have been passed
// successfully.
- virtual void FinishHandlePrint(UserActionBuckets user_action,
- base::Value::Dict settings,
- scoped_refptr<base::RefCountedMemory> data,
- const std::string& callback_id);
+ virtual void FinishHandleDoPrint(UserActionBuckets user_action,
+ base::Value::Dict settings,
+ scoped_refptr<base::RefCountedMemory> data,
+ const std::string& callback_id);
private:
friend class PrintPreviewPdfGeneratedBrowserTest;
@@ -181,7 +181,7 @@ class PrintPreviewHandler : public content::WebUIMessageHandler {
// Gets the job settings from Web UI and initiate printing. First element of
// |args| is a job settings JSON string.
- void HandlePrint(const base::Value::List& args);
+ void HandleDoPrint(const base::Value::List& args);
// Handles the request to hide the preview dialog for printing.
// |args| is unused.
@@ -269,8 +269,8 @@ class PrintPreviewHandler : public content::WebUIMessageHandler {
#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
// Called when enterprise policy returns a verdict.
- // Calls FinishHandlePrint if it's allowed else calls OnPrintResult to report
- // print not allowed.
+ // Calls FinishHandleDoPrint() if it's allowed or calls OnPrintResult() to
+ // report print not allowed.
void OnVerdictByEnterprisePolicy(UserActionBuckets user_action,
base::Value::Dict settings,
scoped_refptr<base::RefCountedMemory> data,
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc
index 351634c3934..9ae1782ba2f 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc
@@ -4,7 +4,6 @@
#include "chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.h"
-#include <ctype.h>
#include <stddef.h>
#include <memory>
@@ -17,22 +16,28 @@
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/lazy_instance.h"
+#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
+#include "chrome/browser/printing/print_preview_dialog_controller.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_utils.h"
#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
#include "chrome/common/printing/printer_capabilities.h"
+#include "chrome/common/webui_url_constants.h"
#include "chromeos/crosapi/mojom/local_printer.mojom.h"
#include "chromeos/printing/printer_configuration.h"
#include "chromeos/printing/printing_constants.h"
#include "components/signin/public/identity_manager/scope_set.h"
+#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "printing/mojom/print.mojom.h"
+#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/crosapi/crosapi_ash.h"
@@ -117,6 +122,11 @@ void PrintPreviewHandlerChromeOS::RegisterMessages() {
base::BindRepeating(
&PrintPreviewHandlerChromeOS::HandleRecordPrintAttemptOutcome,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getShowManagePrinters",
+ base::BindRepeating(
+ &PrintPreviewHandlerChromeOS::HandleGetShowManagePrinters,
+ base::Unretained(this)));
}
void PrintPreviewHandlerChromeOS::OnJavascriptAllowed() {
@@ -214,6 +224,7 @@ void PrintPreviewHandlerChromeOS::SendPrinterSetup(
return;
}
+ FilterContinuousFeedMediaSizes(*caps_value);
base::Value::Dict response;
response.Set("printerId", printer_name);
response.Set("capabilities", std::move(*caps_value));
@@ -335,4 +346,41 @@ void PrintPreviewHandlerChromeOS::OnServerPrintersChanged() {
FireWebUIListener("server-printers-loading", base::Value(false));
}
+content::WebContents* PrintPreviewHandlerChromeOS::GetInitiator() {
+ if (this->test_initiator_) {
+ return this->test_initiator_;
+ }
+
+ auto* dialog_controller = PrintPreviewDialogController::GetInstance();
+ CHECK(dialog_controller);
+ return dialog_controller->GetInitiator(web_ui()->GetWebContents());
+}
+
+void PrintPreviewHandlerChromeOS::HandleGetShowManagePrinters(
+ const base::Value::List& args) {
+ CHECK_EQ(1U, args.size());
+ CHECK(args[0].is_string());
+
+ // AllowJavascript needs to be called here instead of relying on
+ // `HandleGetInitialSettings` due to timing of calls.
+ if (!IsJavascriptAllowed()) {
+ AllowJavascript();
+ }
+
+ auto* initiator = this->GetInitiator();
+ if (initiator == nullptr) {
+ ResolveJavascriptCallback(args[0], base::Value(false));
+ return;
+ }
+
+ const bool domain_is_os_settings = initiator->GetLastCommittedURL().DomainIs(
+ chrome::kChromeUIOSSettingsHost);
+ ResolveJavascriptCallback(args[0], base::Value(!domain_is_os_settings));
+}
+
+void PrintPreviewHandlerChromeOS::SetInitiatorForTesting(
+ content::WebContents* test_initiator) {
+ this->test_initiator_ = test_initiator;
+}
+
} // namespace printing
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.h b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.h
index 4178d9ca8f0..a24cf4043b9 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.h
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.h
@@ -23,6 +23,10 @@
#include "printing/buildflags/buildflags.h"
#include "printing/print_job_constants.h"
+namespace content {
+class WebContents;
+}
+
namespace printing {
namespace mojom {
@@ -108,8 +112,23 @@ class PrintPreviewHandlerChromeOS : public content::WebUIMessageHandler,
// Records the `PrintPreview.PrintAttemptOutcome` histogram.
void HandleRecordPrintAttemptOutcome(const base::Value::List& args);
+ // Gets the WebContents that initiated print preview request using
+ // `PrintPreviewDialogController`.
+ content::WebContents* GetInitiator();
+
+ // Gets whether the UI should show the button to open printer settings. Button
+ // should be hidden if preview launched from the settings SWA.
+ void HandleGetShowManagePrinters(const base::Value::List& args);
+
+ void SetInitiatorForTesting(content::WebContents* test_initiator);
+
mojo::Receiver<crosapi::mojom::PrintServerObserver> receiver_{this};
+ // Used for testing, when `GetInitiator` called and `test_initiator` is set
+ // then it will be returned instead of calling `PrintPreviewDialogController`
+ // to find the initiator.
+ raw_ptr<content::WebContents> test_initiator_ = nullptr;
+
// Used to transmit mojo interface method calls to ash chrome.
// Null if the interface is unavailable.
// Note that this is not propagated to LocalPrinterHandlerLacros.
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos_unittest.cc
index 8e7074836df..dec27fbbcdb 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos_unittest.cc
@@ -4,6 +4,9 @@
#include "chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.h"
+#include <map>
+#include <memory>
+#include <string>
#include <vector>
#include "base/logging.h"
@@ -12,8 +15,11 @@
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "chrome/browser/ash/crosapi/test_crosapi_dependency_registry.h"
+#include "chrome/browser/printing/print_test_utils.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_utils.h"
+#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/chromeos/printing/fake_local_printer_chromeos.h"
@@ -91,6 +97,97 @@ class FakePrintPreviewUI : public PrintPreviewUI {
private:
};
+struct PrinterInfo {
+ std::string id;
+ bool is_default;
+ base::Value::Dict basic_info;
+ base::Value::Dict capabilities;
+};
+
+class TestPrinterHandlerChromeOS : public PrinterHandler {
+ public:
+ explicit TestPrinterHandlerChromeOS(
+ const std::vector<PrinterInfo>& printers) {
+ SetPrinters(printers);
+ }
+ TestPrinterHandlerChromeOS(const TestPrinterHandlerChromeOS&) = delete;
+ TestPrinterHandlerChromeOS& operator=(const TestPrinterHandlerChromeOS&) =
+ delete;
+ ~TestPrinterHandlerChromeOS() override = default;
+
+ void Reset() override {}
+
+ void GetDefaultPrinter(DefaultPrinterCallback cb) override {
+ std::move(cb).Run(default_printer_);
+ }
+
+ void StartGetPrinters(AddedPrintersCallback added_printers_callback,
+ GetPrintersDoneCallback done_callback) override {
+ if (!printers_.empty()) {
+ added_printers_callback.Run(printers_.Clone());
+ }
+ std::move(done_callback).Run();
+ }
+
+ void StartGetCapability(const std::string& destination_id,
+ GetCapabilityCallback callback) override {
+ std::move(callback).Run(printer_capabilities_[destination_id].Clone());
+ }
+
+ void StartGrantPrinterAccess(const std::string& printer_id,
+ GetPrinterInfoCallback callback) override {}
+
+ void StartPrint(const std::u16string& job_title,
+ base::Value::Dict settings,
+ scoped_refptr<base::RefCountedMemory> print_data,
+ PrintCallback callback) override {
+ std::move(callback).Run(base::Value());
+ }
+
+ void SetPrinters(const std::vector<PrinterInfo>& printers) {
+ printers_.clear();
+ for (const auto& printer : printers) {
+ if (printer.is_default) {
+ default_printer_ = printer.id;
+ }
+ printers_.Append(printer.basic_info.Clone());
+ printer_capabilities_[printer.id] = printer.capabilities.Clone();
+ }
+ }
+
+ private:
+ std::string default_printer_;
+ base::Value::List printers_;
+ std::map<std::string, base::Value::Dict> printer_capabilities_;
+};
+
+class TestPrintPreviewHandlerChromeOS : public PrintPreviewHandlerChromeOS {
+ public:
+ explicit TestPrintPreviewHandlerChromeOS(
+ std::unique_ptr<PrinterHandler> printer_handler)
+ : test_printer_handler_(std::move(printer_handler)) {}
+
+ PrinterHandler* GetPrinterHandler(mojom::PrinterType printer_type) override {
+ return test_printer_handler_.get();
+ }
+
+ private:
+ std::unique_ptr<PrinterHandler> test_printer_handler_;
+};
+
+PrinterInfo GetSimplePrinterInfo(const std::string& name, bool is_default) {
+ PrinterInfo simple_printer;
+ simple_printer.id = name;
+ simple_printer.is_default = is_default;
+ simple_printer.basic_info.Set("printer_name", simple_printer.id);
+ simple_printer.basic_info.Set("printer_description", "Printer for test");
+ simple_printer.basic_info.Set("printer_status", 1);
+ base::Value::Dict cdd;
+ simple_printer.capabilities.Set("printer", simple_printer.basic_info.Clone());
+ simple_printer.capabilities.Set("capabilities", cdd.Clone());
+ return simple_printer;
+}
+
class PrintPreviewHandlerChromeOSTest : public testing::Test {
public:
PrintPreviewHandlerChromeOSTest() = default;
@@ -112,7 +209,16 @@ class PrintPreviewHandlerChromeOSTest : public testing::Test {
web_ui_ = std::make_unique<content::TestWebUI>();
web_ui_->set_web_contents(preview_web_contents_.get());
- auto preview_handler = std::make_unique<PrintPreviewHandlerChromeOS>();
+ // Create printer handler.
+ printers_.push_back(
+ GetSimplePrinterInfo(test::kPrinterName, /*is_default=*/true));
+ auto printer_handler =
+ std::make_unique<TestPrinterHandlerChromeOS>(printers_);
+ printer_handler_ = printer_handler.get();
+
+ auto preview_handler = std::make_unique<TestPrintPreviewHandlerChromeOS>(
+ std::move(printer_handler));
+ preview_handler->SetInitiatorForTesting(preview_web_contents_.get());
handler_ = preview_handler.get();
local_printer_ = std::make_unique<TestLocalPrinter>();
handler_->local_printer_ = local_printer_.get();
@@ -155,6 +261,8 @@ class PrintPreviewHandlerChromeOSTest : public testing::Test {
return local_printer_->TakePrintServerIds();
}
void ChangeServerPrinters() { handler_->OnServerPrintersChanged(); }
+ TestPrinterHandlerChromeOS* printer_handler() { return printer_handler_; }
+ std::vector<PrinterInfo>& printers() { return printers_; }
private:
content::BrowserTaskEnvironment task_environment_;
@@ -168,6 +276,8 @@ class PrintPreviewHandlerChromeOSTest : public testing::Test {
std::unique_ptr<content::WebContents> preview_web_contents_;
std::unique_ptr<content::TestWebUI> web_ui_;
raw_ptr<PrintPreviewHandlerChromeOS> handler_;
+ raw_ptr<TestPrinterHandlerChromeOS> printer_handler_;
+ std::vector<PrinterInfo> printers_;
};
TEST_F(PrintPreviewHandlerChromeOSTest, ChoosePrintServersNoAsh) {
@@ -264,4 +374,64 @@ TEST_F(PrintPreviewHandlerChromeOSTest, OnServerPrintersUpdated) {
EXPECT_EQ(web_ui()->call_data().back()->arg2()->GetBool(), false);
}
+TEST_F(PrintPreviewHandlerChromeOSTest, HandlePrinterSetup) {
+ base::Value::Dict media_1;
+ media_1.Set("width_microns", 100);
+ media_1.Set("height_microns", 200);
+ base::Value::Dict media_2;
+ media_2.Set("width_microns", 300);
+ media_2.Set("is_continuous_feed", true);
+ // After filtering, the expected media will just have the discrete media.
+ base::Value::List expected_media;
+ expected_media.Append(media_1.Clone());
+
+ base::Value::List option_list;
+ option_list.Append(std::move(media_1));
+ option_list.Append(std::move(media_2));
+ base::Value::Dict media_size;
+ media_size.Set("option", std::move(option_list));
+ base::Value::Dict printer;
+ printer.Set("media_size", std::move(media_size));
+ base::Value::Dict cdd;
+ cdd.Set("printer", std::move(printer));
+
+ ASSERT_EQ(1u, printers().size());
+ printers()[0].capabilities.Set(kSettingCapabilities, std::move(cdd));
+ printer_handler()->SetPrinters(printers());
+
+ base::Value::List args;
+ args.Append("callback_id");
+ args.Append(test::kPrinterName);
+ web_ui()->HandleReceivedMessage("setupPrinter", args);
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIResponse", data.function_name());
+ ASSERT_TRUE(data.arg1()->is_string());
+ EXPECT_EQ("callback_id", data.arg1()->GetString());
+ ASSERT_TRUE(data.arg2()->is_bool());
+ EXPECT_TRUE(data.arg2()->GetBool());
+ ASSERT_TRUE(data.arg3()->is_dict());
+ const base::Value::Dict* cdd_result =
+ data.arg3()->GetDict().FindDict(kSettingCapabilities);
+ ASSERT_TRUE(cdd_result);
+ const base::Value::List* options = GetMediaSizeOptionsFromCdd(*cdd_result);
+ ASSERT_TRUE(options);
+ EXPECT_EQ(expected_media, *options);
+}
+
+// Verify 'getShowManagePrinters' can be called.
+TEST_F(PrintPreviewHandlerChromeOSTest, HandleGetCanShowManagePrinters) {
+ const std::string callback_id = "callback-id";
+ base::Value::List args;
+ args.Append(callback_id);
+ web_ui()->HandleReceivedMessage("getShowManagePrinters", args);
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIResponse", data.function_name());
+ ASSERT_TRUE(data.arg1()->is_string());
+ EXPECT_EQ(callback_id, data.arg1()->GetString());
+ ASSERT_TRUE(data.arg2()->is_bool());
+ ASSERT_TRUE(data.arg2()->GetBool());
+}
+
} // namespace printing
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
index fa862b2fb1a..efdaf2f0310 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
@@ -30,10 +30,12 @@
#include "chrome/browser/ui/webui/print_preview/policy_settings.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_metrics.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_utils.h"
#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
+#include "components/enterprise/buildflags/buildflags.h"
#include "components/prefs/pref_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/render_frame_host.h"
@@ -55,9 +57,9 @@
#include "chrome/browser/enterprise/connectors/test/fake_content_analysis_delegate.h"
#include "chrome/browser/policy/dm_token_utils.h"
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
#include "chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.h" // nogncheck
-#endif
+#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
@@ -89,10 +91,10 @@ const char16_t kDummyInitiatorName16[] = u"TestInitiator";
const char kEmptyPrinterName[] = "EmptyPrinter";
const char kTestData[] = "abc";
-#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
constexpr char kFakeDmToken[] = "fake-dm-token";
constexpr char kCallbackId[] = "test-callback-id-1";
-#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
// Array of all mojom::PrinterTypes.
constexpr mojom::PrinterType kAllTypes[] = {mojom::PrinterType::kExtension,
@@ -429,15 +431,15 @@ class TestPrintPreviewHandlerForContentAnalysis
bool print_called_after_scan() const { return print_called_after_scan_; }
private:
- void FinishHandlePrint(UserActionBuckets user_action,
- base::Value::Dict settings,
- scoped_refptr<base::RefCountedMemory> data,
- const std::string& callback_id) override {
+ void FinishHandleDoPrint(UserActionBuckets user_action,
+ base::Value::Dict settings,
+ scoped_refptr<base::RefCountedMemory> data,
+ const std::string& callback_id) override {
ASSERT_EQ(base::StringPiece(data->front_as<const char>(), data->size()),
kTestData);
print_called_after_scan_ = true;
- PrintPreviewHandler::FinishHandlePrint(user_action, std::move(settings),
- data, callback_id);
+ PrintPreviewHandler::FinishHandleDoPrint(user_action, std::move(settings),
+ data, callback_id);
}
bool print_called_after_scan_ = false;
@@ -1292,6 +1294,64 @@ TEST_F(PrintPreviewHandlerTest, GetNoDenyListPrinterCapabilities) {
}
}
+TEST_F(PrintPreviewHandlerTest, GetPrinterCapabilitiesContinuousMedia) {
+ // Add two media sizes to our printer - one with discrete sizes and one with
+ // continuous feed. The continuous feed media should get filtered out when
+ // the capabilities are retrieved.
+ base::Value::Dict media_1;
+ media_1.Set("width_microns", 100);
+ media_1.Set("height_microns", 200);
+ base::Value::Dict media_2;
+ media_2.Set("width_microns", 300);
+ media_2.Set("is_continuous_feed", true);
+ // After filtering, the expected media will just have the discrete media.
+ base::Value::List expected_media;
+ expected_media.Append(media_1.Clone());
+
+ base::Value::List option_list;
+ option_list.Append(std::move(media_1));
+ option_list.Append(std::move(media_2));
+ base::Value::Dict media_size;
+ media_size.Set("option", std::move(option_list));
+ base::Value::Dict printer;
+ printer.Set("media_size", std::move(media_size));
+ base::Value::Dict cdd;
+ cdd.Set("printer", std::move(printer));
+
+ ASSERT_EQ(1u, printers().size());
+ printers()[0].capabilities.Set(kSettingCapabilities, std::move(cdd));
+ printer_handler()->SetPrinters(printers());
+
+ const base::Value::Dict* capabilities =
+ printers()[0].capabilities.FindDict(kSettingCapabilities);
+ ASSERT_TRUE(capabilities);
+ const base::Value::List* options = GetMediaSizeOptionsFromCdd(*capabilities);
+
+ ASSERT_TRUE(options);
+ EXPECT_EQ(2u, options->size());
+
+ // Initial settings first to enable javascript.
+ Initialize();
+
+ std::string callback_id_in = "test-callback-id";
+ handler()->reset_calls();
+ SendGetPrinterCapabilities(mojom::PrinterType::kLocal, callback_id_in,
+ test::kPrinterName);
+ ASSERT_EQ(2u, web_ui()->call_data().size());
+
+ // Validate printer capabilities only has one media type (the continuous feed
+ // option should get filtered out).
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ CheckWebUIResponse(data, callback_id_in, true);
+ ASSERT_TRUE(data.arg3()->is_dict());
+ const base::Value::Dict& settings = data.arg3()->GetDict();
+ capabilities = settings.FindDict(kSettingCapabilities);
+ ASSERT_TRUE(capabilities);
+ options = GetMediaSizeOptionsFromCdd(*capabilities);
+ ASSERT_TRUE(options);
+ EXPECT_EQ(expected_media, *options);
+}
+
TEST_F(PrintPreviewHandlerTest, Print) {
Initialize();
@@ -1319,7 +1379,7 @@ TEST_F(PrintPreviewHandlerTest, Print) {
std::string json;
base::JSONWriter::Write(print_ticket, &json);
print_args.Append(json);
- handler()->HandlePrint(print_args);
+ handler()->HandleDoPrint(print_args);
CheckHistograms(histograms, type);
@@ -1545,7 +1605,7 @@ TEST_F(PrintPreviewHandlerFailingTest, GetPrinterCapabilities) {
}
}
-#if BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
class ContentAnalysisPrintPreviewHandlerTest
: public PrintPreviewHandlerTest,
public testing::WithParamInterface<bool> {
@@ -1615,12 +1675,11 @@ class ContentAnalysisPrintPreviewHandlerTest
private:
base::test::ScopedFeatureList feature_list_;
base::RunLoop run_loop_;
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+
// This installs a fake SDK manager that creates fake SDK clients when
// its GetClient() method is called. This is needed so that calls to
// ContentAnalysisSdkManager::Get()->GetClient() do not fail.
enterprise_connectors::FakeContentAnalysisSdkManager sdk_manager_;
-#endif
};
TEST_P(ContentAnalysisPrintPreviewHandlerTest, LocalScanBeforePrinting) {
@@ -1637,7 +1696,7 @@ TEST_P(ContentAnalysisPrintPreviewHandlerTest, LocalScanBeforePrinting) {
base::JSONWriter::Write(print_ticket, &json);
print_args.Append(json);
- handler()->HandlePrint(print_args);
+ handler()->HandleDoPrint(print_args);
WaitForScan();
auto* print_preview_handler =
@@ -1656,6 +1715,6 @@ INSTANTIATE_TEST_SUITE_P(All,
ContentAnalysisPrintPreviewHandlerTest,
/*scanning_allows_print=*/testing::Bool());
-#endif // BUILDFLAG(ENABLE_PRINT_CONTENT_ANALYSIS)
+#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
} // namespace printing
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 341fffbe509..9e616396c9c 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -11,6 +11,7 @@
#include "base/check.h"
#include "base/containers/flat_map.h"
#include "base/containers/id_map.h"
+#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/lazy_instance.h"
@@ -24,8 +25,8 @@
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/pdf/pdf_extension_util.h"
-#include "chrome/browser/policy/management_utils.h"
#include "chrome/browser/printing/background_printing_manager.h"
#include "chrome/browser/printing/pdf_nup_converter_client.h"
#include "chrome/browser/printing/print_job_manager.h"
@@ -51,6 +52,7 @@
#include "chrome/grit/pdf_resources_map.h"
#include "chrome/grit/print_preview_resources.h"
#include "chrome/grit/print_preview_resources_map.h"
+#include "components/policy/core/common/management/management_service.h"
#include "components/prefs/pref_service.h"
#include "components/printing/browser/print_composite_client.h"
#include "components/printing/browser/print_manager_utils.h"
@@ -73,11 +75,14 @@
#include "ui/base/ui_base_features.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
#include "ui/web_dialogs/web_dialog_delegate.h"
#include "ui/web_dialogs/web_dialog_ui.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.h"
+#include "chrome/common/chrome_features.h"
#endif
#if !BUILDFLAG(OPTIMIZE_WEBUI)
@@ -103,8 +108,8 @@ const char16_t kBasicPrintShortcut[] = u"(Ctrl+Shift+P)";
constexpr char kInvalidArgsForDidStartPreview[] =
"Invalid arguments for DidStartPreview";
-constexpr char kInvalidPageNumberForDidPreviewPage[] =
- "Invalid page number for DidPreviewPage";
+constexpr char kInvalidPageIndexForDidPreviewPage[] =
+ "Invalid page index for DidPreviewPage";
constexpr char kInvalidPageCountForMetafileReadyForPrinting[] =
"Invalid page count for MetafileReadyForPrinting";
@@ -120,8 +125,8 @@ void StopWorker(int document_cookie) {
queue->PopPrinterQuery(document_cookie);
}
-bool IsValidPageNumber(uint32_t page_number, uint32_t page_count) {
- return page_number < page_count;
+bool IsValidPageIndex(uint32_t page_index, uint32_t page_count) {
+ return page_index < page_count;
}
bool ShouldUseCompositor(PrintPreviewUI* print_preview_ui) {
@@ -155,6 +160,7 @@ void AddPrintPreviewStrings(content::WebUIDataSource* source) {
IDS_PRINT_PREVIEW_ADVANCED_SETTINGS_DIALOG_TITLE},
{"advancedSettingsSearchBoxPlaceholder",
IDS_PRINT_PREVIEW_ADVANCED_SETTINGS_SEARCH_BOX_PLACEHOLDER},
+ {"borderlessLabel", IDS_PRINT_PREVIEW_BORDERLESS_LABEL},
{"bottom", IDS_PRINT_PREVIEW_BOTTOM_MARGIN_LABEL},
{"cancel", IDS_CANCEL},
{"clearSearch", IDS_CLEAR_SEARCH},
@@ -176,9 +182,13 @@ void AddPrintPreviewStrings(content::WebUIDataSource* source) {
{"left", IDS_PRINT_PREVIEW_LEFT_MARGIN_LABEL},
{"loading", IDS_PRINT_PREVIEW_LOADING},
{"manage", IDS_PRINT_PREVIEW_MANAGE},
+#if BUILDFLAG(IS_CHROMEOS)
+ {"managePrintersLabel", IDS_PRINT_PREVIEW_MANAGE_PRINTERS_LABEL},
+#endif
{"managedSettings", IDS_PRINT_PREVIEW_MANAGED_SETTINGS_TEXT},
{"marginsLabel", IDS_PRINT_PREVIEW_MARGINS_LABEL},
{"mediaSizeLabel", IDS_PRINT_PREVIEW_MEDIA_SIZE_LABEL},
+ {"mediaTypeLabel", IDS_PRINT_PREVIEW_MEDIA_TYPE_LABEL},
{"minimumMargins", IDS_PRINT_PREVIEW_MINIMUM_MARGINS},
{"moreOptionsLabel", IDS_MORE_OPTIONS_LABEL},
{"newShowAdvancedOptions", IDS_PRINT_PREVIEW_NEW_SHOW_ADVANCED_OPTIONS},
@@ -223,6 +233,14 @@ void AddPrintPreviewStrings(content::WebUIDataSource* source) {
{"printDestinationsTitle", IDS_PRINT_PREVIEW_PRINT_DESTINATIONS_TITLE},
{"printPagesLabel", IDS_PRINT_PREVIEW_PRINT_PAGES_LABEL},
#if BUILDFLAG(IS_CHROMEOS)
+ {"printerSetupInfoMessageDetailNoPrintersText",
+ IDS_PRINT_PREVIEW_PRINTER_SETUP_INFO_MESSAGE_DETAIL_NO_PRINTERS_TEXT},
+ {"printerSetupInfoMessageDetailPrinterOfflineText",
+ IDS_PRINT_PREVIEW_PRINTER_SETUP_INFO_MESSAGE_DETAIL_PRINTER_OFFLINE_TEXT},
+ {"printerSetupInfoMessageHeadingNoPrintersText",
+ IDS_PRINT_PREVIEW_PRINTER_SETUP_INFO_MESSAGE_HEADING_NO_PRINTERS_TEXT},
+ {"printerSetupInfoMessageHeadingPrinterOfflineText",
+ IDS_PRINT_PREVIEW_PRINTER_SETUP_INFO_MESSAGE_HEADING_PRINTER_OFFLINE_TEXT},
{"printToGoogleDrive", IDS_PRINT_PREVIEW_PRINT_TO_GOOGLE_DRIVE},
#endif
{"printToPDF", IDS_PRINT_PREVIEW_PRINT_TO_PDF},
@@ -303,14 +321,26 @@ void AddPrintPreviewStrings(content::WebUIDataSource* source) {
void AddPrintPreviewFlags(content::WebUIDataSource* source, Profile* profile) {
#if BUILDFLAG(IS_CHROMEOS)
source->AddBoolean("useSystemDefaultPrinter", false);
+ source->AddBoolean(
+ "isPrintPreviewSetupAssistanceEnabled",
+ base::FeatureList::IsEnabled(::features::kPrintPreviewSetupAssistance));
#else
bool system_default_printer = profile->GetPrefs()->GetBoolean(
prefs::kPrintPreviewUseSystemDefaultPrinter);
source->AddBoolean("useSystemDefaultPrinter", system_default_printer);
#endif
- source->AddBoolean("isEnterpriseManaged",
- policy::IsDeviceEnterpriseManaged());
+ source->AddBoolean(
+ "isEnterpriseManaged",
+ policy::ManagementServiceFactory::GetForPlatform()->IsManaged());
+
+#if BUILDFLAG(IS_CHROMEOS)
+ source->AddBoolean(
+ "isBorderlessPrintingEnabled",
+ base::FeatureList::IsEnabled(features::kEnableBorderlessPrinting));
+#else
+ source->AddBoolean("isBorderlessPrintingEnabled", false);
+#endif
}
void SetupPrintPreviewPlugin(content::WebUIDataSource* source) {
@@ -458,7 +488,7 @@ void PrintPreviewUI::ClearAllPreviewData() {
}
void PrintPreviewUI::NotifyUIPreviewPageReady(
- uint32_t page_number,
+ uint32_t page_index,
int request_id,
scoped_refptr<base::RefCountedMemory> data_bytes) {
if (!data_bytes || !data_bytes->size())
@@ -468,13 +498,13 @@ void PrintPreviewUI::NotifyUIPreviewPageReady(
if (ShouldCancelRequest(id_, request_id))
return;
- DCHECK_NE(page_number, kInvalidPageIndex);
- SetPrintPreviewDataForIndex(base::checked_cast<int>(page_number),
+ DCHECK_NE(page_index, kInvalidPageIndex);
+ SetPrintPreviewDataForIndex(base::checked_cast<int>(page_index),
std::move(data_bytes));
if (g_test_delegate)
g_test_delegate->DidRenderPreviewPage(web_ui()->GetWebContents());
- handler_->SendPagePreviewReady(base::checked_cast<int>(page_number), *id_,
+ handler_->SendPagePreviewReady(base::checked_cast<int>(page_index), *id_,
request_id);
}
@@ -504,7 +534,7 @@ void PrintPreviewUI::NotifyUIPreviewDocumentReady(
}
void PrintPreviewUI::OnCompositePdfPageDone(
- uint32_t page_number,
+ uint32_t page_index,
int document_cookie,
int32_t request_id,
mojom::PrintCompositor::Status status,
@@ -522,19 +552,19 @@ void PrintPreviewUI::OnCompositePdfPageDone(
if (pages_per_sheet_ == 1) {
NotifyUIPreviewPageReady(
- page_number, request_id,
+ page_index, request_id,
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(region));
} else {
AddPdfPageForNupConversion(std::move(region));
- uint32_t current_page_index = GetPageToNupConvertIndex(page_number);
+ uint32_t current_page_index = GetPageToNupConvertIndex(page_index);
if (current_page_index == kInvalidPageIndex)
return;
if (((current_page_index + 1) % pages_per_sheet_) == 0 ||
- LastPageComposited(page_number)) {
- uint32_t new_page_number =
+ LastPageComposited(page_index)) {
+ uint32_t new_page_index =
base::checked_cast<uint32_t>(current_page_index / pages_per_sheet_);
- DCHECK_NE(new_page_number, kInvalidPageIndex);
+ DCHECK_NE(new_page_index, kInvalidPageIndex);
std::vector<base::ReadOnlySharedMemoryRegion> pdf_page_regions =
TakePagesForNupConvert();
@@ -554,7 +584,7 @@ void PrintPreviewUI::OnCompositePdfPageDone(
std::move(pdf_page_regions),
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(&PrintPreviewUI::OnNupPdfConvertDone,
- weak_ptr_factory_.GetWeakPtr(), new_page_number,
+ weak_ptr_factory_.GetWeakPtr(), new_page_index,
request_id),
mojom::PdfNupConverter::Status::CONVERSION_FAILURE,
base::ReadOnlySharedMemoryRegion()));
@@ -563,7 +593,7 @@ void PrintPreviewUI::OnCompositePdfPageDone(
}
void PrintPreviewUI::OnNupPdfConvertDone(
- uint32_t page_number,
+ uint32_t page_index,
int32_t request_id,
mojom::PdfNupConverter::Status status,
base::ReadOnlySharedMemoryRegion region) {
@@ -575,7 +605,7 @@ void PrintPreviewUI::OnNupPdfConvertDone(
}
NotifyUIPreviewPageReady(
- page_number, request_id,
+ page_index, request_id,
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(region));
}
@@ -654,17 +684,18 @@ void PrintPreviewUI::SetInitiatorTitle(const std::u16string& job_title) {
initiator_title_ = job_title;
}
-bool PrintPreviewUI::LastPageComposited(uint32_t page_number) const {
+bool PrintPreviewUI::LastPageComposited(uint32_t page_index) const {
if (pages_to_render_.empty())
return false;
- return page_number == pages_to_render_.back();
+ return page_index == pages_to_render_.back();
}
-uint32_t PrintPreviewUI::GetPageToNupConvertIndex(uint32_t page_number) const {
- for (size_t index = 0; index < pages_to_render_.size(); ++index) {
- if (page_number == pages_to_render_[index])
+uint32_t PrintPreviewUI::GetPageToNupConvertIndex(uint32_t page_index) const {
+ for (uint32_t index : pages_to_render_) {
+ if (page_index == index) {
return index;
+ }
}
return kInvalidPageIndex;
}
@@ -761,8 +792,8 @@ void PrintPreviewUI::DidStartPreview(mojom::DidStartPreviewParamsPtr params,
return;
}
- for (uint32_t page_number : params->pages_to_render) {
- if (!IsValidPageNumber(page_number, params->page_count)) {
+ for (uint32_t page_index : params->pages_to_render) {
+ if (!IsValidPageIndex(page_index, params->page_count)) {
receiver_.ReportBadMessage(kInvalidArgsForDidStartPreview);
return;
}
@@ -781,7 +812,7 @@ void PrintPreviewUI::DidStartPreview(mojom::DidStartPreviewParamsPtr params,
pages_to_render_ = params->pages_to_render;
pages_to_render_index_ = 0;
pages_per_sheet_ = params->pages_per_sheet;
- page_size_ = params->page_size;
+ page_size_ = ToFlooredSize(params->page_size);
ClearAllPreviewData();
if (g_test_delegate)
@@ -792,7 +823,7 @@ void PrintPreviewUI::DidStartPreview(mojom::DidStartPreviewParamsPtr params,
void PrintPreviewUI::DidGetDefaultPageLayout(
mojom::PageSizeMarginsPtr page_layout_in_points,
- const gfx::Rect& printable_area_in_points,
+ const gfx::RectF& printable_area_in_points,
bool all_pages_have_custom_size,
bool all_pages_have_custom_orientation,
int32_t request_id) {
@@ -802,7 +833,7 @@ void PrintPreviewUI::DidGetDefaultPageLayout(
return;
}
// Save printable_area_in_points information for N-up conversion.
- printable_area_ = printable_area_in_points;
+ printable_area_ = ToEnclosedRect(printable_area_in_points);
if (page_layout_in_points->margin_top < 0 ||
page_layout_in_points->margin_left < 0 ||
@@ -829,11 +860,11 @@ void PrintPreviewUI::DidGetDefaultPageLayout(
all_pages_have_custom_orientation, request_id);
}
-bool PrintPreviewUI::OnPendingPreviewPage(uint32_t page_number) {
+bool PrintPreviewUI::OnPendingPreviewPage(uint32_t page_index) {
if (pages_to_render_index_ >= pages_to_render_.size())
return false;
- bool matched = page_number == pages_to_render_[pages_to_render_index_];
+ bool matched = page_index == pages_to_render_[pages_to_render_index_];
++pages_to_render_index_;
return matched;
}
@@ -925,15 +956,15 @@ void PrintPreviewUI::DidPrepareDocumentForPreview(int32_t document_cookie,
void PrintPreviewUI::DidPreviewPage(mojom::DidPreviewPageParamsPtr params,
int32_t request_id) {
- uint32_t page_number = params->page_number;
+ uint32_t page_index = params->page_index;
const mojom::DidPrintContentParams& content = *params->content;
- if (page_number == kInvalidPageIndex ||
+ if (page_index == kInvalidPageIndex ||
!content.metafile_data_region.IsValid()) {
return;
}
- if (!OnPendingPreviewPage(page_number)) {
- receiver_.ReportBadMessage(kInvalidPageNumberForDidPreviewPage);
+ if (!OnPendingPreviewPage(page_index)) {
+ receiver_.ReportBadMessage(kInvalidPageIndexForDidPreviewPage);
return;
}
@@ -961,13 +992,13 @@ void PrintPreviewUI::DidPreviewPage(mojom::DidPreviewPageParamsPtr params,
params->document_cookie, render_frame_host, content,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(&PrintPreviewUI::OnCompositePdfPageDone,
- weak_ptr_factory_.GetWeakPtr(), page_number,
+ weak_ptr_factory_.GetWeakPtr(), page_index,
params->document_cookie, request_id),
mojom::PrintCompositor::Status::kCompositingFailure,
base::ReadOnlySharedMemoryRegion()));
} else {
NotifyUIPreviewPageReady(
- page_number, request_id,
+ page_index, request_id,
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(
content.metafile_data_region));
}
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.h b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.h
index 5bf273f42d9..9ae67edac44 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.h
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.h
@@ -28,6 +28,7 @@
#include "printing/mojom/print.mojom-forward.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#if BUILDFLAG(ENABLE_OOP_PRINTING)
@@ -77,7 +78,7 @@ class PrintPreviewUI : public ConstrainedWebDialogUI,
void PrinterSettingsInvalid(int32_t document_cookie,
int32_t request_id) override;
void DidGetDefaultPageLayout(mojom::PageSizeMarginsPtr page_layout_in_points,
- const gfx::Rect& printable_area_in_points,
+ const gfx::RectF& printable_area_in_points,
bool all_pages_have_custom_size,
bool all_pages_have_custom_orientation,
int32_t request_id) override;
@@ -109,13 +110,13 @@ class PrintPreviewUI : public ConstrainedWebDialogUI,
PrintPreviewHandler* handler() const { return handler_; }
- // Returns true if |page_number| is the last page in |pages_to_render_|.
- // |page_number| is a 0-based number.
- bool LastPageComposited(uint32_t page_number) const;
+ // Returns true if `page_index` is the last page in `pages_to_render_`.
+ // `page_index` is a 0-based.
+ bool LastPageComposited(uint32_t page_index) const;
- // Get the 0-based index of the |page_number| in |pages_to_render_|.
- // Same as above, |page_number| is a 0-based number.
- uint32_t GetPageToNupConvertIndex(uint32_t page_number) const;
+ // Get the 0-based index of the `page_index` in `pages_to_render_`.
+ // `page_index` is a 0-based.
+ uint32_t GetPageToNupConvertIndex(uint32_t page_index) const;
std::vector<base::ReadOnlySharedMemoryRegion> TakePagesForNupConvert();
@@ -137,10 +138,10 @@ class PrintPreviewUI : public ConstrainedWebDialogUI,
// Notifies the Web UI of a print preview request with |request_id|.
virtual void OnPrintPreviewRequest(int request_id);
- // Notifies the Web UI that the 0-based page |page_number| rendering is being
+ // Notifies the Web UI that the 0-based page `page_index` rendering is being
// processed and an OnPendingPreviewPage() call is imminent. Returns whether
- // |page_number| is the expected page.
- bool OnPendingPreviewPage(uint32_t page_number);
+ // `page_index` is the expected page.
+ bool OnPendingPreviewPage(uint32_t page_index);
// Notifies the Web UI that the print preview failed to render for the request
// with id = |request_id|.
@@ -226,10 +227,10 @@ class PrintPreviewUI : public ConstrainedWebDialogUI,
// Clear the existing print preview data.
void ClearAllPreviewData();
- // Notifies the Web UI that the 0-based page |page_number| has been rendered.
- // |request_id| indicates which request resulted in this response.
+ // Notifies the Web UI that the 0-based page `page_index` has been rendered.
+ // `request_id` indicates which request resulted in this response.
void NotifyUIPreviewPageReady(
- uint32_t page_number,
+ uint32_t page_index,
int request_id,
scoped_refptr<base::RefCountedMemory> data_bytes);
@@ -242,12 +243,12 @@ class PrintPreviewUI : public ConstrainedWebDialogUI,
// Callbacks for print compositor client.
void OnPrepareForDocumentToPdfDone(int32_t request_id,
mojom::PrintCompositor::Status status);
- void OnCompositePdfPageDone(uint32_t page_number,
+ void OnCompositePdfPageDone(uint32_t page_index,
int32_t document_cookie,
int32_t request_id,
mojom::PrintCompositor::Status status,
base::ReadOnlySharedMemoryRegion region);
- void OnNupPdfConvertDone(uint32_t page_number,
+ void OnNupPdfConvertDone(uint32_t page_index,
int32_t request_id,
mojom::PdfNupConverter::Status status,
base::ReadOnlySharedMemoryRegion region);
@@ -305,13 +306,14 @@ class PrintPreviewUI : public ConstrainedWebDialogUI,
// title.
std::u16string initiator_title_;
- // The list of 0-based page numbers that will be rendered.
+ // The list of 0-based page indices that will be rendered.
std::vector<uint32_t> pages_to_render_;
// The list of pages to be converted.
std::vector<base::ReadOnlySharedMemoryRegion> pages_for_nup_convert_;
- // Index into |pages_to_render_| for the page number to expect.
+ // Index into `pages_to_render_`. The expected page index is the value at
+ // `pages_to_render_[pages_to_render_index_]`
size_t pages_to_render_index_ = 0;
// number of pages per sheet and should be greater or equal to 1.
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.cc
index e1b67066465..6aeeb81d5cf 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.cc
@@ -4,15 +4,12 @@
#include "chrome/browser/ui/webui/print_preview/print_preview_utils.h"
-#include <memory>
#include <string>
#include <utility>
-#include <vector>
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
-#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/string_piece.h"
@@ -46,6 +43,8 @@ const char kTypeKey[] = "type";
const char kDpiCapabilityKey[] = "dpi";
const char kHorizontalDpi[] = "horizontal_dpi";
const char kVerticalDpi[] = "vertical_dpi";
+const char kMediaSizeKey[] = "media_size";
+const char kIsContinuousFeed[] = "is_continuous_feed";
// The dictionary key for the CDD item containing custom vendor capabilities.
const char kVendorCapabilityKey[] = "vendor_capability";
@@ -218,6 +217,37 @@ base::Value::Dict UpdateCddWithDpiIfMissing(base::Value::Dict cdd) {
return cdd;
}
+const base::Value::List* GetMediaSizeOptionsFromCdd(
+ const base::Value::Dict& cdd) {
+ const base::Value::Dict* printer = cdd.FindDict(kPrinter);
+ if (!printer) {
+ return nullptr;
+ }
+ const base::Value::Dict* media_size = printer->FindDict(kMediaSizeKey);
+ if (!media_size) {
+ return nullptr;
+ }
+ return media_size->FindList(kOptionKey);
+}
+
+void FilterContinuousFeedMediaSizes(base::Value::Dict& cdd) {
+ // OK to const_cast here since `cdd` started off non-const.
+ base::Value::List* options =
+ const_cast<base::Value::List*>(GetMediaSizeOptionsFromCdd(cdd));
+ if (!options) {
+ return;
+ }
+
+ options->EraseIf([](const base::Value& item) {
+ const base::Value::Dict* item_dict = item.GetIfDict();
+ if (!item_dict) {
+ return false;
+ }
+ absl::optional<bool> is_continuous = item_dict->FindBool(kIsContinuousFeed);
+ return is_continuous.value_or(false);
+ });
+}
+
void ConvertPrinterListForCallback(
PrinterHandler::AddedPrintersCallback callback,
PrinterHandler::GetPrintersDoneCallback done_callback,
@@ -259,30 +289,4 @@ void StartLocalPrint(base::Value::Dict job_settings,
preview_web_contents->GetPrimaryMainFrame(), std::move(callback));
}
-bool ParseSettings(const base::Value::Dict& settings,
- std::string* out_destination_id,
- std::string* out_capabilities,
- gfx::Size* out_page_size,
- base::Value::Dict* out_ticket) {
- const std::string* ticket_opt = settings.FindString(kSettingTicket);
- const std::string* capabilities_opt =
- settings.FindString(kSettingCapabilities);
- out_page_size->SetSize(settings.FindInt(kSettingPageWidth).value_or(0),
- settings.FindInt(kSettingPageHeight).value_or(0));
- if (!ticket_opt || !capabilities_opt || out_page_size->IsEmpty()) {
- NOTREACHED();
- return false;
- }
- absl::optional<base::Value> ticket_value =
- base::JSONReader::Read(*ticket_opt);
- if (!ticket_value || !ticket_value->is_dict()) {
- return false;
- }
-
- *out_destination_id = *settings.FindString(kSettingDeviceName);
- *out_capabilities = *capabilities_opt;
- *out_ticket = std::move(*ticket_value).TakeDict();
- return true;
-}
-
} // namespace printing
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.h b/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.h
index c2d4d567248..089d225953d 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.h
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils.h
@@ -5,8 +5,6 @@
#ifndef CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_UTILS_H_
#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_UTILS_H_
-#include <string>
-
#include "base/values.h"
#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
#include "printing/backend/print_backend.h"
@@ -49,6 +47,14 @@ base::Value::Dict ValidateCddForPrintPreview(base::Value::Dict cdd);
// Assumes `cdd` is the output from ValidateCddForPrintPreview().
base::Value::Dict UpdateCddWithDpiIfMissing(base::Value::Dict cdd);
+// Returns the list of media size options from the `cdd`, or nullptr if it does
+// not exist. Returns a pointer into `cdd`.
+const base::Value::List* GetMediaSizeOptionsFromCdd(
+ const base::Value::Dict& cdd);
+
+// Updates `cdd` by removing all continuous feed media size options.
+void FilterContinuousFeedMediaSizes(base::Value::Dict& cdd);
+
// Starts a local print of `print_data` with print settings dictionary
// `job_settings`. Runs `callback` on failure or success.
void StartLocalPrint(base::Value::Dict job_settings,
@@ -56,14 +62,6 @@ void StartLocalPrint(base::Value::Dict job_settings,
content::WebContents* preview_web_contents,
PrinterHandler::PrintCallback callback);
-// Parses print job settings. Returns `true` on success.
-// This is used by extension printers.
-bool ParseSettings(const base::Value::Dict& settings,
- std::string* out_destination_id,
- std::string* out_capabilities,
- gfx::Size* out_page_size,
- base::Value::Dict* out_ticket);
-
} // namespace printing
#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_UTILS_H_
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils_unittest.cc
index 4e5e93be5e1..00c30ec8069 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_utils_unittest.cc
@@ -20,6 +20,10 @@ const char kDpi[] = "dpi";
const char kHorizontalDpi[] = "horizontal_dpi";
const char kId[] = "id";
const char kIsDefault[] = "is_default";
+const char kMediaHeight[] = "height_microns";
+const char kMediaIsContinuousFeed[] = "is_continuous_feed";
+const char kMediaSizeKey[] = "media_size";
+const char kMediaWidth[] = "width_microns";
const char kMediaSizes[] = "media_sizes";
const char kPagesPerSheet[] = "Pages per sheet";
const char kPaperType[] = "Paper Type";
@@ -232,6 +236,18 @@ bool GetDpiResetToDefault(base::Value::Dict cdd) {
return reset_to_default.value();
}
+// Returns a CDD with the media size options populated with `options`.
+base::Value::Dict CreateCddWithMediaOptions(base::Value::List options) {
+ base::Value::Dict media_size;
+ media_size.Set(kOptionKey, std::move(options));
+ base::Value::Dict printer;
+ printer.Set(kMediaSizeKey, std::move(media_size));
+ base::Value::Dict cdd;
+ cdd.Set(kPrinter, std::move(printer));
+
+ return cdd;
+}
+
} // namespace
using PrintPreviewUtilsTest = testing::Test;
@@ -495,4 +511,69 @@ TEST_F(PrintPreviewUtilsTest, ExistingValidDpiCapabilityDoesNotChange) {
ValidatePrinter(UpdateCddWithDpiIfMissing(std::move(cdd_out)), printer);
}
+TEST_F(PrintPreviewUtilsTest, FilterMediaSizesNoContinuousFeed) {
+ base::Value::Dict media_1;
+ media_1.Set(kMediaWidth, 100);
+ media_1.Set(kMediaHeight, 200);
+ base::Value::Dict media_2;
+ media_2.Set(kMediaWidth, 300);
+ media_2.Set(kMediaHeight, 400);
+ base::Value::List option_list;
+ option_list.Append(std::move(media_1));
+ option_list.Append(std::move(media_2));
+
+ base::Value::List expected_list = option_list.Clone();
+
+ base::Value::Dict cdd = CreateCddWithMediaOptions(std::move(option_list));
+
+ FilterContinuousFeedMediaSizes(cdd);
+
+ const base::Value::List* options = GetMediaSizeOptionsFromCdd(cdd);
+ ASSERT_TRUE(options);
+ EXPECT_EQ(expected_list, *options);
+}
+
+TEST_F(PrintPreviewUtilsTest, FilterMediaSizesWithContinuousFeed) {
+ base::Value::Dict media_1;
+ media_1.Set(kMediaWidth, 100);
+ media_1.Set(kMediaHeight, 200);
+ base::Value::Dict media_2;
+ media_2.Set(kMediaWidth, 300);
+ media_2.Set(kMediaIsContinuousFeed, true);
+ base::Value::List option_list;
+ option_list.Append(media_1.Clone());
+ option_list.Append(std::move(media_2));
+
+ base::Value::List expected_list;
+ expected_list.Append(std::move(media_1));
+
+ base::Value::Dict cdd = CreateCddWithMediaOptions(std::move(option_list));
+
+ FilterContinuousFeedMediaSizes(cdd);
+
+ const base::Value::List* options = GetMediaSizeOptionsFromCdd(cdd);
+ ASSERT_TRUE(options);
+ EXPECT_EQ(expected_list, *options);
+}
+
+TEST_F(PrintPreviewUtilsTest, FilterMediaSizesAllContinuousFeed) {
+ base::Value::Dict media_1;
+ media_1.Set(kMediaWidth, 100);
+ media_1.Set(kMediaIsContinuousFeed, true);
+ base::Value::Dict media_2;
+ media_2.Set(kMediaWidth, 300);
+ media_2.Set(kMediaIsContinuousFeed, true);
+ base::Value::List option_list;
+ option_list.Append(std::move(media_1));
+ option_list.Append(std::move(media_2));
+
+ base::Value::Dict cdd = CreateCddWithMediaOptions(std::move(option_list));
+
+ FilterContinuousFeedMediaSizes(cdd);
+
+ const base::Value::List* options = GetMediaSizeOptionsFromCdd(cdd);
+ ASSERT_TRUE(options);
+ EXPECT_TRUE(options->empty());
+}
+
} // namespace printing
diff --git a/chromium/chrome/browser/ui/webui/privacy_sandbox/DIR_METADATA b/chromium/chrome/browser/ui/webui/privacy_sandbox/DIR_METADATA
index 7d283fa6a30..a01852cff95 100644
--- a/chromium/chrome/browser/ui/webui/privacy_sandbox/DIR_METADATA
+++ b/chromium/chrome/browser/ui/webui/privacy_sandbox/DIR_METADATA
@@ -2,4 +2,4 @@ monorail: {
component: "UI>Settings>Privacy"
}
-team_email: "chrome-friendly-settings@google.com"
+team_email: "chrome-privacy-controls@google.com"
diff --git a/chromium/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.cc b/chromium/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.cc
index 09fd76df92d..02c030e1907 100644
--- a/chromium/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.cc
+++ b/chromium/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.cc
@@ -15,7 +15,6 @@
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
-#include "chrome/grit/google_chrome_strings.h"
#include "chrome/grit/privacy_sandbox_resources.h"
#include "chrome/grit/privacy_sandbox_resources_map.h"
#include "components/strings/grit/components_strings.h"
diff --git a/chromium/chrome/browser/ui/webui/profile_helper.cc b/chromium/chrome/browser/ui/webui/profile_helper.cc
index c032e627850..c3709b230a7 100644
--- a/chromium/chrome/browser/ui/webui/profile_helper.cc
+++ b/chromium/chrome/browser/ui/webui/profile_helper.cc
@@ -16,7 +16,7 @@
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/browser/ui/webui/signin/signin_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
diff --git a/chromium/chrome/browser/ui/webui/profile_internals/profile_internals_handler.cc b/chromium/chrome/browser/ui/webui/profile_internals/profile_internals_handler.cc
index 1a8de3b66d6..1260bb1366d 100644
--- a/chromium/chrome/browser/ui/webui/profile_internals/profile_internals_handler.cc
+++ b/chromium/chrome/browser/ui/webui/profile_internals/profile_internals_handler.cc
@@ -15,7 +15,7 @@
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "content/public/browser/web_ui.h"
#include "skia/ext/skia_utils_base.h"
@@ -121,7 +121,7 @@ base::Value::List ProfileInternalsHandler::GetProfilesList() {
std::vector<ProfileAttributesEntry*> entries =
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
- .GetAllProfilesAttributesSortedByLocalProfileName();
+ .GetAllProfilesAttributesSortedByLocalProfileNameWithCheck();
std::vector<Profile*> loaded_profiles =
g_browser_process->profile_manager()->GetLoadedProfiles();
base::flat_set<base::FilePath> loaded_profile_paths =
diff --git a/chromium/chrome/browser/ui/webui/realbox/realbox_handler.cc b/chromium/chrome/browser/ui/webui/realbox/realbox_handler.cc
index 29de5e9bc48..0149b744cd1 100644
--- a/chromium/chrome/browser/ui/webui/realbox/realbox_handler.cc
+++ b/chromium/chrome/browser/ui/webui/realbox/realbox_handler.cc
@@ -125,6 +125,8 @@ constexpr char kGoogleKeepNoteIconResourceName[] =
constexpr char kGoogleSitesIconResourceName[] =
"//resources/cr_components/omnibox/icons/sites.svg";
#endif
+constexpr char kHistoryIconResourceName[] =
+ "//resources/images/icon_history.svg";
constexpr char kIncognitoIconResourceName[] =
"//resources/cr_components/omnibox/icons/incognito.svg";
constexpr char kJourneysIconResourceName[] =
@@ -399,6 +401,7 @@ class RealboxOmniboxClient : public OmniboxClient {
override;
bool IsPasteAndGoEnabled() const override;
SessionID GetSessionID() const override;
+ PrefService* GetPrefs() override;
bookmarks::BookmarkModel* GetBookmarkModel() override;
AutocompleteControllerEmitter* GetAutocompleteControllerEmitter() override;
TemplateURLService* GetTemplateURLService() override;
@@ -459,6 +462,10 @@ SessionID RealboxOmniboxClient::GetSessionID() const {
return sessions::SessionTabHelper::IdForTab(web_contents_);
}
+PrefService* RealboxOmniboxClient::GetPrefs() {
+ return profile_->GetPrefs();
+}
+
bookmarks::BookmarkModel* RealboxOmniboxClient::GetBookmarkModel() {
return BookmarkModelFactory::GetForBrowserContext(profile_);
}
@@ -577,6 +584,9 @@ void RealboxHandler::SetupWebUIDataSource(content::WebUIDataSource* source,
base::FeatureList::IsEnabled(ntp_features::kNtpRealboxLensSearch) &&
profile->GetPrefs()->GetBoolean(prefs::kLensDesktopNTPSearchEnabled));
source->AddString("realboxLensVariations", GetBase64UrlVariations(profile));
+ source->AddBoolean(
+ "realboxLensDirectUpload",
+ base::FeatureList::IsEnabled(ntp_features::kNtpLensDirectUpload));
}
// static
@@ -589,9 +599,6 @@ void RealboxHandler::SetupDropdownWebUIDataSource(
{"hideSuggestions", IDS_TOOLTIP_HEADER_HIDE_SUGGESTIONS_BUTTON},
{"showSuggestions", IDS_TOOLTIP_HEADER_SHOW_SUGGESTIONS_BUTTON}};
source->AddLocalizedStrings(kStrings);
-
- source->AddBoolean("roundCorners", base::FeatureList::IsEnabled(
- ntp_features::kRealboxRoundedCorners));
}
// static
@@ -646,6 +653,9 @@ std::string RealboxHandler::AutocompleteMatchVectorIconToResourceName(
return kDriveVideoIconResourceName;
} else if (icon.name == omnibox::kExtensionAppIcon.name) {
return kExtensionAppIconResourceName;
+ } else if (icon.name == vector_icons::kHistoryIcon.name ||
+ icon.name == vector_icons::kHistoryChromeRefreshIcon.name) {
+ return kHistoryIconResourceName;
} else if (icon.name == omnibox::kJourneysIcon.name ||
icon.name == omnibox::kJourneysChromeRefreshIcon.name) {
return kJourneysIconResourceName;
@@ -758,20 +768,18 @@ RealboxHandler::RealboxHandler(
Profile* profile,
content::WebContents* web_contents,
MetricsReporter* metrics_reporter,
- bool is_omnibox_popup_handler)
+ OmniboxController* omnibox_controller)
: profile_(profile),
web_contents_(web_contents),
metrics_reporter_(metrics_reporter),
+ page_set_(false),
page_handler_(this, std::move(pending_page_handler)) {
// Keep a reference to the OmniboxController instance owned by the OmniboxView
// when the handler is being used in the context of the omnibox popup.
// Otherwise, create own instance of OmniboxController. Either way, observe
// the AutocompleteController instance owned by the OmniboxController.
- if (is_omnibox_popup_handler) {
- // TODO(crbug.com/1396174): This seems hacky. But currently there is no API
- // for getting the browser instance from the web contents in top chrome.
- Browser* active_browser = chrome::FindLastActive();
- controller_ = search::GetOmniboxView(active_browser)->controller();
+ if (omnibox_controller) {
+ controller_ = omnibox_controller;
} else {
owned_controller_ = std::make_unique<OmniboxController>(
/*view=*/nullptr,
@@ -788,9 +796,14 @@ RealboxHandler::~RealboxHandler() {
controller_ = nullptr;
}
+bool RealboxHandler::IsRemoteBound() const {
+ return page_set_;
+}
+
void RealboxHandler::SetPage(
mojo::PendingRemote<omnibox::mojom::Page> pending_page) {
page_.Bind(std::move(pending_page));
+ page_set_ = page_.is_bound();
}
void RealboxHandler::OnFocusChanged(bool focused) {
@@ -893,10 +906,8 @@ void RealboxHandler::ToggleSuggestionGroupIdVisibility(
const auto& group_id = omnibox::GroupIdForNumber(suggestion_group_id);
DCHECK_NE(omnibox::GROUP_INVALID, group_id);
const bool current_visibility =
- autocomplete_controller()->result().IsSuggestionGroupHidden(
- profile_->GetPrefs(), group_id);
- autocomplete_controller()->result().SetSuggestionGroupHidden(
- profile_->GetPrefs(), group_id, !current_visibility);
+ controller_->IsSuggestionGroupHidden(group_id);
+ controller_->SetSuggestionGroupHidden(group_id, !current_visibility);
}
void RealboxHandler::ExecuteAction(uint8_t line,
@@ -954,8 +965,30 @@ void RealboxHandler::OnResultChanged(AutocompleteController* controller,
}
}
-void RealboxHandler::SelectMatchAtLine(size_t old_line, size_t new_line) {
- page_->SelectMatchAtLine(new_line);
+omnibox::mojom::SelectionLineState ConvertLineState(
+ OmniboxPopupSelection::LineState state) {
+ switch (state) {
+ case OmniboxPopupSelection::LineState::FOCUSED_BUTTON_HEADER:
+ return omnibox::mojom::SelectionLineState::kFocusedButtonHeader;
+ case OmniboxPopupSelection::LineState::NORMAL:
+ return omnibox::mojom::SelectionLineState::kNormal;
+ case OmniboxPopupSelection::LineState::KEYWORD_MODE:
+ return omnibox::mojom::SelectionLineState::kKeywordMode;
+ case OmniboxPopupSelection::LineState::FOCUSED_BUTTON_ACTION:
+ return omnibox::mojom::SelectionLineState::kFocusedButtonAction;
+ case OmniboxPopupSelection::LineState::FOCUSED_BUTTON_REMOVE_SUGGESTION:
+ return omnibox::mojom::SelectionLineState::kFocusedButtonRemoveSuggestion;
+ default:
+ NOTREACHED();
+ break;
+ }
+ return omnibox::mojom::SelectionLineState::kNormal;
+}
+
+void RealboxHandler::UpdateSelection(OmniboxPopupSelection selection) {
+ page_->UpdateSelection(omnibox::mojom::OmniboxPopupSelection::New(
+ selection.line, ConvertLineState(selection.state),
+ selection.action_index));
}
// LocationBarModel:
diff --git a/chromium/chrome/browser/ui/webui/realbox/realbox_handler.h b/chromium/chrome/browser/ui/webui/realbox/realbox_handler.h
index 3fa874829a4..61c62282046 100644
--- a/chromium/chrome/browser/ui/webui/realbox/realbox_handler.h
+++ b/chromium/chrome/browser/ui/webui/realbox/realbox_handler.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_UI_WEBUI_REALBOX_REALBOX_HANDLER_H_
#define CHROME_BROWSER_UI_WEBUI_REALBOX_REALBOX_HANDLER_H_
+#include <atomic>
#include <memory>
#include "base/memory/raw_ptr.h"
@@ -13,6 +14,7 @@
#include "components/omnibox/browser/autocomplete_controller.h"
#include "components/omnibox/browser/location_bar_model.h"
#include "components/omnibox/browser/omnibox.mojom.h"
+#include "components/omnibox/browser/omnibox_popup_selection.h"
#include "components/url_formatter/spoof_checks/idna_metrics.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
@@ -52,18 +54,23 @@ class RealboxHandler : public omnibox::mojom::PageHandler,
const gfx::VectorIcon& icon);
static std::string PedalVectorIconToResourceName(const gfx::VectorIcon& icon);
+ // Note: `omnibox_controller` may be null for the Realbox, in which case
+ // an internally owned controller is created and used.
RealboxHandler(
mojo::PendingReceiver<omnibox::mojom::PageHandler> pending_page_handler,
Profile* profile,
content::WebContents* web_contents,
MetricsReporter* metrics_reporter,
- bool is_omnibox_popup_handler);
+ OmniboxController* omnibox_controller);
RealboxHandler(const RealboxHandler&) = delete;
RealboxHandler& operator=(const RealboxHandler&) = delete;
~RealboxHandler() override;
+ // Returns true if the page remote is bound and ready to receive calls.
+ bool IsRemoteBound() const;
+
// omnibox::mojom::PageHandler:
void SetPage(mojo::PendingRemote<omnibox::mojom::Page> pending_page) override;
void OnFocusChanged(bool focused) override;
@@ -98,7 +105,7 @@ class RealboxHandler : public omnibox::mojom::PageHandler,
void OnResultChanged(AutocompleteController* controller,
bool default_match_changed) override;
- void SelectMatchAtLine(size_t old_line, size_t new_line);
+ void UpdateSelection(OmniboxPopupSelection selection);
// LocationBarModel:
std::u16string GetFormattedFullURL() const override;
@@ -131,6 +138,8 @@ class RealboxHandler : public omnibox::mojom::PageHandler,
AutocompleteController::Observer>
autocomplete_controller_observation_{this};
+ // Since mojo::Remote is not thread-safe, use an atomic to signal readiness.
+ std::atomic<bool> page_set_;
mojo::Remote<omnibox::mojom::Page> page_;
mojo::Receiver<omnibox::mojom::PageHandler> page_handler_;
diff --git a/chromium/chrome/browser/ui/webui/realbox/realbox_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/realbox/realbox_handler_browsertest.cc
index a0e56890076..de268daf5fb 100644
--- a/chromium/chrome/browser/ui/webui/realbox/realbox_handler_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/realbox/realbox_handler_browsertest.cc
@@ -173,7 +173,8 @@ class RealboxSearchBrowserTestPage : public omnibox::mojom::Page {
// omnibox::mojom::Page
void AutocompleteResultChanged(
omnibox::mojom::AutocompleteResultPtr result) override {}
- void SelectMatchAtLine(uint8_t line) override {}
+ void UpdateSelection(
+ omnibox::mojom::OmniboxPopupSelectionPtr selection) override {}
mojo::PendingRemote<omnibox::mojom::Page> GetRemotePage() {
return receiver_.BindNewPipeAndPassRemote();
}
@@ -189,7 +190,7 @@ IN_PROC_BROWSER_TEST_F(RealboxSearchPreloadBrowserTest, SearchPreloadSuccess) {
RealboxHandler realbox_handler = RealboxHandler(
remote_page_handler.BindNewPipeAndPassReceiver(), browser()->profile(),
GetWebContents(), /*metrics_reporter=*/nullptr,
- /*is_omnibox_popup_handler=*/false);
+ /*omnibox_controller=*/nullptr);
realbox_handler.SetPage(page.GetRemotePage());
content::test::PrerenderHostRegistryObserver registry_observer(
*GetWebContents());
@@ -198,8 +199,7 @@ IN_PROC_BROWSER_TEST_F(RealboxSearchPreloadBrowserTest, SearchPreloadSuccess) {
std::string search_terms = "prerender";
AddNewSuggestionRule(input_query, {search_terms}, /*prefetch_index=*/0,
/*prerender_index=*/0);
- GURL prerender_url = GetSearchServerQueryURL(search_terms + "&pf=cs&");
-
+ auto [_, prerender_url] = GetSearchPrefetchAndNonPrefetch(search_terms);
// Fake a WebUI input.
remote_page_handler->QueryAutocomplete(base::ASCIIToUTF16(input_query),
/*prevent_inline_autocomplete=*/false);
diff --git a/chromium/chrome/browser/ui/webui/realbox/realbox_handler_unittest.cc b/chromium/chrome/browser/ui/webui/realbox/realbox_handler_unittest.cc
index e263178fb57..dc5888dac45 100644
--- a/chromium/chrome/browser/ui/webui/realbox/realbox_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/realbox/realbox_handler_unittest.cc
@@ -11,8 +11,32 @@
#include "components/variations/variations_ids_provider.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_web_ui_data_source.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace {
+class MockPage : public omnibox::mojom::Page {
+ public:
+ MockPage() = default;
+ ~MockPage() override = default;
+
+ mojo::PendingRemote<omnibox::mojom::Page> BindAndGetRemote() {
+ DCHECK(!receiver_.is_bound());
+ return receiver_.BindNewPipeAndPassRemote();
+ }
+ mojo::Receiver<omnibox::mojom::Page> receiver_{this};
+
+ void FlushForTesting() { receiver_.FlushForTesting(); }
+
+ MOCK_METHOD(void,
+ AutocompleteResultChanged,
+ (omnibox::mojom::AutocompleteResultPtr));
+ MOCK_METHOD(void,
+ UpdateSelection,
+ (omnibox::mojom::OmniboxPopupSelectionPtr));
+};
+} // namespace
+
class RealboxHandlerTest : public ::testing::Test {
public:
RealboxHandlerTest() = default;
@@ -24,6 +48,10 @@ class RealboxHandlerTest : public ::testing::Test {
content::TestWebUIDataSource* source() { return source_.get(); }
TestingProfile* profile() { return profile_.get(); }
+ protected:
+ std::unique_ptr<RealboxHandler> handler_;
+ testing::NiceMock<MockPage> page_;
+
private:
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<content::TestWebUIDataSource> source_;
@@ -41,7 +69,15 @@ class RealboxHandlerTest : public ::testing::Test {
variations::VariationsIdsProvider::ForceIdsResult::SUCCESS,
variations::VariationsIdsProvider::GetInstance()->ForceVariationIds(
/*variation_ids=*/{"100"}, /*command_line_variation_ids=*/""));
+
+ handler_ = std::make_unique<RealboxHandler>(
+ mojo::PendingReceiver<omnibox::mojom::PageHandler>(), profile(),
+ /*web_contents=*/nullptr, /*metrics_reporter=*/nullptr,
+ /*omnibox_controller=*/nullptr);
+ handler_->SetPage(page_.BindAndGetRemote());
}
+
+ void TearDown() override { handler_.reset(); }
};
TEST_F(RealboxHandlerTest, RealboxLensSearchIsFalseWhenDisabled) {
@@ -77,3 +113,40 @@ TEST_F(RealboxHandlerTest, RealboxLensVariationsContainsVariations) {
EXPECT_EQ("CGQ", *source()->GetLocalizedStrings()->FindString(
"realboxLensVariations"));
}
+
+TEST_F(RealboxHandlerTest, RealboxUpdatesSelection) {
+ omnibox::mojom::OmniboxPopupSelectionPtr selection;
+ EXPECT_CALL(page_, UpdateSelection)
+ .Times(4)
+ .WillRepeatedly(testing::Invoke(
+ [&selection](omnibox::mojom::OmniboxPopupSelectionPtr arg) {
+ selection = std::move(arg);
+ }));
+
+ handler_->UpdateSelection(
+ OmniboxPopupSelection(0, OmniboxPopupSelection::NORMAL));
+ page_.FlushForTesting();
+ EXPECT_EQ(0, selection->line);
+ EXPECT_EQ(omnibox::mojom::SelectionLineState::kNormal, selection->state);
+
+ handler_->UpdateSelection(
+ OmniboxPopupSelection(1, OmniboxPopupSelection::KEYWORD_MODE));
+ page_.FlushForTesting();
+ EXPECT_EQ(1, selection->line);
+ EXPECT_EQ(omnibox::mojom::SelectionLineState::kKeywordMode, selection->state);
+
+ handler_->UpdateSelection(OmniboxPopupSelection(
+ 2, OmniboxPopupSelection::FOCUSED_BUTTON_ACTION, 4));
+ page_.FlushForTesting();
+ EXPECT_EQ(2, selection->line);
+ EXPECT_EQ(4, selection->action_index);
+ EXPECT_EQ(omnibox::mojom::SelectionLineState::kFocusedButtonAction,
+ selection->state);
+
+ handler_->UpdateSelection(OmniboxPopupSelection(
+ 3, OmniboxPopupSelection::FOCUSED_BUTTON_REMOVE_SUGGESTION));
+ page_.FlushForTesting();
+ EXPECT_EQ(3, selection->line);
+ EXPECT_EQ(omnibox::mojom::SelectionLineState::kFocusedButtonRemoveSuggestion,
+ selection->state);
+}
diff --git a/chromium/chrome/browser/ui/webui/sandbox/sandbox_handler.cc b/chromium/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
index 0f872ef7ff4..5d2ab8f23c4 100644
--- a/chromium/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
+++ b/chromium/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
@@ -86,11 +86,13 @@ base::Value::List FetchSandboxFeatures() {
features.Append(
FeatureToValue(sandbox::policy::features::kRendererAppContainer));
features.Append(
- FeatureToValue(sandbox::policy::features::kWinSboxAllowSystemFonts));
- features.Append(
FeatureToValue(sandbox::policy::features::kWinSboxRendererCloseKsecDD));
features.Append(FeatureToValue(
sandbox::policy::features::kWinSboxDisableExtensionPoints));
+ features.Append(
+ FeatureToValue(sandbox::policy::features::kWinSboxWarmupProcessPrng));
+ features.Append(
+ FeatureToValue(sandbox::policy::features::kWinSboxZeroAppShim));
return features;
}
diff --git a/chromium/chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc b/chromium/chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc
index 4993a173e72..0be9a6e6574 100644
--- a/chromium/chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc
@@ -15,6 +15,7 @@
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
#if BUILDFLAG(IS_WIN)
#include "chrome/browser/ui/webui/sandbox/sandbox_handler.h"
@@ -73,6 +74,13 @@ void CreateAndAddDataSource(Profile* profile) {
kSandboxInternalsResourcesSize));
source->SetDefaultResource(IDR_SANDBOX_INTERNALS_SANDBOX_INTERNALS_HTML);
+ source->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::TrustedTypes,
+ "trusted-types static-types;");
+ source->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ScriptSrc,
+ "script-src chrome://resources chrome://webui-test 'self';");
+
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
SetSandboxStatusData(source);
source->UseStringsJs();
diff --git a/chromium/chrome/browser/ui/webui/waffle/BUILD.gn b/chromium/chrome/browser/ui/webui/search_engine_choice/BUILD.gn
index 02f8f1e562e..7b5fadbae7b 100644
--- a/chromium/chrome/browser/ui/webui/waffle/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/search_engine_choice/BUILD.gn
@@ -5,10 +5,10 @@
import("//components/signin/features.gni")
import("//mojo/public/tools/bindings/mojom.gni")
-assert(enable_waffle_desktop)
+assert(enable_search_engine_choice)
mojom("mojo_bindings") {
- sources = [ "waffle.mojom" ]
+ sources = [ "search_engine_choice.mojom" ]
webui_module_path = "/"
use_typescript_sources = true
}
diff --git a/chromium/chrome/browser/ui/webui/search_engine_choice/DIR_METADATA b/chromium/chrome/browser/ui/webui/search_engine_choice/DIR_METADATA
new file mode 100644
index 00000000000..c8383f40dfb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/search_engine_choice/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chrome/browser/search_engine_choice/COMMON_METADATA"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/OWNERS b/chromium/chrome/browser/ui/webui/search_engine_choice/OWNERS
index 2acde077ff6..25d7aa0c3d2 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/OWNERS
+++ b/chromium/chrome/browser/ui/webui/search_engine_choice/OWNERS
@@ -1,5 +1,3 @@
-khorimoto@chromium.org
-zentaro@chromium.org
-
+file://chrome/browser/search_engine_choice/OWNERS
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom
new file mode 100644
index 00000000000..4b38609390c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom
@@ -0,0 +1,25 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module search_engine_choice.mojom;
+
+// Lives in the browser process. A renderer uses this to link itself with
+// a page handler.
+interface PageHandlerFactory {
+ // Creates a page handler for the file manager and link it to the UI.
+ CreatePageHandler(pending_receiver<PageHandler> handler);
+};
+
+// Browser-side handler for requests from WebUI page.
+interface PageHandler {
+ // Displays the Search Engine Choice dialog using the height of its content.
+ // Triggered by the `displayDialog()` call in
+ // `chrome/browser/resources/search_engine_choice/app.ts`
+ DisplayDialog(uint32 content_height);
+
+ // Changes the search engine based on the user's choice.
+ // Triggered by the `handleSearchEngineChoiceSelected()` call in
+ // `chrome/browser/resources/search_engine_choice/app.ts`
+ HandleSearchEngineChoiceSelected(int32 prepopulate_id);
+};
diff --git a/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.cc b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.cc
new file mode 100644
index 00000000000..5fae5f72363
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.cc
@@ -0,0 +1,34 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.h"
+
+#include "components/signin/public/base/signin_switches.h"
+
+SearchEngineChoiceHandler::SearchEngineChoiceHandler(
+ mojo::PendingReceiver<search_engine_choice::mojom::PageHandler> receiver,
+ base::OnceCallback<void(int)> display_dialog_callback,
+ base::OnceCallback<void(int)> handle_choice_selected_callback)
+ : receiver_(this, std::move(receiver)),
+ display_dialog_callback_(std::move(display_dialog_callback)),
+ handle_choice_selected_callback_(
+ std::move(handle_choice_selected_callback)) {
+ CHECK(base::FeatureList::IsEnabled(switches::kSearchEngineChoice));
+ CHECK(handle_choice_selected_callback_);
+}
+
+SearchEngineChoiceHandler::~SearchEngineChoiceHandler() = default;
+
+void SearchEngineChoiceHandler::DisplayDialog(uint32_t content_height) {
+ if (display_dialog_callback_) {
+ std::move(display_dialog_callback_).Run(content_height);
+ }
+}
+
+void SearchEngineChoiceHandler::HandleSearchEngineChoiceSelected(
+ int prepopulate_id) {
+ if (handle_choice_selected_callback_) {
+ std::move(handle_choice_selected_callback_).Run(prepopulate_id);
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.h b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.h
new file mode 100644
index 00000000000..a88676bd617
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.h
@@ -0,0 +1,36 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_HANDLER_H_
+
+#include "base/functional/callback_forward.h"
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+class SearchEngineChoiceHandler
+ : public search_engine_choice::mojom::PageHandler {
+ public:
+ explicit SearchEngineChoiceHandler(
+ mojo::PendingReceiver<search_engine_choice::mojom::PageHandler> receiver,
+ base::OnceCallback<void(int)> display_dialog_callback,
+ base::OnceCallback<void(int)> handle_choice_selected_callback);
+
+ SearchEngineChoiceHandler(const SearchEngineChoiceHandler&) = delete;
+ SearchEngineChoiceHandler& operator=(const SearchEngineChoiceHandler&) =
+ delete;
+
+ ~SearchEngineChoiceHandler() override;
+
+ // search_engine_choice::mojom::PageHandler:
+ void DisplayDialog(uint32_t content_height) override;
+ void HandleSearchEngineChoiceSelected(int32_t prepopulate_id) override;
+
+ private:
+ mojo::Receiver<search_engine_choice::mojom::PageHandler> receiver_;
+ base::OnceCallback<void(int)> display_dialog_callback_;
+ base::OnceCallback<void(int)> handle_choice_selected_callback_;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc
new file mode 100644
index 00000000000..f8cd208060f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.cc
@@ -0,0 +1,136 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h"
+
+#include "base/check_deref.h"
+#include "base/feature_list.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_forward.h"
+#include "base/json/json_writer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engine_choice/search_engine_choice_service.h"
+#include "chrome/browser/search_engine_choice/search_engine_choice_service_factory.h"
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/chrome_unscaled_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/search_engine_choice_resources.h"
+#include "chrome/grit/search_engine_choice_resources_map.h"
+#include "chrome/grit/signin_resources.h"
+#include "components/search_engines/template_url_data.h"
+#include "components/search_engines/template_url_prepopulate_data.h"
+#include "components/signin/public/base/signin_switches.h"
+#include "components/strings/grit/components_chromium_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+std::string GetChoiceListJSON(Profile& profile) {
+ base::Value::List choice_value_list;
+ SearchEngineChoiceService* search_engine_choice_service =
+ SearchEngineChoiceServiceFactory::GetForProfile(&profile);
+ const std::vector<std::unique_ptr<TemplateURLData>> choices =
+ search_engine_choice_service->GetSearchEngines();
+
+ for (const auto& choice : choices) {
+ base::Value::Dict choice_value;
+ choice_value.Set("prepopulate_id", choice->prepopulate_id);
+ choice_value.Set("name", choice->short_name());
+ choice_value_list.Append(std::move(choice_value));
+ }
+
+ std::string json_choice_list;
+ base::JSONWriter::Write(choice_value_list, &json_choice_list);
+ return json_choice_list;
+}
+} // namespace
+
+SearchEngineChoiceUI::SearchEngineChoiceUI(content::WebUI* web_ui)
+ : ui::MojoWebUIController(web_ui, true),
+ profile_(CHECK_DEREF(Profile::FromWebUI(web_ui))) {
+ CHECK(base::FeatureList::IsEnabled(switches::kSearchEngineChoice));
+
+ content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+ web_ui->GetWebContents()->GetBrowserContext(),
+ chrome::kChromeUISearchEngineChoiceHost);
+
+ source->AddLocalizedString("title", IDS_SEARCH_ENGINE_CHOICE_PAGE_TITLE);
+ source->AddLocalizedString("subtitle",
+ IDS_SEARCH_ENGINE_CHOICE_PAGE_SUBTITLE);
+ source->AddLocalizedString("subtitleInfoLink",
+ IDS_SEARCH_ENGINE_CHOICE_PAGE_SUBTITLE_INFO_LINK);
+ source->AddLocalizedString("buttonText",
+ IDS_SEARCH_ENGINE_CHOICE_BUTTON_TITLE);
+ source->AddLocalizedString("infoDialogTitle",
+ IDS_SEARCH_ENGINE_CHOICE_INFO_DIALOG_TITLE);
+ source->AddLocalizedString(
+ "infoDialogFirstParagraph",
+ IDS_SEARCH_ENGINE_CHOICE_INFO_DIALOG_BODY_FIRST_PARAGRAPH);
+ source->AddLocalizedString(
+ "infoDialogSecondParagraph",
+ IDS_SEARCH_ENGINE_CHOICE_INFO_DIALOG_BODY_SECOND_PARAGRAPH);
+ source->AddLocalizedString(
+ "infoDialogThirdParagraph",
+ IDS_SEARCH_ENGINE_CHOICE_INFO_DIALOG_BODY_THIRD_PARAGRAPH);
+ source->AddLocalizedString("infoDialogButtonText",
+ IDS_SEARCH_ENGINE_CHOICE_INFO_DIALOG_BUTTON_TITLE);
+ source->AddLocalizedString("productLogoAltText",
+ IDS_SHORT_PRODUCT_LOGO_ALT_TEXT);
+
+ source->AddResourcePath("images/left_illustration.svg",
+ IDR_SIGNIN_IMAGES_SHARED_LEFT_BANNER_SVG);
+ source->AddResourcePath("images/left_illustration_dark.svg",
+ IDR_SIGNIN_IMAGES_SHARED_LEFT_BANNER_DARK_SVG);
+ source->AddResourcePath("images/right_illustration.svg",
+ IDR_SIGNIN_IMAGES_SHARED_RIGHT_BANNER_SVG);
+ source->AddResourcePath("images/right_illustration_dark.svg",
+ IDR_SIGNIN_IMAGES_SHARED_RIGHT_BANNER_DARK_SVG);
+ source->AddResourcePath("images/product-logo.svg", IDR_PRODUCT_LOGO_SVG);
+ source->AddResourcePath("tangible_sync_style_shared.css.js",
+ IDR_SIGNIN_TANGIBLE_SYNC_STYLE_SHARED_CSS_JS);
+ source->AddResourcePath("signin_vars.css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS);
+
+ source->AddString("choiceList", GetChoiceListJSON(profile_.get()));
+
+ webui::SetupChromeRefresh2023(source);
+
+ webui::SetupWebUIDataSource(
+ source,
+ base::make_span(kSearchEngineChoiceResources,
+ kSearchEngineChoiceResourcesSize),
+ IDR_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_HTML);
+}
+
+WEB_UI_CONTROLLER_TYPE_IMPL(SearchEngineChoiceUI)
+
+SearchEngineChoiceUI::~SearchEngineChoiceUI() = default;
+
+void SearchEngineChoiceUI::BindInterface(
+ mojo::PendingReceiver<search_engine_choice::mojom::PageHandlerFactory>
+ receiver) {
+ page_factory_receiver_.reset();
+ page_factory_receiver_.Bind(std::move(receiver));
+}
+
+void SearchEngineChoiceUI::Initialize(
+ base::OnceCallback<void(int)> display_dialog_callback) {
+ CHECK(display_dialog_callback);
+ display_dialog_callback_ = std::move(display_dialog_callback);
+}
+
+void SearchEngineChoiceUI::HandleSearchEngineChoiceMade(int prepopulate_id) {
+ SearchEngineChoiceService* search_engine_choice_service =
+ SearchEngineChoiceServiceFactory::GetForProfile(&profile_.get());
+ search_engine_choice_service->NotifyChoiceMade(prepopulate_id);
+}
+
+void SearchEngineChoiceUI::CreatePageHandler(
+ mojo::PendingReceiver<search_engine_choice::mojom::PageHandler> receiver) {
+ page_handler_ = std::make_unique<SearchEngineChoiceHandler>(
+ std::move(receiver), std::move(display_dialog_callback_),
+ base::BindOnce(&SearchEngineChoiceUI::HandleSearchEngineChoiceMade,
+ weak_ptr_factory_.GetWeakPtr()));
+}
diff --git a/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h
new file mode 100644
index 00000000000..7a23bf9bffe
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui.h
@@ -0,0 +1,60 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_UI_H_
+
+#include "base/functional/callback_forward.h"
+#include "base/memory/raw_ref.h"
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice.mojom.h"
+#include "chrome/browser/ui/webui/search_engine_choice/search_engine_choice_handler.h"
+#include "ui/webui/mojo_web_ui_controller.h"
+
+class Profile;
+
+// The WebUI controller for `chrome://search-engine-choice`.
+class SearchEngineChoiceUI
+ : public ui::MojoWebUIController,
+ public search_engine_choice::mojom::PageHandlerFactory {
+ public:
+ explicit SearchEngineChoiceUI(content::WebUI* web_ui);
+
+ SearchEngineChoiceUI(const SearchEngineChoiceUI&) = delete;
+ SearchEngineChoiceUI& operator=(const SearchEngineChoiceUI&) = delete;
+
+ ~SearchEngineChoiceUI() override;
+
+ // Instantiates the implementor of the mojom::PageHandlerFactory mojo
+ // interface passing the pending receiver that will be internally bound.
+ void BindInterface(
+ mojo::PendingReceiver<search_engine_choice::mojom::PageHandlerFactory>
+ receiver);
+
+ // Initializes the callbacks that need to be passed to the handler.
+ // `display_dialog_callback` is how we display the search engine choice
+ // dialog. It will be called when the page's static content is rendered.
+ void Initialize(base::OnceCallback<void(int)> display_dialog_callback);
+
+ private:
+ // search_engine_choice::mojom::PageHandlerFactory:
+ void CreatePageHandler(
+ mojo::PendingReceiver<search_engine_choice::mojom::PageHandler> receiver)
+ override;
+
+ // Notifies the search engine choice service that a choice has been made.
+ void HandleSearchEngineChoiceMade(int prepopulate_id);
+
+ std::unique_ptr<SearchEngineChoiceHandler> page_handler_;
+
+ mojo::Receiver<search_engine_choice::mojom::PageHandlerFactory>
+ page_factory_receiver_{this};
+
+ base::OnceCallback<void(int)> display_dialog_callback_;
+ const raw_ref<Profile> profile_;
+ base::WeakPtrFactory<SearchEngineChoiceUI> weak_ptr_factory_{this};
+
+ WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SEARCH_ENGINE_CHOICE_SEARCH_ENGINE_CHOICE_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui_browsertest.cc
new file mode 100644
index 00000000000..f101eb75174
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/search_engine_choice/search_engine_choice_ui_browsertest.cc
@@ -0,0 +1,164 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engine_choice/search_engine_choice_service.h"
+#include "chrome/browser/search_engine_choice/search_engine_choice_service_factory.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/search_engine_choice/search_engine_choice_tab_helper.h"
+#include "chrome/browser/ui/test/pixel_test_configuration_mixin.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/search_engines/template_url_data.h"
+#include "components/search_engines/template_url_prepopulate_data.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/signin/public/base/signin_buildflags.h"
+#include "components/signin/public/base/signin_switches.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "ui/views/widget/any_widget_observer.h"
+
+#if !BUILDFLAG(ENABLE_SEARCH_ENGINE_CHOICE)
+#error Platform not supported
+#endif
+
+// Tests for the chrome://search-engine-choice WebUI page.
+namespace {
+// Class that mocks `SearchEngineChoiceService`.
+class MockSearchEngineChoiceService : public SearchEngineChoiceService {
+ public:
+ explicit MockSearchEngineChoiceService(Profile* profile)
+ : SearchEngineChoiceService(
+ *profile,
+ *TemplateURLServiceFactory::GetForProfile(profile)) {
+ ON_CALL(*this, GetSearchEngines).WillByDefault([]() {
+ std::vector<std::unique_ptr<TemplateURLData>> choices;
+ auto choice = TemplateURLData();
+
+ // TODO(b/280753754): Update this to 12 search engines when the UI is
+ // ready to handle more than 5.
+ for (int i = 0; i < 5; i++) {
+ const std::u16string kShortName = u"Test" + base::NumberToString16(i);
+ choice.SetShortName(kShortName);
+ choices.push_back(std::make_unique<TemplateURLData>(choice));
+ }
+ return choices;
+ });
+ }
+ ~MockSearchEngineChoiceService() override = default;
+
+ static std::unique_ptr<KeyedService> Create(
+ content::BrowserContext* context) {
+ return std::make_unique<testing::NiceMock<MockSearchEngineChoiceService>>(
+ Profile::FromBrowserContext(context));
+ }
+
+ MOCK_METHOD(std::vector<std::unique_ptr<TemplateURLData>>,
+ GetSearchEngines,
+ (),
+ (override));
+};
+
+struct TestParam {
+ std::string test_suffix;
+ bool use_dark_theme = false;
+ bool use_right_to_left_language = false;
+ bool use_first_small_size_variant = false;
+};
+
+// To be passed as 4th argument to `INSTANTIATE_TEST_SUITE_P()`, allows the test
+// to be named like `<TestClassName>.InvokeUi_default/<TestSuffix>` instead
+// of using the index of the param in `TestParam` as suffix.
+std::string ParamToTestSuffix(const ::testing::TestParamInfo<TestParam>& info) {
+ return info.param.test_suffix;
+}
+
+// Permutations of supported parameters.
+const TestParam kTestParams[] = {
+ {.test_suffix = "Default"},
+ {.test_suffix = "DarkTheme", .use_dark_theme = true},
+ {.test_suffix = "RightToLeft", .use_right_to_left_language = true},
+ {.test_suffix = "FirstSmallSizeVariant",
+ .use_first_small_size_variant = true},
+};
+} // namespace
+
+class SearchEngineChoiceUIPixelTest
+ : public TestBrowserDialog,
+ public MixinBasedInProcessBrowserTest,
+ public testing::WithParamInterface<TestParam> {
+ public:
+ SearchEngineChoiceUIPixelTest()
+ : scoped_chrome_build_override_(SearchEngineChoiceServiceFactory::
+ ScopedChromeBuildOverrideForTesting(
+ /*force_chrome_build=*/true)),
+ pixel_test_mixin_(&mixin_host_,
+ GetParam().use_dark_theme,
+ GetParam().use_right_to_left_language) {}
+
+ ~SearchEngineChoiceUIPixelTest() override = default;
+
+ void SetUpInProcessBrowserTestFixture() override {
+ InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
+ create_services_subscription_ =
+ BrowserContextDependencyManager::GetInstance()
+ ->RegisterCreateServicesCallbackForTesting(
+ base::BindRepeating([](content::BrowserContext* context) {
+ SearchEngineChoiceServiceFactory::GetInstance()
+ ->SetTestingFactoryAndUse(
+ context, base::BindRepeating(
+ &MockSearchEngineChoiceService::Create));
+ }));
+ }
+
+ // TestBrowserDialog
+ void ShowUi(const std::string& name) override {
+ SearchEngineChoiceService::SetDialogDisabledForTests(
+ /*dialog_disabled=*/false);
+
+ GURL url = GURL(chrome::kChromeUISearchEngineChoiceURL);
+ content::TestNavigationObserver observer(url);
+ observer.StartWatchingNewWebContents();
+
+ views::NamedWidgetShownWaiter widget_waiter(
+ views::test::AnyWidgetTestPasskey{}, "SearchEngineChoiceDialogView");
+
+ // Make the default size smaller so that the dialog can fit in the test
+ // window.
+ int dialog_width = 930;
+ int dialog_height = 580;
+
+ if (GetParam().use_first_small_size_variant) {
+ dialog_width = 900;
+ }
+ ShowSearchEngineChoiceDialog(*browser(),
+ gfx::Size(dialog_width, dialog_height));
+ widget_waiter.WaitIfNeededAndGet();
+ observer.Wait();
+ }
+
+ private:
+ base::AutoReset<bool> scoped_chrome_build_override_;
+ base::test::ScopedFeatureList feature_list_{switches::kSearchEngineChoice};
+ PixelTestConfigurationMixin pixel_test_mixin_;
+ base::CallbackListSubscription create_services_subscription_;
+};
+
+IN_PROC_BROWSER_TEST_P(SearchEngineChoiceUIPixelTest, InvokeUi_default) {
+ ShowAndVerifyUi();
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+ SearchEngineChoiceUIPixelTest,
+ testing::ValuesIn(kTestParams),
+ &ParamToTestSuffix);
diff --git a/chromium/chrome/browser/ui/webui/settings/about_handler.cc b/chromium/chrome/browser/ui/webui/settings/about_handler.cc
index 76ca56df5bc..5da363c3f78 100644
--- a/chromium/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/about_handler.cc
@@ -27,7 +27,7 @@
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_content_browser_client.h"
-#include "chrome/browser/policy/management_utils.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
@@ -40,6 +40,7 @@
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/google/core/common/google_util.h"
+#include "components/policy/core/common/management/management_service.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/policy_constants.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
@@ -77,6 +78,7 @@
#include "chromeos/version/version_loader.h"
#include "components/user_manager/user_manager.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
#include "ui/chromeos/devicetype_utils.h"
#endif
@@ -119,7 +121,7 @@ std::u16string GetAllowedConnectionTypesMessage() {
// Returns true if current user can change channel, false otherwise.
bool CanChangeChannel(Profile* profile) {
- if (policy::IsDeviceEnterpriseManaged()) {
+ if (policy::ManagementServiceFactory::GetForPlatform()->IsManaged()) {
bool value = false;
// On a managed machine we delegate this setting to the affiliated users
// only if the policy value is true.
@@ -552,8 +554,15 @@ void AboutHandler::OnGetVersionInfoReady(std::string callback_id,
void AboutHandler::HandleGetFirmwareUpdateCount(const base::Value::List& args) {
CHECK_EQ(1U, args.size());
const std::string& callback_id = args[0].GetString();
+ size_t update_count = 0u;
+ if (!ash::FirmwareUpdateManager::IsInitialized()) {
+ ResolveJavascriptCallback(base::Value(callback_id),
+ base::Value(static_cast<int>(update_count)));
+ return;
+ }
+
auto* firmware_update_manager = ash::FirmwareUpdateManager::Get();
- size_t update_count = firmware_update_manager->GetUpdateCount();
+ update_count = firmware_update_manager->GetUpdateCount();
DCHECK_LT(update_count, std::numeric_limits<size_t>::max());
ResolveJavascriptCallback(base::Value(callback_id),
base::Value(static_cast<int>(update_count)));
diff --git a/chromium/chrome/browser/ui/webui/settings/about_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/about_handler_unittest.cc
index 7bbed9c0ffc..7f74d757183 100644
--- a/chromium/chrome/browser/ui/webui/settings/about_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/about_handler_unittest.cc
@@ -100,7 +100,7 @@ class AboutHandlerTest : public testing::Test {
TestingProfile profile_;
content::TestWebUI web_ui_;
std::unique_ptr<TestAboutHandler> handler_;
- raw_ptr<ash::FakeUpdateEngineClient, ExperimentalAsh>
+ raw_ptr<ash::FakeUpdateEngineClient, DanglingUntriaged | ExperimentalAsh>
fake_update_engine_client_;
std::unique_ptr<base::SimpleTestClock> clock_;
};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/DEPS b/chromium/chrome/browser/ui/webui/settings/ash/DEPS
index a4fb8ca3f21..e0fe22cee25 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/DEPS
+++ b/chromium/chrome/browser/ui/webui/settings/ash/DEPS
@@ -1,7 +1,8 @@
include_rules = [
+ "+ash/color_enhancement/color_enhancement_controller.h", # To use enum in accessibility_section.
"+ash/quick_pair",
- '+ash/shell.h', # To use KeyboardCapability API in device_secion.
- "+ash/system/power", # To use AdaptiveChargingController API in device_secion
+ "+ash/shell.h", # To use KeyboardCapability API in device_section.
+ "+ash/system/power", # To use AdaptiveChargingController API in device_section.
"+components/account_manager_core",
"+chrome/services/local_search_service",
"+device/udev_linux/fake_udev_loader.h", # For keyboard unit test.
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/DIR_METADATA b/chromium/chrome/browser/ui/webui/settings/ash/DIR_METADATA
index e84643a0ace..a3b9d18a21d 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/DIR_METADATA
+++ b/chromium/chrome/browser/ui/webui/settings/ash/DIR_METADATA
@@ -1 +1 @@
-mixins: "//chrome/browser/resources/settings/chromeos/COMMON_METADATA"
+mixins: "//chrome/browser/resources/ash/settings/COMMON_METADATA"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/OWNERS b/chromium/chrome/browser/ui/webui/settings/ash/OWNERS
index f7c4d143805..e4b1c84ef22 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/OWNERS
+++ b/chromium/chrome/browser/ui/webui/settings/ash/OWNERS
@@ -2,9 +2,9 @@ file://chrome/browser/resources/ash/settings/OWNERS
per-file account_manager_*=file://chromeos/ash/components/account_manager/OWNERS
per-file apps_section*=file://chrome/browser/ui/webui/app_management/OWNERS
-per-file device_*=file://chrome/browser/resources/settings/chromeos/device_page/OWNERS
+per-file device_*=file://chrome/browser/resources/ash/settings/device_page/OWNERS
per-file files_section*=file://ui/file_manager/OWNERS
per-file kerberos_*=file://chrome/browser/ash/kerberos/OWNERS
-per-file languages_section*=file://chrome/browser/resources/settings/chromeos/os_languages_page/OWNERS
+per-file languages_section*=file://chrome/browser/resources/ash/settings/os_languages_page/OWNERS
per-file multidevice_handler*=file://chromeos/ash/components/multidevice/OWNERS
-per-file printing_*,cups_*=file://chrome/browser/resources/settings/chromeos/os_printing_page/OWNERS
+per-file printing_*,cups_*=file://chrome/browser/resources/ash/settings/os_printing_page/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/about_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/about_section.cc
index c4afddbbda0..f2c656c972f 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/about_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/about_section.cc
@@ -17,13 +17,13 @@
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/obsolete_system/obsolete_system.h"
-#include "chrome/browser/policy/management_utils.h"
#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/management/management_ui.h"
#include "chrome/browser/ui/webui/settings/about_handler.h"
#include "chrome/browser/ui/webui/settings/ash/device_name_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/version/version_ui.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/channel_info.h"
@@ -32,6 +32,7 @@
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/dbus/constants/dbus_switches.h"
+#include "components/policy/core/common/management/management_service.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/tribool.h"
@@ -46,7 +47,6 @@
namespace ash::settings {
namespace mojom {
-using ::chromeos::settings::mojom::kAboutChromeOsDetailsSubpagePath;
using ::chromeos::settings::mojom::kAboutChromeOsSectionPath;
using ::chromeos::settings::mojom::kDetailedBuildInfoSubpagePath;
using ::chromeos::settings::mojom::Section;
@@ -65,17 +65,17 @@ const std::vector<SearchConcept>& GetAboutSearchConcepts() {
mojom::SearchResultType::kSubpage,
{.subpage = mojom::Subpage::kDetailedBuildInfo}},
{IDS_SETTINGS_ABOUT_OS,
- mojom::kAboutChromeOsDetailsSubpagePath,
+ mojom::kAboutChromeOsSectionPath,
mojom::SearchResultIcon::kChrome,
mojom::SearchResultDefaultRank::kMedium,
- mojom::SearchResultType::kSubpage,
- {.subpage = mojom::Subpage::kAboutChromeOsDetails}},
+ mojom::SearchResultType::kSection,
+ {.section = mojom::Section::kAboutChromeOs}},
{IDS_OS_SETTINGS_TAG_OS_VERSION,
- mojom::kAboutChromeOsDetailsSubpagePath,
+ mojom::kAboutChromeOsSectionPath,
mojom::SearchResultIcon::kChrome,
mojom::SearchResultDefaultRank::kMedium,
- mojom::SearchResultType::kSubpage,
- {.subpage = mojom::Subpage::kAboutChromeOsDetails}},
+ mojom::SearchResultType::kSection,
+ {.section = mojom::Section::kAboutChromeOs}},
{IDS_OS_SETTINGS_TAG_ABOUT_CHROME_OS_CHANNEL,
mojom::kDetailedBuildInfoSubpagePath,
mojom::SearchResultIcon::kChrome,
@@ -89,19 +89,19 @@ const std::vector<SearchConcept>& GetAboutSearchConcepts() {
mojom::SearchResultType::kSetting,
{.setting = mojom::Setting::kCopyDetailedBuildInfo}},
{IDS_OS_SETTINGS_TAG_ABOUT_OS_UPDATE,
- mojom::kAboutChromeOsDetailsSubpagePath,
+ mojom::kAboutChromeOsSectionPath,
mojom::SearchResultIcon::kChrome,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
{.setting = mojom::Setting::kCheckForOsUpdate}},
{IDS_OS_SETTINGS_TAG_ABOUT_HELP,
- mojom::kAboutChromeOsDetailsSubpagePath,
+ mojom::kAboutChromeOsSectionPath,
mojom::SearchResultIcon::kChrome,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
{.setting = mojom::Setting::kGetHelpWithChromeOs}},
{IDS_OS_SETTINGS_TAG_ABOUT_RELEASE_NOTES,
- mojom::kAboutChromeOsDetailsSubpagePath,
+ mojom::kAboutChromeOsSectionPath,
mojom::SearchResultIcon::kChrome,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
@@ -115,7 +115,7 @@ const std::vector<SearchConcept>& GetAboutSearchConcepts() {
const std::vector<SearchConcept>& GetDiagnosticsAppSearchConcepts() {
static const base::NoDestructor<std::vector<SearchConcept>> tags({
{IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS,
- mojom::kAboutChromeOsDetailsSubpagePath,
+ mojom::kAboutChromeOsSectionPath,
mojom::SearchResultIcon::kChrome,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
@@ -131,7 +131,7 @@ const std::vector<SearchConcept>& GetDiagnosticsAppSearchConcepts() {
const std::vector<SearchConcept>& GetFirmwareUpdatesAppSearchConcepts() {
static const base::NoDestructor<std::vector<SearchConcept>> tags({
{IDS_OS_SETTINGS_TAG_ABOUT_FIRMWARE_UPDATES,
- mojom::kAboutChromeOsDetailsSubpagePath,
+ mojom::kAboutChromeOsSectionPath,
mojom::SearchResultIcon::kChrome,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
@@ -157,7 +157,7 @@ const std::vector<SearchConcept>& GetDeviceNameSearchConcepts() {
const std::vector<SearchConcept>& GetAboutTermsOfServiceSearchConcepts() {
static const base::NoDestructor<std::vector<SearchConcept>> tags({
{IDS_OS_SETTINGS_TAG_ABOUT_TERMS_OF_SERVICE,
- mojom::kAboutChromeOsDetailsSubpagePath,
+ mojom::kAboutChromeOsSectionPath,
mojom::SearchResultIcon::kChrome,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
@@ -169,7 +169,7 @@ const std::vector<SearchConcept>& GetAboutTermsOfServiceSearchConcepts() {
const std::vector<SearchConcept>& GetAboutReportIssueSearchConcepts() {
static const base::NoDestructor<std::vector<SearchConcept>> tags({
{IDS_OS_SETTINGS_TAG_ABOUT_REPORT_ISSUE,
- mojom::kAboutChromeOsDetailsSubpagePath,
+ mojom::kAboutChromeOsSectionPath,
mojom::SearchResultIcon::kChrome,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
@@ -245,15 +245,26 @@ AboutSection::AboutSection(Profile* profile,
AboutSection::~AboutSection() = default;
void AboutSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
// Top level About page strings.
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ webui::LocalizedString kLocalizedStrings[] = {
{"aboutProductLogoAlt", IDS_SHORT_PRODUCT_LOGO_ALT_TEXT},
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
{"aboutReportAnIssue", IDS_SETTINGS_ABOUT_PAGE_REPORT_AN_ISSUE},
{"aboutSendFeedback", IDS_SETTINGS_ABOUT_PAGE_SEND_FEEDBACK},
+ {"aboutSendFeedbackDescription",
+ IDS_OS_SETTINGS_REVAMP_SEND_FEEDBACK_DESCRIPTION},
#endif
{"aboutDiagnostics", IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS},
+ {"aboutDiagnosticseDescription",
+ IDS_OS_SETTINGS_REVAMP_DIAGNOSTICS_DESCRIPTION},
{"aboutFirmwareUpdates", IDS_SETTINGS_ABOUT_PAGE_FIRMWARE_UPDATES},
+ {"aboutFirmwareUpToDateDescription",
+ IDS_OS_SETTINGS_REVAMP_FIRMWARE_UP_TO_DATE_DESCRIPTION},
+ {"aboutFirmwareUpdateAvailableDescription",
+ IDS_OS_SETTINGS_REVAMP_FIRMWARE_UPDATE_AVAILABLE_DESCRIPTION},
{"aboutRelaunch", IDS_SETTINGS_ABOUT_PAGE_RELAUNCH},
{"aboutUpgradeCheckStarted", IDS_SETTINGS_ABOUT_UPGRADE_CHECK_STARTED},
{"aboutUpgradeNotUpToDate", IDS_SETTINGS_UPGRADE_NOT_UP_TO_DATE},
@@ -351,10 +362,16 @@ void AboutSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
{"isEnterpriseManagedTitle",
IDS_OS_SETTINGS_ABOUT_PAGE_ENTERPRISE_ENNROLLED_TITLE},
{"aboutOsPageTitle", IDS_SETTINGS_ABOUT_OS},
- {"aboutGetHelpUsingChromeOs", IDS_SETTINGS_GET_HELP_USING_CHROME_OS},
+ {"aboutGetHelpUsingChromeOs",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_GET_HELP_USING_CHROME_OS
+ : IDS_SETTINGS_GET_HELP_USING_CHROME_OS},
+ {"aboutGetHelpDescription",
+ IDS_OS_SETTINGS_REVAMP_GET_HELP_USING_CHROME_OS_DESCRIPTION},
{"aboutOsProductTitle", IDS_PRODUCT_OS_NAME},
{"aboutReleaseNotesOffline", IDS_SETTINGS_ABOUT_PAGE_RELEASE_NOTES},
{"aboutShowReleaseNotes", IDS_SETTINGS_ABOUT_PAGE_SHOW_RELEASE_NOTES},
+ {"aboutShowReleaseNotesDescription",
+ IDS_OS_SETTINGS_REVAMP_ABOUT_PAGE_SHOW_RELEASE_NOTES_DESCRIPTION},
{"aboutManagedEndOfLifeSubtitle",
IDS_SETTINGS_ABOUT_PAGE_MANAGED_END_OF_LIFE_SUBTITLE},
{"aboutUpgradeTryAgain", IDS_SETTINGS_UPGRADE_TRY_AGAIN},
@@ -390,7 +407,8 @@ void AboutSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
html_source->AddString("deviceManager", GetDeviceManager());
if (user_manager::UserManager::IsInitialized()) {
- bool is_enterprise_managed = policy::IsDeviceEnterpriseManaged();
+ bool is_enterprise_managed =
+ policy::ManagementServiceFactory::GetForPlatform()->IsManaged();
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
bool is_current_owner = user_manager->IsCurrentUserOwner();
@@ -431,8 +449,9 @@ void AboutSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
base::ASCIIToUTF16(chrome::kChromeUICrostiniCreditsURL));
html_source->AddString("aboutProductOsWithLinuxLicense",
os_with_linux_license);
- html_source->AddBoolean("aboutEnterpriseManaged",
- policy::IsDeviceEnterpriseManaged());
+ html_source->AddBoolean(
+ "aboutEnterpriseManaged",
+ policy::ManagementServiceFactory::GetForPlatform()->IsManaged());
html_source->AddBoolean("aboutIsArcEnabled",
arc::IsArcPlayStoreEnabledForProfile(profile()));
html_source->AddBoolean("aboutIsDeveloperMode",
@@ -502,7 +521,7 @@ mojom::SearchResultIcon AboutSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kChrome;
}
-std::string AboutSection::GetSectionPath() const {
+const char* AboutSection::GetSectionPath() const {
return mojom::kAboutChromeOsSectionPath;
}
@@ -512,24 +531,20 @@ bool AboutSection::LogMetric(mojom::Setting setting, base::Value& value) const {
}
void AboutSection::RegisterHierarchy(HierarchyGenerator* generator) const {
- // About Chrome OS.
- generator->RegisterTopLevelSubpage(
- IDS_SETTINGS_ABOUT_OS, mojom::Subpage::kAboutChromeOsDetails,
- mojom::SearchResultIcon::kChrome, mojom::SearchResultDefaultRank::kMedium,
- mojom::kAboutChromeOsDetailsSubpagePath);
- static constexpr mojom::Setting kAboutChromeOsDetailsSettings[] = {
- mojom::Setting::kCheckForOsUpdate, mojom::Setting::kSeeWhatsNew,
- mojom::Setting::kGetHelpWithChromeOs, mojom::Setting::kReportAnIssue,
- mojom::Setting::kTermsOfService, mojom::Setting::kDiagnostics,
- mojom::Setting::kFirmwareUpdates};
- RegisterNestedSettingBulk(mojom::Subpage::kAboutChromeOsDetails,
- kAboutChromeOsDetailsSettings, generator);
+ // Top-level About section
+ generator->RegisterTopLevelSetting(mojom::Setting::kCheckForOsUpdate);
+ generator->RegisterTopLevelSetting(mojom::Setting::kSeeWhatsNew);
+ generator->RegisterTopLevelSetting(mojom::Setting::kGetHelpWithChromeOs);
+ generator->RegisterTopLevelSetting(mojom::Setting::kReportAnIssue);
+ generator->RegisterTopLevelSetting(mojom::Setting::kTermsOfService);
+ generator->RegisterTopLevelSetting(mojom::Setting::kDiagnostics);
+ generator->RegisterTopLevelSetting(mojom::Setting::kFirmwareUpdates);
// Detailed build info.
- generator->RegisterNestedSubpage(
+ generator->RegisterTopLevelSubpage(
IDS_SETTINGS_ABOUT_PAGE_DETAILED_BUILD_INFO,
- mojom::Subpage::kDetailedBuildInfo, mojom::Subpage::kAboutChromeOsDetails,
- mojom::SearchResultIcon::kChrome, mojom::SearchResultDefaultRank::kMedium,
+ mojom::Subpage::kDetailedBuildInfo, mojom::SearchResultIcon::kChrome,
+ mojom::SearchResultDefaultRank::kMedium,
mojom::kDetailedBuildInfoSubpagePath);
static constexpr mojom::Setting kDetailedBuildInfoSettings[] = {
mojom::Setting::kChangeChromeChannel, mojom::Setting::kChangeDeviceName,
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/about_section.h b/chromium/chrome/browser/ui/webui/settings/ash/about_section.h
index 567422fc649..a31a4deb3de 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/about_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/about_section.h
@@ -31,18 +31,18 @@ class AboutSection : public OsSettingsSection {
AboutSection(Profile* profile, SearchTagRegistry* search_tag_registry);
~AboutSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
// Returns if the auto update toggle should be shown for the active user.
bool ShouldShowAUToggle(user_manager::User* active_user);
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_browsertest.cc
index efe002668b2..bf3e5dee090 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_browsertest.cc
@@ -25,6 +25,7 @@
#include "content/public/test/browser_test.h"
#include "content/public/test/test_web_ui.h"
#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/base/ime/ash/input_method_descriptor.h"
#include "ui/base/ime/ash/input_method_manager.h"
@@ -274,9 +275,9 @@ IN_PROC_BROWSER_TEST_F(AccessibilityHandlerTest, DictationLocalesCalculation) {
input_method::InputMethodDescriptors imes;
for (auto& locale : testcase.ime_locales) {
std::string id = "fake-ime-extension-" + locale;
- input_method::InputMethodDescriptor descriptor(id, locale, std::string(),
- std::string(), {locale},
- false, GURL(), GURL());
+ input_method::InputMethodDescriptor descriptor(
+ id, locale, std::string(), std::string(), {locale}, false, GURL(),
+ GURL(), /*handwriting_language=*/absl::nullopt);
imes.push_back(descriptor);
}
ime_manager->GetInputMethodUtil()->ResetInputMethods(imes);
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_unittest.cc
index ec470e5eea7..6ed7813fe88 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/accessibility_handler_unittest.cc
@@ -62,11 +62,12 @@ class AccessibilityHandlerTest : public testing::Test {
protected:
std::unique_ptr<content::TestWebUI> web_ui_;
- raw_ptr<MockNewWindowDelegate, ExperimentalAsh> new_window_delegate_primary_;
+ raw_ptr<MockNewWindowDelegate, DanglingUntriaged | ExperimentalAsh>
+ new_window_delegate_primary_;
private:
content::BrowserTaskEnvironment task_environment_;
- raw_ptr<AccessibilityHandler, ExperimentalAsh> handler_;
+ raw_ptr<AccessibilityHandler, DanglingUntriaged | ExperimentalAsh> handler_;
std::unique_ptr<TestingProfileManager> profile_manager_;
std::unique_ptr<TestNewWindowDelegateProvider> new_window_provider_;
};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.cc
index 766eff0c09c..661890e835b 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.cc
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
+#include "ash/color_enhancement/color_enhancement_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/accessibility_controller_enums.h"
@@ -20,10 +21,10 @@
#include "chrome/browser/accessibility/accessibility_state_utils.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/accessibility_main_handler.h"
#include "chrome/browser/ui/webui/settings/ash/accessibility_handler.h"
#include "chrome/browser/ui/webui/settings/ash/pdf_ocr_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/select_to_speak_handler.h"
#include "chrome/browser/ui/webui/settings/ash/switch_access_handler.h"
#include "chrome/browser/ui/webui/settings/ash/tts_handler.h"
@@ -86,7 +87,7 @@ const std::vector<SearchConcept>& GetA11ySearchConcepts() {
IDS_OS_SETTINGS_TAG_A11Y_TEXT_TO_SPEECH_PAGE_ALT2,
IDS_OS_SETTINGS_TAG_A11Y_TEXT_TO_SPEECH_PAGE_ALT3,
IDS_OS_SETTINGS_TAG_A11Y_TEXT_TO_SPEECH_PAGE_ALT4,
- SearchConcept::kAltTagEnd}},
+ IDS_OS_SETTINGS_TAG_A11Y_TEXT_TO_SPEECH_PAGE_ALT5}},
{IDS_OS_SETTINGS_TAG_A11Y_DISPLAY_AND_MAGNIFICATION_PAGE,
mojom::kDisplayAndMagnificationSubpagePath,
mojom::SearchResultIcon::kA11y,
@@ -419,6 +420,23 @@ GetA11yFullscreenMagnifierFocusFollowingSearchConcepts() {
return *tags;
}
+const std::vector<SearchConcept>& GetA11yColorCorrectionSearchConcepts() {
+ static const base::NoDestructor<std::vector<SearchConcept>> tags({
+ {IDS_OS_SETTINGS_TAG_A11Y_COLOR_CORRECTION,
+ mojom::kDisplayAndMagnificationSubpagePath,
+ mojom::SearchResultIcon::kA11y,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::SearchResultType::kSetting,
+ {.setting = mojom::Setting::kColorCorrectionEnabled},
+ {IDS_OS_SETTINGS_TAG_A11Y_COLOR_CORRECTION_ALT1,
+ IDS_OS_SETTINGS_TAG_A11Y_COLOR_CORRECTION_ALT2,
+ IDS_OS_SETTINGS_TAG_A11Y_COLOR_CORRECTION_ALT3,
+ IDS_OS_SETTINGS_TAG_A11Y_COLOR_CORRECTION_ALT4,
+ IDS_OS_SETTINGS_TAG_A11Y_COLOR_CORRECTION_ALT5}},
+ });
+ return *tags;
+}
+
bool IsLiveCaptionEnabled() {
return captions::IsLiveCaptionFeatureSupported();
}
@@ -427,10 +445,6 @@ bool IsAccessibilityChromeVoxPageMigrationEnabled() {
return ::features::IsAccessibilityChromeVoxPageMigrationEnabled();
}
-bool IsAccessibilitySelectToSpeakPageMigrationEnabled() {
- return ::features::IsAccessibilitySelectToSpeakPageMigrationEnabled();
-}
-
bool AreExperimentalAccessibilityColorEnhancementSettingsEnabled() {
return ::features::
AreExperimentalAccessibilityColorEnhancementSettingsEnabled();
@@ -446,6 +460,17 @@ bool AreTabletNavigationButtonsAllowed() {
TabletMode::IsBoardTypeMarkedAsTabletCapable();
}
+int GetDisplayAndMangificationLinkDescriptionResourceId() {
+ if (AreExperimentalAccessibilityColorEnhancementSettingsEnabled()) {
+ return IDS_SETTINGS_ACCESSIBILITY_DISPLAY_AND_MAGNIFICATION_LINK_NEW_DESCRIPTION;
+ }
+ return IDS_SETTINGS_ACCESSIBILITY_DISPLAY_AND_MAGNIFICATION_LINK_DESCRIPTION;
+}
+
+bool IsAccessibilityGameFaceIntegrationEnabled() {
+ return ::features::IsAccessibilityGameFaceIntegrationEnabled();
+}
+
} // namespace
AccessibilitySection::AccessibilitySection(
@@ -497,7 +522,10 @@ AccessibilitySection::~AccessibilitySection() {
void AccessibilitySection::AddLoadTimeData(
content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
{"a11yExplanation", IDS_SETTINGS_ACCESSIBILITY_EXPLANATION},
{"a11yPageTitle", IDS_SETTINGS_ACCESSIBILITY},
{"a11yWebStore", IDS_SETTINGS_ACCESSIBILITY_WEB_STORE},
@@ -716,8 +744,6 @@ void AccessibilitySection::AddLoadTimeData(
IDS_SETTINGS_ACCESSIBILITY_DICTATION_SUBTITLE_SODA_DOWNLOAD_ERROR},
{"dictationLocaleSubLabelOffline",
IDS_SETTINGS_ACCESSIBILITY_DICTATION_LOCALE_SUB_LABEL_OFFLINE},
- {"displayAndMagnificationLinkDescription",
- IDS_SETTINGS_ACCESSIBILITY_DISPLAY_AND_MAGNIFICATION_LINK_DESCRIPTION},
{"displayAndMagnificationLinkTitle",
IDS_SETTINGS_ACCESSIBILITY_DISPLAY_AND_MAGNIFICATION_LINK_TITLE},
{"displayHeading", IDS_SETTINGS_ACCESSIBILITY_DISPLAY_HEADING},
@@ -1027,7 +1053,11 @@ void AccessibilitySection::AddLoadTimeData(
IDS_SETTINGS_A11Y_TABLET_MODE_SHELF_BUTTONS_DESCRIPTION},
{"tabletModeShelfNavigationButtonsSettingLabel",
IDS_SETTINGS_A11Y_TABLET_MODE_SHELF_BUTTONS_LABEL},
- {"tapDraggingLabel", IDS_SETTINGS_TAP_DRAGGING_LABEL},
+ {"tapDraggingLabel", kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_TAP_DRAGGING_LABEL
+ : IDS_SETTINGS_TAP_DRAGGING_LABEL},
+ {"tapDraggingDescription",
+ IDS_OS_SETTINGS_REVAMP_TAP_DRAGGING_DESCRIPTION},
{"textToSpeechEngines", IDS_SETTINGS_TEXT_TO_SPEECH_ENGINES},
{"textToSpeechHeading",
IDS_SETTINGS_ACCESSIBILITY_TEXT_TO_SPEECH_HEADING},
@@ -1072,6 +1102,11 @@ void AccessibilitySection::AddLoadTimeData(
html_source->AddString("selectToSpeakLearnMoreUrl",
chrome::kSelectToSpeakLearnMoreURL);
+ html_source->AddString(
+ "displayAndMagnificationLinkDescription",
+ l10n_util::GetStringUTF16(
+ GetDisplayAndMangificationLinkDescriptionResourceId()));
+
html_source->AddBoolean(
"showExperimentalAccessibilitySwitchAccessImprovedTextInput",
IsSwitchAccessTextAllowed());
@@ -1085,9 +1120,6 @@ void AccessibilitySection::AddLoadTimeData(
html_source->AddBoolean("isAccessibilityChromeVoxPageMigrationEnabled",
IsAccessibilityChromeVoxPageMigrationEnabled());
- html_source->AddBoolean("isAccessibilitySelectToSpeakPageMigrationEnabled",
- IsAccessibilitySelectToSpeakPageMigrationEnabled());
-
html_source->AddBoolean(
"areExperimentalAccessibilityColorEnhancementSettingsEnabled",
AreExperimentalAccessibilityColorEnhancementSettingsEnabled());
@@ -1095,6 +1127,9 @@ void AccessibilitySection::AddLoadTimeData(
html_source->AddBoolean("pdfOcrEnabled",
base::FeatureList::IsEnabled(::features::kPdfOcr));
+ html_source->AddBoolean("isAccessibilityGameFaceIntegrationEnabled",
+ IsAccessibilityGameFaceIntegrationEnabled());
+
::settings::AddCaptionSubpageStrings(html_source);
}
@@ -1127,9 +1162,10 @@ mojom::SearchResultIcon AccessibilitySection::GetSectionIcon() const {
return mojom::SearchResultIcon::kA11y;
}
-std::string AccessibilitySection::GetSectionPath() const {
+const char* AccessibilitySection::GetSectionPath() const {
return mojom::kAccessibilitySectionPath;
}
+
bool AccessibilitySection::LogMetric(mojom::Setting setting,
base::Value& value) const {
// TODO(accessibility): Ensure to capture metrics for Switch Access's action
@@ -1146,6 +1182,21 @@ bool AccessibilitySection::LogMetric(mojom::Setting setting,
"FullscreenMagnifierMouseFollowingMode",
static_cast<MagnifierMouseFollowingMode>(value.GetInt()));
return true;
+ case mojom::Setting::kColorCorrectionEnabled:
+ base::UmaHistogramBoolean(
+ "ChromeOS.Settings.Accessibility.ColorCorrection.Enabled",
+ value.GetBool());
+ return true;
+ case mojom::Setting::kColorCorrectionFilterType:
+ base::UmaHistogramEnumeration(
+ "ChromeOS.Settings.Accessibility.ColorCorrection.FilterType",
+ static_cast<ColorVisionCorrectionType>(value.GetInt()));
+ return true;
+ case mojom::Setting::kColorCorrectionFilterAmount:
+ base::UmaHistogramPercentage(
+ "ChromeOS.Settings.Accessibility.ColorCorrection.FilterAmount",
+ value.GetInt());
+ return true;
default:
return false;
@@ -1178,20 +1229,18 @@ void AccessibilitySection::RegisterHierarchy(
mojom::kChromeVoxSubpagePath);
}
// Select to speak options page.
- if (IsAccessibilitySelectToSpeakPageMigrationEnabled()) {
- generator->RegisterTopLevelSubpage(
- IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_LINK_TITLE,
- mojom::Subpage::kSelectToSpeak, mojom::SearchResultIcon::kA11y,
- mojom::SearchResultDefaultRank::kMedium,
- mojom::kSelectToSpeakSubpagePath);
- static constexpr mojom::Setting kSelectToSpeakSettings[] = {
- mojom::Setting::kSelectToSpeakWordHighlight,
- mojom::Setting::kSelectToSpeakBackgroundShading,
- mojom::Setting::kSelectToSpeakNavigationControls,
- };
- RegisterNestedSettingBulk(mojom::Subpage::kSelectToSpeak,
- kSelectToSpeakSettings, generator);
- }
+ generator->RegisterTopLevelSubpage(
+ IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_LINK_TITLE,
+ mojom::Subpage::kSelectToSpeak, mojom::SearchResultIcon::kA11y,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::kSelectToSpeakSubpagePath);
+ static constexpr mojom::Setting kSelectToSpeakSettings[] = {
+ mojom::Setting::kSelectToSpeakWordHighlight,
+ mojom::Setting::kSelectToSpeakBackgroundShading,
+ mojom::Setting::kSelectToSpeakNavigationControls,
+ };
+ RegisterNestedSettingBulk(mojom::Subpage::kSelectToSpeak,
+ kSelectToSpeakSettings, generator);
// Display and magnification page.
generator->RegisterTopLevelSubpage(
IDS_SETTINGS_ACCESSIBILITY_DISPLAY_AND_MAGNIFICATION_LINK_TITLE,
@@ -1239,6 +1288,9 @@ void AccessibilitySection::RegisterHierarchy(
mojom::Setting::kMonoAudio,
mojom::Setting::kStartupSound,
mojom::Setting::kEnableCursorColor,
+ mojom::Setting::kColorCorrectionEnabled,
+ mojom::Setting::kColorCorrectionFilterType,
+ mojom::Setting::kColorCorrectionFilterAmount,
};
RegisterNestedSettingBulk(mojom::Subpage::kManageAccessibility,
kManageAccessibilitySettings, generator);
@@ -1342,6 +1394,11 @@ void AccessibilitySection::UpdateSearchTags() {
GetA11yFullscreenMagnifierFocusFollowingSearchConcepts());
}
+ if (::features::
+ AreExperimentalAccessibilityColorEnhancementSettingsEnabled()) {
+ updater.AddSearchTags(GetA11yColorCorrectionSearchConcepts());
+ }
+
if (!pref_service_->GetBoolean(prefs::kAccessibilitySwitchAccessEnabled)) {
return;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.h b/chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.h
index 40ef35a6a82..3343c250916 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/accessibility_section.h
@@ -33,18 +33,18 @@ class AccessibilitySection : public OsSettingsSection,
PrefService* pref_service);
~AccessibilitySection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
// content::VoicesChangedDelegate:
void OnVoicesChanged() override;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/account_manager_ui_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/ash/account_manager_ui_handler_browsertest.cc
index b09bbdcc0bc..162ec6b40ae 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/account_manager_ui_handler_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/account_manager_ui_handler_browsertest.cc
@@ -21,6 +21,7 @@
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/account_manager/account_manager_factory.h"
+#include "chromeos/ash/components/standalone_browser/feature_refs.h"
#include "components/account_manager_core/account_manager_facade.h"
#include "components/account_manager_core/chromeos/account_manager.h"
#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
@@ -67,16 +68,6 @@ std::ostream& operator<<(std::ostream& stream,
<< ", user_type: " << device_account_info.user_type << "}";
}
-DeviceAccountInfo GetActiveDirectoryDeviceAccountInfo() {
- return {"fake-ad-id" /*id*/,
- "primary@example.com" /*email*/,
- "primary" /*fullName*/,
- "example.com" /*organization*/,
- user_manager::USER_TYPE_ACTIVE_DIRECTORY /*user_type*/,
- account_manager::AccountType::kActiveDirectory /*account_type*/,
- AccountManager::kActiveDirectoryDummyToken /*token*/};
-}
-
DeviceAccountInfo GetGaiaDeviceAccountInfo() {
return {signin::GetTestGaiaIdForEmail("primary@example.com") /*id*/,
"primary@example.com" /*email*/,
@@ -189,14 +180,7 @@ class AccountManagerUIHandlerTest
auto user_manager = std::make_unique<FakeChromeUserManager>();
const user_manager::User* user;
if (GetDeviceAccountInfo().user_type ==
- user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY) {
- user = user_manager->AddUserWithAffiliationAndTypeAndProfile(
- AccountId::AdFromUserEmailObjGuid(GetDeviceAccountInfo().email,
- GetDeviceAccountInfo().id),
- true, user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
- profile_.get());
- } else if (GetDeviceAccountInfo().user_type ==
- user_manager::UserType::USER_TYPE_CHILD) {
+ user_manager::UserType::USER_TYPE_CHILD) {
user = user_manager->AddChildUser(AccountId::FromUserEmailGaiaId(
GetDeviceAccountInfo().email, GetDeviceAccountInfo().id));
} else {
@@ -261,15 +245,18 @@ class AccountManagerUIHandlerTest
std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<TestingProfile> profile_;
- raw_ptr<AccountManager, ExperimentalAsh> account_manager_ = nullptr;
- raw_ptr<signin::IdentityManager, ExperimentalAsh> identity_manager_ = nullptr;
+ raw_ptr<AccountManager, DanglingUntriaged | ExperimentalAsh>
+ account_manager_ = nullptr;
+ raw_ptr<signin::IdentityManager, DanglingUntriaged | ExperimentalAsh>
+ identity_manager_ = nullptr;
content::TestWebUI web_ui_;
AccountId primary_account_id_;
std::unique_ptr<TestingAccountManagerUIHandler> handler_;
};
IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTest,
- OnGetAccountsNoSecondaryAccounts) {
+ // TODO(crbug.com/1474301): Re-enable this test
+ DISABLED_OnGetAccountsNoSecondaryAccounts) {
const std::vector<::account_manager::Account> account_manager_accounts =
GetAccountsFromAccountManager();
// Only Primary account.
@@ -396,18 +383,21 @@ IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTest,
}
}
-INSTANTIATE_TEST_SUITE_P(
- AccountManagerUIHandlerTestSuite,
- AccountManagerUIHandlerTest,
- ::testing::Values(GetActiveDirectoryDeviceAccountInfo(),
- GetGaiaDeviceAccountInfo(),
- GetChildDeviceAccountInfo()));
+INSTANTIATE_TEST_SUITE_P(AccountManagerUIHandlerTestSuite,
+ AccountManagerUIHandlerTest,
+ ::testing::Values(GetGaiaDeviceAccountInfo(),
+ GetChildDeviceAccountInfo()));
class AccountManagerUIHandlerTestWithArcAccountRestrictions
: public AccountManagerUIHandlerTest {
public:
AccountManagerUIHandlerTestWithArcAccountRestrictions() {
- feature_list_.InitAndEnableFeature(ash::features::kLacrosSupport);
+ std::vector<base::test::FeatureRef> lacros =
+ ash::standalone_browser::GetFeatureRefs();
+ if (GetDeviceAccountInfo().user_type == user_manager::USER_TYPE_CHILD) {
+ lacros.push_back(crosapi::browser_util::kLacrosForSupervisedUsers);
+ }
+ feature_list_.InitWithFeatures(lacros, {});
}
void SetUpOnMainThread() override {
@@ -426,6 +416,9 @@ class AccountManagerUIHandlerTestWithArcAccountRestrictions
handler_->RegisterMessages();
handler_->AllowJavascriptForTesting();
base::RunLoop().RunUntilIdle();
+
+ ASSERT_TRUE(
+ ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled());
}
void TearDownOnMainThread() override {
@@ -466,7 +459,8 @@ class AccountManagerUIHandlerTestWithArcAccountRestrictions
private:
base::test::ScopedFeatureList feature_list_;
- raw_ptr<AccountAppsAvailability, ExperimentalAsh> account_apps_availability_;
+ raw_ptr<AccountAppsAvailability, DanglingUntriaged | ExperimentalAsh>
+ account_apps_availability_;
std::unique_ptr<TestingAccountManagerUIHandler> handler_;
};
@@ -606,8 +600,6 @@ IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTestWithArcAccountRestrictions,
INSTANTIATE_TEST_SUITE_P(
AccountManagerUIHandlerTestWithArcAccountRestrictionsSuite,
AccountManagerUIHandlerTestWithArcAccountRestrictions,
- ::testing::Values(GetActiveDirectoryDeviceAccountInfo(),
- GetGaiaDeviceAccountInfo(),
- GetChildDeviceAccountInfo()));
+ ::testing::Values(GetGaiaDeviceAccountInfo(), GetChildDeviceAccountInfo()));
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/apps_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/apps_section.cc
index 439a160a1fb..b20ff1243d5 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/apps_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/apps_section.cc
@@ -18,10 +18,10 @@
#include "chrome/browser/ash/plugin_vm/plugin_vm_features.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_pref_names.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/android_apps_handler.h"
#include "chrome/browser/ui/webui/settings/ash/guest_os_handler.h"
#include "chrome/browser/ui/webui/settings/ash/plugin_vm_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/url_constants.h"
@@ -195,18 +195,19 @@ const std::vector<SearchConcept>& GetOnStartupSearchConcepts() {
}
void AddAppManagementStrings(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
{"appManagementAppDetailsTitle", IDS_APP_MANAGEMENT_APP_DETAILS_TITLE},
- {"appManagementAppDetailsTooltipSystem",
- IDS_APP_MANAGEMENT_APP_DETAILS_TOOLTIP_SYSTEM},
+ {"appManagementAppDetailsTooltipCrosSystem",
+ IDS_APP_MANAGEMENT_APP_DETAILS_TOOLTIP_CROS_SYSTEM},
{"appManagementAppDetailsTypeAndroid",
IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_ANDROID},
{"appManagementAppDetailsTypeChrome",
IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_CHROME},
{"appManagementAppDetailsTypeWeb",
IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_WEB},
- {"appManagementAppDetailsTypeSystem",
- IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_SYSTEM},
{"appManagementAppDetailsTypeCrosSystem",
IDS_APP_MANAGEMENT_APP_DETAILS_TYPE_CROS_SYSTEM},
{"appManagementAppDetailsInstallSourceWebStore",
@@ -263,6 +264,9 @@ void AddAppManagementStrings(content::WebUIDataSource* html_source) {
IDS_APP_MANAGEMENT_INTENT_SETTINGS_TITLE},
{"appManagementIntentSharingOpenAppLabel",
IDS_APP_MANAGEMENT_INTENT_SHARING_APP_OPEN},
+ {"appManagementIntentSharingOpenAppLabel",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_OPEN_IN_APP_TITLE
+ : IDS_APP_MANAGEMENT_INTENT_SHARING_APP_OPEN},
{"appManagementIntentSharingOpenBrowserLabel",
IDS_APP_MANAGEMENT_INTENT_SHARING_BROWSER_OPEN},
{"appManagementIntentSharingTabExplanation",
@@ -270,10 +274,20 @@ void AddAppManagementStrings(content::WebUIDataSource* html_source) {
{"appManagementLocationPermissionLabel", IDS_APP_MANAGEMENT_LOCATION},
{"appManagementMicrophonePermissionLabel", IDS_APP_MANAGEMENT_MICROPHONE},
{"appManagementMorePermissionsLabel", IDS_APP_MANAGEMENT_MORE_SETTINGS},
+ {"appManagementMorePermissionsLabelAndroidApp",
+ IDS_OS_SETTINGS_REVAMP_APP_PERMISSIONS_TITLE_ANDROID},
+ {"appManagementMorePermissionsLabelWebApp",
+ IDS_OS_SETTINGS_REVAMP_APP_PERMISSIONS_TITLE_WEB_APP},
+ {"appManagementMorePermissionsLabelChromeApp",
+ IDS_OS_SETTINGS_REVAMP_APP_PERMISSIONS_TITLE_CHROME_APP},
{"appManagementNoAppsFound", IDS_APP_MANAGEMENT_NO_APPS_FOUND},
{"appManagementNoPermissions",
IDS_APPLICATION_INFO_APP_NO_PERMISSIONS_TEXT},
- {"appManagementNotificationsLabel", IDS_APP_MANAGEMENT_NOTIFICATIONS},
+ {"appManagementNotificationsLabel",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_TITLE
+ : IDS_APP_MANAGEMENT_NOTIFICATIONS},
+ {"appManagementParentAppPermissionExplanation",
+ IDS_APP_MANAGEMENT_PARENT_APP_PERMISSION_EXPLANATION},
{"appManagementPermissionAllowed", IDS_APP_MANAGEMENT_PERMISSION_ALLOWED},
{"appManagementPermissionAllowedWithDetails",
IDS_APP_MANAGEMENT_PERMISSION_ALLOWED_WITH_DETAILS},
@@ -290,6 +304,8 @@ void AddAppManagementStrings(content::WebUIDataSource* html_source) {
{"appManagementStoragePermissionLabel", IDS_APP_MANAGEMENT_STORAGE},
{"appManagementSubAppsListHeading",
IDS_APP_MANAGEMENT_SUB_APPS_LIST_HEADING},
+ {"appManagementSubAppPermissionExplanation",
+ IDS_APP_MANAGEMENT_SUB_APP_PERMISSION_EXPLANATION},
{"appManagementUninstallLabel", IDS_APP_MANAGEMENT_UNINSTALL_APP},
{"close", IDS_CLOSE},
{"fileHandlingOverflowDialogTitle",
@@ -318,6 +334,8 @@ void AddGuestOsStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_GUEST_OS_SHARED_PATHS_LIST_EMPTY_MESSAGE},
{"guestOsSharedUsbDevicesLabel",
IDS_SETTINGS_GUEST_OS_SHARED_USB_DEVICES_LABEL},
+ {"guestOsSharedUsbDevicesDescription",
+ IDS_SETTINGS_GUEST_OS_SHARED_USB_DEVICES_DESCRIPTION},
{"guestOsSharedUsbDevicesExtraDescription",
IDS_SETTINGS_GUEST_OS_SHARED_USB_DEVICES_EXTRA_DESCRIPTION},
{"guestOsSharedUsbDevicesListEmptyMessage",
@@ -411,18 +429,25 @@ AppsSection::~AppsSection() {
}
void AppsSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
{"appsPageTitle", IDS_SETTINGS_APPS_TITLE},
{"appManagementTitle", IDS_SETTINGS_APPS_LINK_TEXT},
{"appNotificationsTitle", IDS_SETTINGS_APP_NOTIFICATIONS_LINK_TEXT},
{"doNotDisturbToggleTitle",
IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_TITLE},
{"doNotDisturbToggleDescription",
- IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION},
+ kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION
+ : IDS_SETTINGS_APP_NOTIFICATIONS_DO_NOT_DISTURB_TOGGLE_DESCRIPTION},
{"appNotificationsLinkToBrowserSettingsDescription",
IDS_SETTINGS_APP_NOTIFICATIONS_LINK_TO_BROWSER_SETTINGS_DESCRIPTION},
{"appNotificationsCountDescription",
- IDS_SETTINGS_APP_NOTIFICATIONS_SUBLABEL_TEXT},
+ kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_APP_NOTIFICATIONS_LINK_DESCRIPTION
+ : IDS_SETTINGS_APP_NOTIFICATIONS_SUBLABEL_TEXT},
{"appNotificationsDoNotDisturbDescription",
IDS_SETTINGS_APP_NOTIFICATIONS_DND_ENABLED_SUBLABEL_TEXT},
{"appBadgingToggleLabel", IDS_SETTINGS_APP_BADGING_TOGGLE_LABEL},
@@ -491,7 +516,7 @@ mojom::SearchResultIcon AppsSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kAppsGrid;
}
-std::string AppsSection::GetSectionPath() const {
+const char* AppsSection::GetSectionPath() const {
return mojom::kAppsSectionPath;
}
@@ -574,22 +599,37 @@ void AppsSection::OnAppRegistered(const std::string& app_id,
}
void AppsSection::AddAndroidAppStrings(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
- {"androidAppsPageLabel", IDS_SETTINGS_ANDROID_APPS_LABEL},
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
+ {"androidAppsPageLabel", kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_ANDROID_APPS_LABEL
+ : IDS_SETTINGS_ANDROID_APPS_LABEL},
{"androidAppsEnable", IDS_SETTINGS_TURN_ON},
- {"androidAppsManageApps", IDS_SETTINGS_ANDROID_APPS_MANAGE_APPS},
- {"androidAppsRemove", IDS_SETTINGS_ANDROID_APPS_REMOVE},
+ {"androidAppsManageApps",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_ANDROID_APPS_MANAGE_APPS
+ : IDS_SETTINGS_ANDROID_APPS_MANAGE_APPS},
+ {"androidAppsRemove", kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_ANDROID_APPS_REMOVE
+ : IDS_SETTINGS_ANDROID_APPS_REMOVE},
{"androidAppsRemoveButton", IDS_SETTINGS_ANDROID_APPS_REMOVE_BUTTON},
{"androidAppsDisableDialogTitle",
- IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_TITLE},
+ kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_ANDROID_APPS_DISABLE_DIALOG_TITLE
+ : IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_TITLE},
{"androidAppsDisableDialogMessage",
- IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_MESSAGE},
+ kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_ANDROID_APPS_DISABLE_DIALOG_MESSAGE
+ : IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_MESSAGE},
{"androidAppsDisableDialogRemove",
- IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_REMOVE},
+ kIsRevampEnabled ? IDS_SETTINGS_ANDROID_APPS_REMOVE_BUTTON
+ : IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_REMOVE},
{"arcvmSharedUsbDevicesDescription",
IDS_SETTINGS_APPS_ARC_VM_SHARED_USB_DEVICES_DESCRIPTION},
{"androidAppsEnableButtonRole",
IDS_SETTINGS_ANDROID_APPS_ENABLE_BUTTON_ROLE},
+ {"androidOpenGooglePlay", IDS_OS_SETTINGS_REVAMP_OPEN_GOOGLE_PLAY},
};
html_source->AddLocalizedStrings(kLocalizedStrings);
html_source->AddLocalizedString("androidAppsPageTitle",
@@ -598,9 +638,13 @@ void AppsSection::AddAndroidAppStrings(content::WebUIDataSource* html_source) {
: IDS_SETTINGS_ANDROID_SETTINGS_TITLE);
html_source->AddString(
"androidAppsSubtext",
- l10n_util::GetStringFUTF16(
- IDS_SETTINGS_ANDROID_APPS_SUBTEXT, ui::GetChromeOSDeviceName(),
- GetHelpUrlWithBoard(chrome::kAndroidAppsLearnMoreURL)));
+ kIsRevampEnabled
+ ? l10n_util::GetStringFUTF16(
+ IDS_OS_SETTINGS_REVAMP_ANDROID_APPS_SUBTEXT,
+ GetHelpUrlWithBoard(chrome::kAndroidAppsLearnMoreURL))
+ : l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_ANDROID_APPS_SUBTEXT, ui::GetChromeOSDeviceName(),
+ GetHelpUrlWithBoard(chrome::kAndroidAppsLearnMoreURL)));
}
void AppsSection::AddPluginVmLoadTimeData(
@@ -631,8 +675,14 @@ void AppsSection::AddPluginVmLoadTimeData(
}
void AppsSection::AddOnStartupTimeData(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
- {"onStartupTitle", IDS_OS_SETTINGS_ON_STARTUP_TITLE},
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
+ {"onStartupTitle", kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_ON_STARTUP_TITLE
+ : IDS_OS_SETTINGS_ON_STARTUP_TITLE},
+ {"onStartupDescription", IDS_OS_SETTINGS_REVAMP_ON_STARTUP_DESCRIPTION},
{"onStartupAlways", IDS_OS_SETTINGS_ON_STARTUP_ALWAYS},
{"onStartupAskEveryTime", IDS_OS_SETTINGS_ON_STARTUP_ASK_EVERY_TIME},
{"onStartupDoNotRestore", IDS_OS_SETTINGS_ON_STARTUP_DO_NOT_RESTORE},
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/apps_section.h b/chromium/chrome/browser/ui/webui/settings/ash/apps_section.h
index eea0e42d8db..84075c2a20c 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/apps_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/apps_section.h
@@ -35,18 +35,18 @@ class AppsSection : public OsSettingsSection,
apps::AppServiceProxy* app_service_proxy);
~AppsSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
// ArcAppListPrefs::Observer:
void OnAppRegistered(const std::string& app_id,
const ArcAppListPrefs::AppInfo& app_info) override;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.cc
index 927d728a10b..84c278d31fa 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.cc
@@ -6,17 +6,17 @@
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
+#include "ash/webui/settings/public/constants/setting.mojom.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "chrome/browser/ui/webui/ash/bluetooth_shared_load_time_data_provider.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/search_result_icon.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/bluetooth_handler.h"
#include "chrome/browser/ui/webui/settings/ash/fast_pair_saved_devices_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/webui_url_constants.h"
@@ -358,7 +358,7 @@ mojom::SearchResultIcon BluetoothSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kBluetooth;
}
-std::string BluetoothSection::GetSectionPath() const {
+const char* BluetoothSection::GetSectionPath() const {
return mojom::kBluetoothSectionPath;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.h b/chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.h
index 77d70a20fca..3f62bd5776a 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/bluetooth_section.h
@@ -34,18 +34,18 @@ class BluetoothSection : public OsSettingsSection,
PrefService* pref_service);
~BluetoothSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
// device::BluetoothAdapter::Observer:
void AdapterPresentChanged(device::BluetoothAdapter* adapter,
bool present) override;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.cc
index 4fa5b79069e..c629792e729 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.cc
@@ -31,6 +31,7 @@
#include "chrome/browser/ui/views/crostini/crostini_uninstaller_view.h"
#include "chrome/browser/ui/webui/ash/crostini_upgrader/crostini_upgrader_dialog.h"
#include "chrome/common/pref_names.h"
+#include "chromeos/ash/components/network/network_handler.h"
#include "components/prefs/pref_service.h"
#include "components/services/app_service/public/cpp/intent_util.h"
#include "content/public/browser/browser_task_traits.h"
@@ -152,6 +153,10 @@ void CrostiniHandler::RegisterMessages() {
base::BindRepeating(&CrostiniHandler::HandleGetCrostiniActivePorts,
handler_weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
+ "getCrostiniActiveNetworkInfo",
+ base::BindRepeating(&CrostiniHandler::HandleGetCrostiniActiveNetworkInfo,
+ handler_weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
"checkCrostiniIsRunning",
base::BindRepeating(&CrostiniHandler::HandleCheckCrostiniIsRunning,
handler_weak_ptr_factory_.GetWeakPtr()));
@@ -515,6 +520,11 @@ void CrostiniHandler::OnActivePortsChanged(
activePorts);
}
+void CrostiniHandler::OnActiveNetworkChanged(const base::Value& interface,
+ const base::Value& ipAddress) {
+ FireWebUIListener("crostini-active-network-info", interface, ipAddress);
+}
+
void CrostiniHandler::HandleAddCrostiniPortForward(
const base::Value::List& args) {
CHECK_EQ(5U, args.size());
@@ -677,6 +687,18 @@ void CrostiniHandler::HandleGetCrostiniActivePorts(
->GetActivePorts());
}
+void CrostiniHandler::HandleGetCrostiniActiveNetworkInfo(
+ const base::Value::List& args) {
+ AllowJavascript();
+ CHECK_EQ(1U, args.size());
+
+ std::string callback_id = args[0].GetString();
+ ResolveJavascriptCallback(
+ base::Value(callback_id),
+ crostini::CrostiniPortForwarder::GetForProfile(profile_)
+ ->GetActiveNetworkInfo());
+}
+
void CrostiniHandler::HandleCheckCrostiniIsRunning(
const base::Value::List& args) {
AllowJavascript();
@@ -744,11 +766,14 @@ void CrostiniHandler::HandleCreateContainer(const base::Value::List& args) {
container_file.Extension() != FILE_PATH_LITERAL(".yaml");
if (isContainerBackupFile) {
- VLOG(1) << "backup_file = " << container_file;
- crostini::CrostiniExportImport::GetForProfile(profile_)->ImportContainer(
- container_id, container_file,
- base::BindOnce(&CrostiniHandler::OnContainerCreated,
- handler_weak_ptr_factory_.GetWeakPtr(), container_id));
+ VLOG(1) << "backup_file = " << container_file
+ << "will be used to create a new container.";
+ crostini::CrostiniExportImport::GetForProfile(profile_)
+ ->CreateContainerFromImport(
+ container_id, container_file,
+ base::BindOnce(&CrostiniHandler::OnContainerCreated,
+ handler_weak_ptr_factory_.GetWeakPtr(),
+ container_id));
return;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.h b/chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.h
index 2493b5ffa0d..6eda1869cf1 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/crostini_handler.h
@@ -18,6 +18,7 @@
#include "chrome/browser/ash/settings/cros_settings.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
#include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
+#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/services/app_service/public/cpp/intent.h"
@@ -104,6 +105,8 @@ class CrostiniHandler : public ::settings::SettingsPageUIHandler,
void HandleRemoveAllCrostiniPortForwards(const base::Value::List& args);
// CrostiniPortForwarder::Observer.
void OnActivePortsChanged(const base::Value::List& activePorts) override;
+ void OnActiveNetworkChanged(const base::Value& interface,
+ const base::Value& ipAddress) override;
// Handles a request for activating an existing port.
void HandleActivateCrostiniPortForward(const base::Value::List& args);
// Handles a request for deactivating an existing port.
@@ -121,6 +124,8 @@ class CrostiniHandler : public ::settings::SettingsPageUIHandler,
bool succeeded);
// Returns a list of currently forwarded ports.
void HandleGetCrostiniActivePorts(const base::Value::List& args);
+ // Returns the current active network for forwarded ports.
+ void HandleGetCrostiniActiveNetworkInfo(const base::Value::List& args);
// Checks if Crostini is running.
void HandleCheckCrostiniIsRunning(const base::Value::List& args);
// guest_os::ContainerStartedObserver
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/crostini_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/crostini_section.cc
index b155552a5dc..c88a20c4fe1 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/crostini_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/crostini_section.cc
@@ -6,6 +6,7 @@
#include "ash/components/arc/arc_prefs.h"
#include "ash/constants/ash_features.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
@@ -18,15 +19,15 @@
#include "chrome/browser/ash/guest_os/guest_id.h"
#include "chrome/browser/ash/guest_os/guest_os_pref_names.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
-#include "chrome/browser/policy/management_utils.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/crostini_handler.h"
#include "chrome/browser/ui/webui/settings/ash/guest_os_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-forward.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
+#include "components/policy/core/common/management/management_service.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
@@ -213,7 +214,7 @@ bool IsProfileManaged(Profile* profile) {
}
bool IsDeviceManaged() {
- return policy::IsDeviceEnterpriseManaged();
+ return policy::ManagementServiceFactory::GetForPlatform()->IsManaged();
}
bool IsAdbSideloadingAllowed() {
@@ -265,21 +266,18 @@ bool CrostiniSection::ShouldShowBruschetta(Profile* profile) {
}
void CrostiniSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
{"bruschettaPageLabel", IDS_SETTINGS_BRUSCHETTA_LABEL},
{"bruschettaEnable", IDS_SETTINGS_TURN_ON},
- {"bruschettaSharedUsbDevicesDescription",
- IDS_SETTINGS_BRUSCHETTA_SHARED_USB_DEVICES_DESCRIPTION},
- {"bruschettaSharedPathsInstructionsAdd",
- IDS_SETTINGS_BRUSCHETTA_SHARED_PATHS_INSTRUCTIONS_ADD},
- {"bruschettaSharedPathsRemoveFailureDialogMessage",
- IDS_SETTINGS_BRUSCHETTA_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE},
- {"bruschettaSubtext", IDS_SETTINGS_BRUSCHETTA_SUBTEXT},
- {"bruschettaRemove", IDS_SETTINGS_BRUSCHETTA_REMOVE},
{"bruschettaRemoveButton", IDS_SETTINGS_BRUSCHETTA_REMOVE_BUTTON},
{"crostiniPageTitle", IDS_SETTINGS_CROSTINI_TITLE},
{"crostiniPageLabel", IDS_SETTINGS_CROSTINI_LABEL},
- {"crostiniEnable", IDS_SETTINGS_TURN_ON},
+ {"crostiniEnable", kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_CROSTINI_SET_UP
+ : IDS_SETTINGS_TURN_ON},
{"crostiniSharedPathsInstructionsAdd",
IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD},
{"crostiniSharedPathsRemoveFailureDialogMessage",
@@ -461,18 +459,61 @@ void CrostiniSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
// Should Bruschetta be displayed in the settings at all?
html_source->AddBoolean("showBruschetta", ShouldShowBruschetta(profile_));
+ auto bruschetta_name = bruschetta::GetOverallVmName(profile_);
+
+ html_source->AddString("bruschettaPageLabel",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_BRUSCHETTA_LABEL, bruschetta_name));
+
+ auto learn_more_url =
+ base::UTF8ToUTF16(bruschetta::GetLearnMoreUrl(profile_).spec());
+ if (learn_more_url.empty()) {
+ html_source->AddString(
+ "bruschettaSubtext",
+ l10n_util::GetStringFUTF16(IDS_SETTINGS_BRUSCHETTA_SUBTEXT_NO_LINK,
+ ui::GetChromeOSDeviceName()));
+ } else {
+ html_source->AddString("bruschettaSubtext",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_BRUSCHETTA_SUBTEXT,
+ ui::GetChromeOSDeviceName(), learn_more_url));
+ }
+
+ html_source->AddString(
+ "bruschettaSharedUsbDevicesDescription",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_BRUSCHETTA_SHARED_USB_DEVICES_DESCRIPTION,
+ bruschetta_name));
html_source->AddString(
"bruschettaSharedPathsInstructionsLocate",
l10n_util::GetStringFUTF16(
IDS_SETTINGS_BRUSCHETTA_SHARED_PATHS_INSTRUCTIONS_LOCATE,
+ bruschetta_name,
base::ASCIIToUTF16(
bruschetta::BruschettaChromeOSBaseDirectory().value())));
+ html_source->AddString(
+ "bruschettaSharedPathsInstructionsAdd",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_BRUSCHETTA_SHARED_PATHS_INSTRUCTIONS_ADD,
+ bruschetta_name));
+ html_source->AddString(
+ "bruschettaSharedPathsRemoveFailureDialogMessage",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_BRUSCHETTA_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE,
+ bruschetta_name));
+ html_source->AddString("bruschettaRemove",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_BRUSCHETTA_REMOVE, bruschetta_name));
html_source->AddString(
"crostiniSubtext",
- l10n_util::GetStringFUTF16(
- IDS_SETTINGS_CROSTINI_SUBTEXT, ui::GetChromeOSDeviceName(),
- GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL)));
+ kIsRevampEnabled
+ ? l10n_util::GetStringFUTF16(
+ IDS_OS_SETTINGS_REVAMP_CROSTINI_SUBTEXT,
+ GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL))
+ : l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_CROSTINI_SUBTEXT, ui::GetChromeOSDeviceName(),
+ GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL)));
html_source->AddString(
"crostiniSubtextNotSupported",
l10n_util::GetStringFUTF16(
@@ -546,7 +587,7 @@ mojom::SearchResultIcon CrostiniSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kDeveloperTags;
}
-std::string CrostiniSection::GetSectionPath() const {
+const char* CrostiniSection::GetSectionPath() const {
return mojom::kCrostiniSectionPath;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/crostini_section.h b/chromium/chrome/browser/ui/webui/settings/ash/crostini_section.h
index 8b5123b964a..55efc0f835d 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/crostini_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/crostini_section.h
@@ -28,22 +28,22 @@ class CrostiniSection : public OsSettingsSection {
PrefService* pref_service);
~CrostiniSection() override;
- private:
- friend class CrostiniSectionTest;
-
- static bool ShouldShowBruschetta(Profile* profile);
-
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
+ friend class CrostiniSectionTest;
+
+ static bool ShouldShowBruschetta(Profile* profile);
+
bool IsExportImportAllowed() const;
bool IsContainerUpgradeAllowed() const;
bool IsPortForwardingAllowed() const;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.cc
index 5342d50f8d5..4791acccce2 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.cc
@@ -30,7 +30,6 @@
#include "chrome/browser/ash/printing/printer_event_tracker.h"
#include "chrome/browser/ash/printing/printer_event_tracker_factory.h"
#include "chrome/browser/ash/printing/printer_info.h"
-#include "chrome/browser/ash/printing/printer_setup_util.h"
#include "chrome/browser/ash/printing/server_printers_fetcher.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_prefs.h"
@@ -424,8 +423,7 @@ void CupsPrintersHandler::HandleUpdateCupsPrinter(
PRINTER_LOG(DEBUG) << "HandleUpdateCupsPrinter() called when "
"kUserPrintersAllowed is set to false";
OnAddedOrEditedPrinterCommon(printer,
- PrinterSetupResult::kNativePrintersNotAllowed,
- false /* is_automatic */);
+ PrinterSetupResult::kNativePrintersNotAllowed);
// Logs the error and runs the callback.
OnAddOrEditPrinterError(callback_id,
PrinterSetupResult::kNativePrintersNotAllowed);
@@ -459,18 +457,24 @@ void CupsPrintersHandler::HandleRetrieveCupsPrinterPpd(
return;
}
- ash::printing::SetUpPrinter(
- printers_manager_, *printer,
+ printers_manager_->SetUpPrinter(
+ *printer, /*is_automatic_installation=*/true,
base::BindOnce(&CupsPrintersHandler::OnSetUpPrinter,
weak_factory_.GetWeakPtr(), printer_id, printer_name,
eula));
}
-void CupsPrintersHandler::OnSetUpPrinter(
- const std::string& printer_id,
- const std::string& printer_name,
- const std::string& eula,
- const absl::optional<::printing::PrinterSemanticCapsAndDefaults>& caps) {
+void CupsPrintersHandler::OnSetUpPrinter(const std::string& printer_id,
+ const std::string& printer_name,
+ const std::string& eula,
+ PrinterSetupResult result) {
+ if (result != PrinterSetupResult::kSuccess) {
+ PRINTER_LOG(ERROR) << "Cannot setup a printer " << printer_id << " ("
+ << printer_name << ")";
+ OnRetrievePpdError(printer_name);
+ return;
+ }
+
// Once the printer has been setup we can request the PPD.
const std::vector<uint8_t> empty_ppd;
@@ -687,7 +691,7 @@ void CupsPrintersHandler::OnAutoconfQueriedDiscovered(
PRINTER_LOG(DEBUG) << "Performing autoconf setup";
printer.mutable_ppd_reference()->autoconf = true;
printers_manager_->SetUpPrinter(
- printer,
+ printer, /*is_automatic_installation=*/true,
base::BindOnce(&CupsPrintersHandler::OnAddedDiscoveredPrinter,
weak_factory_.GetWeakPtr(), callback_id, printer));
return;
@@ -806,8 +810,7 @@ void CupsPrintersHandler::AddOrReconfigurePrinter(const base::Value::List& args,
PRINTER_LOG(DEBUG) << "AddOrReconfigurePrinter() called when "
"kUserPrintersAllowed is set to false";
OnAddedOrEditedPrinterCommon(*printer,
- PrinterSetupResult::kNativePrintersNotAllowed,
- false /* is_automatic */);
+ PrinterSetupResult::kNativePrintersNotAllowed);
// Used to fire the web UI listener.
OnAddOrEditPrinterError(callback_id,
PrinterSetupResult::kNativePrintersNotAllowed);
@@ -886,6 +889,7 @@ void CupsPrintersHandler::AddOrReconfigurePrinter(const base::Value::List& args,
printers_manager_->SetUpPrinter(
*printer,
+ /*is_automatic_installation=*/false,
base::BindOnce(&CupsPrintersHandler::OnAddedOrEditedSpecifiedPrinter,
weak_factory_.GetWeakPtr(), callback_id, *printer,
is_printer_edit));
@@ -893,8 +897,7 @@ void CupsPrintersHandler::AddOrReconfigurePrinter(const base::Value::List& args,
void CupsPrintersHandler::OnAddedOrEditedPrinterCommon(
const Printer& printer,
- PrinterSetupResult result_code,
- bool is_automatic) {
+ PrinterSetupResult result_code) {
if (printer.IsZeroconf()) {
UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.ZeroconfPrinterSetupResult",
result_code, PrinterSetupResult::kMaxValue);
@@ -908,7 +911,6 @@ void CupsPrintersHandler::OnAddedOrEditedPrinterCommon(
UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PrinterAdded",
printer.GetProtocol(), Printer::kProtocolMax);
PRINTER_LOG(USER) << "Performing printer setup";
- printers_manager_->PrinterInstalled(printer, is_automatic);
printers_manager_->SavePrinter(printer);
if (printer.IsUsbProtocol()) {
// Record UMA for USB printer setup source.
@@ -953,7 +955,7 @@ void CupsPrintersHandler::OnAddedDiscoveredPrinter(
const std::string& callback_id,
const Printer& printer,
PrinterSetupResult result_code) {
- OnAddedOrEditedPrinterCommon(printer, result_code, /*is_automatic=*/true);
+ OnAddedOrEditedPrinterCommon(printer, result_code);
if (result_code == PrinterSetupResult::kSuccess) {
ResolveJavascriptCallback(base::Value(callback_id),
base::Value(static_cast<int>(result_code)));
@@ -976,7 +978,7 @@ void CupsPrintersHandler::OnAddedOrEditedSpecifiedPrinter(
}
const int result_code_int = static_cast<int>(result_code);
PRINTER_LOG(EVENT) << "Add/Update manual printer: " << result_code_int;
- OnAddedOrEditedPrinterCommon(printer, result_code, /*is_automatic=*/false);
+ OnAddedOrEditedPrinterCommon(printer, result_code);
if (result_code != PrinterSetupResult::kSuccess &&
result_code != PrinterSetupResult::kEditSuccess) {
@@ -1223,7 +1225,7 @@ void CupsPrintersHandler::HandleAddDiscoveredPrinter(
const std::string& callback_id = args[0].GetString();
const std::string& printer_id = args[1].GetString();
- PRINTER_LOG(USER) << "Adding discovered printer";
+ PRINTER_LOG(USER) << "Adding discovered printer " << printer_id;
absl::optional<Printer> printer = printers_manager_->GetPrinter(printer_id);
if (!printer) {
PRINTER_LOG(ERROR) << "Discovered printer disappeared";
@@ -1251,7 +1253,7 @@ void CupsPrintersHandler::HandleAddDiscoveredPrinter(
// If we have something that looks like a ppd reference for this printer,
// try to configure it.
printers_manager_->SetUpPrinter(
- *printer,
+ *printer, /*is_automatic_installation=*/true,
base::BindOnce(&CupsPrintersHandler::OnAddedDiscoveredPrinter,
weak_factory_.GetWeakPtr(), callback_id, *printer));
return;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.h b/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.h
index fd69553f488..47d80264a1f 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler.h
@@ -82,11 +82,11 @@ class CupsPrintersHandler : public ::settings::SettingsPageUIHandler,
void HandleUpdateCupsPrinter(const base::Value::List& args);
void HandleRemoveCupsPrinter(const base::Value::List& args);
void HandleRetrieveCupsPrinterPpd(const base::Value::List& args);
- void OnSetUpPrinter(
- const std::string& printer_id,
- const std::string& printer_name,
- const std::string& eula,
- const absl::optional<printing::PrinterSemanticCapsAndDefaults>& caps);
+
+ void OnSetUpPrinter(const std::string& printer_id,
+ const std::string& printer_name,
+ const std::string& eula,
+ PrinterSetupResult result);
// For a CupsPrinterInfo in |args|, retrieves the relevant PrinterInfo object
// using an IPP call to the printer.
@@ -208,8 +208,7 @@ class CupsPrintersHandler : public ::settings::SettingsPageUIHandler,
// Code common between the discovered and manual add printer code paths.
void OnAddedOrEditedPrinterCommon(const chromeos::Printer& printer,
- PrinterSetupResult result_code,
- bool is_automatic);
+ PrinterSetupResult result_code);
// CupsPrintersManager::Observer override:
void OnPrintersChanged(
@@ -268,7 +267,7 @@ class CupsPrintersHandler : public ::settings::SettingsPageUIHandler,
const std::string& callback_id,
const chromeos::CupsPrinterStatus& printer_status);
- raw_ptr<Profile, ExperimentalAsh> profile_;
+ raw_ptr<Profile, DanglingUntriaged | ExperimentalAsh> profile_;
// Discovery support. discovery_active_ tracks whether or not the UI
// currently wants updates about printer availability. The two vectors track
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler_unittest.cc
index a61d9b1d887..cd2751ebc0d 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/cups_printers_handler_unittest.cc
@@ -126,7 +126,7 @@ class FakeSelectFileDialog : public ui::SelectFileDialog {
bool IsRunning(gfx::NativeWindow owning_window) const override {
return true;
}
- void ListenerDestroyed() override {}
+ void ListenerDestroyed() override { listener_ = nullptr; }
bool HasMultipleFileTypeChoicesImpl() override { return false; }
void VerifyExtensions(const FileTypeInfo* file_types) {
@@ -274,7 +274,8 @@ class CupsPrintersHandlerTest : public testing::Test {
base::RunLoop run_loop_;
scoped_refptr<printing::TestPrintBackend> print_backend_ =
base::MakeRefCounted<printing::TestPrintBackend>();
- raw_ptr<MockNewWindowDelegate, ExperimentalAsh> new_window_delegate_primary_;
+ raw_ptr<MockNewWindowDelegate, DanglingUntriaged | ExperimentalAsh>
+ new_window_delegate_primary_;
std::unique_ptr<TestNewWindowDelegateProvider> new_window_provider_;
base::ScopedTempDir download_dir_;
base::HistogramTester histogram_tester_;
@@ -297,8 +298,8 @@ TEST_F(CupsPrintersHandlerTest, RemoveCorrectPrinter) {
Printer printer("id");
printers_manager_.SavePrinter(printer);
- printers_manager_.SetUpPrinter(printer, base::DoNothing());
- printers_manager_.PrinterInstalled(printer, /*is_automatic=*/true);
+ printers_manager_.SetUpPrinter(printer, /*is_automatic_installation=*/true,
+ base::DoNothing());
const std::string remove_list = R"(
[")" + printer.id() + R"(", "Test Printer 1"]
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/date_time_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/date_time_section.cc
index d73d623deba..43a3ff8c64b 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/date_time_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/date_time_section.cc
@@ -4,20 +4,20 @@
#include "chrome/browser/ui/webui/settings/ash/date_time_section.h"
+#include "ash/constants/ash_features.h"
#include "base/no_destructor.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/system/timezone_util.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/date_time_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "chromeos/ash/components/settings/system_settings_provider.h"
#include "chromeos/ash/components/settings/timezone_settings.h"
-#include "components/user_manager/user_manager.h"
#include "content/public/browser/web_ui_data_source.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
@@ -26,6 +26,7 @@ namespace ash::settings {
namespace mojom {
using ::chromeos::settings::mojom::kDateAndTimeSectionPath;
+using ::chromeos::settings::mojom::kSystemPreferencesSectionPath;
using ::chromeos::settings::mojom::kTimeZoneSubpagePath;
using ::chromeos::settings::mojom::Section;
using ::chromeos::settings::mojom::Setting;
@@ -34,16 +35,18 @@ using ::chromeos::settings::mojom::Subpage;
namespace {
-const std::vector<SearchConcept>& GetDateTimeSearchConcepts() {
+const std::vector<SearchConcept>& GetDateTimeSearchConcepts(
+ mojom::Section section,
+ const char* section_path) {
static const base::NoDestructor<std::vector<SearchConcept>> tags({
{IDS_OS_SETTINGS_TAG_DATE_TIME,
- mojom::kDateAndTimeSectionPath,
+ section_path,
mojom::SearchResultIcon::kClock,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSection,
- {.section = mojom::Section::kDateAndTime}},
+ {.section = section}},
{IDS_OS_SETTINGS_TAG_DATE_TIME_MILITARY_CLOCK,
- mojom::kDateAndTimeSectionPath,
+ section_path,
mojom::SearchResultIcon::kClock,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
@@ -66,10 +69,11 @@ const std::vector<SearchConcept>& GetFineGrainedTimeZoneSearchConcepts() {
return *tags;
}
-const std::vector<SearchConcept>& GetNoFineGrainedTimeZoneSearchConcepts() {
+const std::vector<SearchConcept>& GetNoFineGrainedTimeZoneSearchConcepts(
+ const char* section_path) {
static const base::NoDestructor<std::vector<SearchConcept>> tags({
{IDS_OS_SETTINGS_TAG_DATE_TIME_ZONE,
- mojom::kDateAndTimeSectionPath,
+ section_path,
mojom::SearchResultIcon::kClock,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
@@ -89,18 +93,24 @@ DateTimeSection::DateTimeSection(Profile* profile,
SearchTagRegistry* search_tag_registry)
: OsSettingsSection(profile, search_tag_registry) {
SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
- updater.AddSearchTags(GetDateTimeSearchConcepts());
- if (IsFineGrainedTimeZoneEnabled())
+ const char* section_path = GetSectionPath();
+ updater.AddSearchTags(GetDateTimeSearchConcepts(GetSection(), section_path));
+
+ if (IsFineGrainedTimeZoneEnabled()) {
updater.AddSearchTags(GetFineGrainedTimeZoneSearchConcepts());
- else
- updater.AddSearchTags(GetNoFineGrainedTimeZoneSearchConcepts());
+ } else {
+ updater.AddSearchTags(GetNoFineGrainedTimeZoneSearchConcepts(section_path));
+ }
}
DateTimeSection::~DateTimeSection() = default;
void DateTimeSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
{"dateTimePageTitle", IDS_SETTINGS_DATE_TIME},
{"timeZone", IDS_SETTINGS_TIME_ZONE},
{"selectTimeZoneResolveMethod",
@@ -115,7 +125,9 @@ void DateTimeSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
{"setTimeZoneAutomaticallyOff",
IDS_SETTINGS_TIME_ZONE_DETECTION_CHOOSE_FROM_LIST},
{"setTimeZoneAutomaticallyIpOnlyDefault",
- IDS_SETTINGS_TIME_ZONE_DETECTION_MODE_IP_ONLY_DEFAULT},
+ kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_TIME_ZONE_DETECTION_MODE_IP_ONLY_DEFAULT
+ : IDS_SETTINGS_TIME_ZONE_DETECTION_MODE_IP_ONLY_DEFAULT},
{"setTimeZoneAutomaticallyWithWiFiAccessPointsData",
IDS_SETTINGS_TIME_ZONE_DETECTION_MODE_SEND_WIFI_AP},
{"setTimeZoneAutomaticallyWithAllLocationInfo",
@@ -148,9 +160,6 @@ void DateTimeSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
html_source->AddString(
"timeZoneID",
system::TimezoneSettings::GetInstance()->GetCurrentTimezoneID());
-
- bool is_child = user_manager::UserManager::Get()->GetActiveUser()->IsChild();
- html_source->AddBoolean("isChild", is_child);
}
void DateTimeSection::AddHandlers(content::WebUI* web_ui) {
@@ -162,15 +171,19 @@ int DateTimeSection::GetSectionNameMessageId() const {
}
mojom::Section DateTimeSection::GetSection() const {
- return mojom::Section::kDateAndTime;
+ return ash::features::IsOsSettingsRevampWayfindingEnabled()
+ ? mojom::Section::kSystemPreferences
+ : mojom::Section::kDateAndTime;
}
mojom::SearchResultIcon DateTimeSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kClock;
}
-std::string DateTimeSection::GetSectionPath() const {
- return mojom::kDateAndTimeSectionPath;
+const char* DateTimeSection::GetSectionPath() const {
+ return ash::features::IsOsSettingsRevampWayfindingEnabled()
+ ? mojom::kSystemPreferencesSectionPath
+ : mojom::kDateAndTimeSectionPath;
}
bool DateTimeSection::LogMetric(mojom::Setting setting,
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/date_time_section.h b/chromium/chrome/browser/ui/webui/settings/ash/date_time_section.h
index 5e4cfa4aff3..93288e60232 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/date_time_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/date_time_section.h
@@ -22,14 +22,13 @@ class DateTimeSection : public OsSettingsSection {
DateTimeSection(Profile* profile, SearchTagRegistry* search_tag_registry);
~DateTimeSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/device_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/device_section.cc
index fc0fb553b24..f5ab7aba954 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/device_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/device_section.cc
@@ -11,6 +11,7 @@
#include "ash/public/cpp/stylus_utils.h"
#include "ash/shell.h"
#include "ash/system/power/adaptive_charging_controller.h"
+#include "ash/system/power/battery_saver_controller.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
@@ -19,13 +20,13 @@
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/login/demo_mode/demo_session.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/device_display_handler.h"
#include "chrome/browser/ui/webui/settings/ash/device_keyboard_handler.h"
#include "chrome/browser/ui/webui/settings/ash/device_pointer_handler.h"
#include "chrome/browser/ui/webui/settings/ash/device_power_handler.h"
#include "chrome/browser/ui/webui/settings/ash/device_stylus_handler.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_features_util.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/url_constants.h"
@@ -48,6 +49,9 @@ namespace ash::settings {
namespace mojom {
using ::chromeos::settings::mojom::kAudioSubpagePath;
+using ::chromeos::settings::mojom::kCustomizeMouseButtonsSubpagePath;
+using ::chromeos::settings::mojom::kCustomizePenButtonsSubpagePath;
+using ::chromeos::settings::mojom::kCustomizeTabletButtonsSubpagePath;
using ::chromeos::settings::mojom::kDeviceSectionPath;
using ::chromeos::settings::mojom::kDisplaySubpagePath;
using ::chromeos::settings::mojom::kExternalStorageSubpagePath;
@@ -321,32 +325,6 @@ const std::vector<SearchConcept>& GetPerDeviceTouchpadSearchConcepts() {
return *tags;
}
-const std::vector<SearchConcept>&
-GetTouchpadScrollAccelerationSearchConcepts() {
- static const base::NoDestructor<std::vector<SearchConcept>> tags({
- {IDS_OS_SETTINGS_TAG_TOUCHPAD_SCROLL_ACCELERATION,
- mojom::kPointersSubpagePath,
- mojom::SearchResultIcon::kLaptop,
- mojom::SearchResultDefaultRank::kMedium,
- mojom::SearchResultType::kSetting,
- {.setting = mojom::Setting::kTouchpadScrollAcceleration}},
- });
- return *tags;
-}
-
-const std::vector<SearchConcept>&
-GetPerDeviceTouchpadScrollAccelerationSearchConcepts() {
- static const base::NoDestructor<std::vector<SearchConcept>> tags({
- {IDS_OS_SETTINGS_TAG_TOUCHPAD_SCROLL_ACCELERATION,
- mojom::kPerDeviceTouchpadSubpagePath,
- mojom::SearchResultIcon::kLaptop,
- mojom::SearchResultDefaultRank::kMedium,
- mojom::SearchResultType::kSetting,
- {.setting = mojom::Setting::kTouchpadScrollAcceleration}},
- });
- return *tags;
-}
-
const std::vector<SearchConcept>& GetTouchpadHapticSearchConcepts() {
static const base::NoDestructor<std::vector<SearchConcept>> tags({
{IDS_OS_SETTINGS_TAG_TOUCHPAD_HAPTIC_FEEDBACK,
@@ -771,8 +749,17 @@ const std::vector<SearchConcept>& GetPowerWithAdaptiveChargingSearchConcepts() {
return *tags;
}
-bool AreScrollSettingsAllowed() {
- return base::FeatureList::IsEnabled(features::kAllowScrollSettings);
+const std::vector<SearchConcept>& GetPowerWithBatterySaverModeSearchConcepts() {
+ static const base::NoDestructor<std::vector<SearchConcept>> tags({
+ {IDS_OS_SETTINGS_TAG_POWER_BATTERY_SAVER,
+ mojom::kPowerSubpagePath,
+ mojom::SearchResultIcon::kPower,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::SearchResultType::kSetting,
+ {.setting = mojom::Setting::kBatterySaver},
+ {}},
+ });
+ return *tags;
}
bool IsUnifiedDesktopAvailable() {
@@ -803,7 +790,10 @@ bool IsShowForceRespectUiGainsToggleEnabled() {
}
void AddDeviceKeyboardStrings(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString keyboard_strings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString keyboard_strings[] = {
{"builtInKeyboardName", IDS_SETTINGS_BUILT_IN_KEYBOARD_NAME},
{"keyboardEnableAutoRepeat", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_ENABLE},
{"keyboardEnableAutoRepeatSubLabel",
@@ -826,7 +816,9 @@ void AddDeviceKeyboardStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS},
{"keyboardSendInvertedFunctionKeysDescription",
IDS_SETTINGS_KEYBOARD_SEND_INVERTED_FUNCTION_KEYS_DESCRIPTION},
- {"keyboardShowInputSettings", IDS_SETTINGS_KEYBOARD_SHOW_INPUT_SETTINGS},
+ {"keyboardShowInputSettings",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_KEYBOARD_SHOW_INPUT_SETTINGS
+ : IDS_SETTINGS_KEYBOARD_SHOW_INPUT_SETTINGS},
// TODO(crbug.com/1097328): Remove this string, as it is unused.
{"keyboardShowLanguageAndInput",
IDS_SETTINGS_KEYBOARD_SHOW_LANGUAGE_AND_INPUT},
@@ -888,6 +880,22 @@ void AddDeviceKeyboardStrings(content::WebUIDataSource* html_source) {
html_source->AddLocalizedString(
"keyboardSendFunctionKeysDescription",
IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS_LAYOUT2_DESCRIPTION);
+ html_source->AddLocalizedString("sixPackKeyDeleteSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_DELETE_LAUNCHER);
+ html_source->AddLocalizedString("sixPackKeyHomeSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_HOME_LAUNCHER);
+ html_source->AddLocalizedString("sixPackKeyEndSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_END_LAUNCHER);
+ html_source->AddLocalizedString("sixPackKeyPageUpSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_PAGE_UP_LAUNCHER);
+ html_source->AddLocalizedString(
+ "sixPackKeyPageDownSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_PAGE_DOWN_LAUNCHER);
+ html_source->AddLocalizedString("sixPackKeyInsertSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_INSERT_LAUNCHER);
+ html_source->AddLocalizedString(
+ "touchpadSimulateRightClickOptionSearch",
+ IDS_SETTINGS_TOUCHPAD_SIMULATE_RIGHT_CLICK_OPTION_LAUNCHER);
} else {
html_source->AddLocalizedString(
"keyboardBlockMetaFunctionKeyRewrites",
@@ -903,6 +911,21 @@ void AddDeviceKeyboardStrings(content::WebUIDataSource* html_source) {
html_source->AddLocalizedString(
"keyboardSendFunctionKeysDescription",
IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS_DESCRIPTION);
+ html_source->AddLocalizedString("sixPackKeyDeleteSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_DELETE_SEARCH);
+ html_source->AddLocalizedString("sixPackKeyHomeSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_HOME_SEARCH);
+ html_source->AddLocalizedString("sixPackKeyEndSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_END_SEARCH);
+ html_source->AddLocalizedString("sixPackKeyPageUpSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_PAGE_UP_SEARCH);
+ html_source->AddLocalizedString("sixPackKeyPageDownSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_PAGE_DOWN_SEARCH);
+ html_source->AddLocalizedString("sixPackKeyInsertSearch",
+ IDS_SETTINGS_SIX_PACK_KEY_INSERT_SEARCH);
+ html_source->AddLocalizedString(
+ "touchpadSimulateRightClickOptionSearch",
+ IDS_SETTINGS_TOUCHPAD_SIMULATE_RIGHT_CLICK_OPTION_SEARCH);
}
}
@@ -1031,7 +1054,10 @@ void AddDeviceAudioStrings(content::WebUIDataSource* html_source) {
}
void AddDevicePowerStrings(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kPowerStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kPowerStrings[] = {
{"calculatingPower", IDS_SETTINGS_POWER_SOURCE_CALCULATING},
{"powerAdaptiveChargingLabel",
IDS_SETTINGS_POWER_ADAPTIVE_CHARGING_LABEL},
@@ -1045,12 +1071,16 @@ void AddDevicePowerStrings(content::WebUIDataSource* html_source) {
{"powerIdleLabel", IDS_SETTINGS_POWER_IDLE_LABEL},
{"powerIdleWhileChargingAriaLabel",
IDS_SETTINGS_POWER_IDLE_WHILE_CHARGING_ARIA_LABEL},
- {"powerIdleWhileChargingLabel",
- IDS_SETTINGS_POWER_IDLE_WHILE_CHARGING_LABEL},
+ {"powerInactiveWhilePluggedInLabel",
+ kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_POWER_INACTIVE_WHILE_PLUGGED_IN_LABEL
+ : IDS_SETTINGS_POWER_IDLE_WHILE_CHARGING_LABEL},
{"powerIdleWhileOnBatteryAriaLabel",
IDS_SETTINGS_POWER_IDLE_WHILE_ON_BATTERY_ARIA_LABEL},
- {"powerIdleWhileOnBatteryLabel",
- IDS_SETTINGS_POWER_IDLE_WHILE_ON_BATTERY_LABEL},
+ {"powerInactiveWhileOnBatteryLabel",
+ kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_POWER_INACTIVE_WHILE_ON_BATTERY_LABEL
+ : IDS_SETTINGS_POWER_IDLE_WHILE_ON_BATTERY_LABEL},
{"powerLidShutDownLabel", IDS_SETTINGS_POWER_LID_CLOSED_SHUT_DOWN_LABEL},
{"powerLidSignOutLabel", IDS_SETTINGS_POWER_LID_CLOSED_SIGN_OUT_LABEL},
{"powerLidSleepLabel", IDS_SETTINGS_POWER_LID_CLOSED_SLEEP_LABEL},
@@ -1131,6 +1161,14 @@ DeviceSection::DeviceSection(Profile* profile,
->IsAdaptiveChargingSupported()) {
updater.AddSearchTags(GetPowerWithAdaptiveChargingSearchConcepts());
}
+
+ const auto* battery_saver_controller =
+ Shell::Get()->battery_saver_controller();
+ if (battery_saver_controller != nullptr &&
+ battery_saver_controller->IsBatterySaverSupported() &&
+ ash::features::IsBatterySaverAvailable()) {
+ updater.AddSearchTags(GetPowerWithBatterySaverModeSearchConcepts());
+ }
}
// Keyboard/mouse search tags are added/removed dynamically.
@@ -1180,9 +1218,16 @@ DeviceSection::~DeviceSection() {
}
void DeviceSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kDeviceStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kDeviceStrings[] = {
{"devicePageTitle", IDS_SETTINGS_DEVICE_TITLE},
- {"touchPadScrollLabel", IDS_OS_SETTINGS_TOUCHPAD_REVERSE_SCROLL_LABEL},
+ {"touchpadScrollLabel",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_TOUCHPAD_REVERSE_SCROLL_LABEL
+ : IDS_OS_SETTINGS_TOUCHPAD_REVERSE_SCROLL_LABEL},
+ {"touchpadScrollDescription",
+ IDS_OS_SETTINGS_REVAMP_TOUCHPAD_REVERSE_SCROLL_DESCRIPTION},
};
html_source->AddLocalizedStrings(kDeviceStrings);
@@ -1202,6 +1247,7 @@ void DeviceSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
AddDevicePointersStrings(html_source);
AddDeviceGraphicsTabletStrings(html_source);
+ AddCustomizeButtonsPageStrings(html_source);
AddDeviceKeyboardStrings(html_source);
AddDeviceStylusStrings(html_source);
AddDeviceDisplayStrings(html_source);
@@ -1237,7 +1283,7 @@ mojom::SearchResultIcon DeviceSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kLaptop;
}
-std::string DeviceSection::GetSectionPath() const {
+const char* DeviceSection::GetSectionPath() const {
return mojom::kDeviceSectionPath;
}
@@ -1255,6 +1301,18 @@ bool DeviceSection::LogMetric(mojom::Setting setting,
value.GetBool());
return true;
+ case mojom::Setting::kLowBatterySound:
+ base::UmaHistogramBoolean(
+ "ChromeOS.Settings.Device.LowBatterySoundButtonEnabled",
+ value.GetBool());
+ return true;
+
+ case mojom::Setting::kChargingSounds:
+ base::UmaHistogramBoolean(
+ "ChromeOS.Settings.Device.ChargingSoundsButtonEnabled",
+ value.GetBool());
+ return true;
+
default:
return false;
}
@@ -1340,6 +1398,30 @@ void DeviceSection::RegisterHierarchy(HierarchyGenerator* generator) const {
mojom::SearchResultIcon::kStylus,
mojom::SearchResultDefaultRank::kMedium,
mojom::kGraphicsTabletSubpagePath);
+
+ generator->RegisterNestedSubpage(IDS_SETTINGS_CUSTOMIZE_MOUSE_BUTTONS_TITLE,
+ mojom::Subpage::kCustomizeMouseButtons,
+ mojom::Subpage::kPerDeviceMouse,
+ mojom::SearchResultIcon::kMouse,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::kCustomizeMouseButtonsSubpagePath);
+
+ // TODO(yyhyyh@): Add icon for graphics tablet to replace the temporary
+ // stylus icon.
+ generator->RegisterNestedSubpage(
+ IDS_SETTINGS_GRAPHICS_TABLET_CUSTOMIZE_TABLET_BUTTONS_LABEL,
+ mojom::Subpage::kCustomizeTabletButtons,
+ mojom::Subpage::kGraphicsTablet, mojom::SearchResultIcon::kStylus,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::kCustomizeTabletButtonsSubpagePath);
+
+ // TODO(yyhyyh@): Decide whether to use stylus icon or add a new icon.
+ generator->RegisterNestedSubpage(
+ IDS_SETTINGS_GRAPHICS_TABLET_CUSTOMIZE_TABLET_BUTTONS_LABEL,
+ mojom::Subpage::kCustomizePenButtons, mojom::Subpage::kGraphicsTablet,
+ mojom::SearchResultIcon::kStylus,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::kCustomizePenButtonsSubpagePath);
}
// Keyboard.
@@ -1434,27 +1516,17 @@ void DeviceSection::TouchpadExists(bool exists) {
if (!ash::features::IsInputDeviceSettingsSplitEnabled()) {
updater.RemoveSearchTags(GetTouchpadSearchConcepts());
- updater.RemoveSearchTags(GetTouchpadScrollAccelerationSearchConcepts());
if (exists) {
updater.AddSearchTags(GetTouchpadSearchConcepts());
- if (base::FeatureList::IsEnabled(features::kAllowScrollSettings)) {
- updater.AddSearchTags(GetTouchpadScrollAccelerationSearchConcepts());
- }
}
return;
}
updater.RemoveSearchTags(GetPerDeviceTouchpadSearchConcepts());
- updater.RemoveSearchTags(
- GetPerDeviceTouchpadScrollAccelerationSearchConcepts());
if (exists) {
updater.AddSearchTags(GetPerDeviceTouchpadSearchConcepts());
- if (base::FeatureList::IsEnabled((features::kAllowScrollSettings))) {
- updater.AddSearchTags(
- GetPerDeviceTouchpadScrollAccelerationSearchConcepts());
- }
}
}
@@ -1486,7 +1558,7 @@ void DeviceSection::MouseExists(bool exists) {
if (exists) {
updater.AddSearchTags(GetMouseSearchConcepts());
- if (AreScrollSettingsAllowed()) {
+ if (features::IsAllowScrollSettingsEnabled()) {
updater.AddSearchTags(GetMouseScrollAccelerationSearchConcepts());
}
}
@@ -1498,7 +1570,7 @@ void DeviceSection::MouseExists(bool exists) {
if (exists) {
updater.AddSearchTags(GetPerDeviceMouseSearchConcepts());
- if (AreScrollSettingsAllowed()) {
+ if (features::IsAllowScrollSettingsEnabled()) {
updater.AddSearchTags(
GetPerDeviceMouseScrollAccelerationSearchConcepts());
}
@@ -1675,7 +1747,22 @@ void DeviceSection::UpdateStylusSearchTags() {
void DeviceSection::AddDevicePointersStrings(
content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kPointersStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+ const bool kIsAllowMouseScrollSettingsEnabled =
+ features::IsAllowScrollSettingsEnabled();
+
+ webui::LocalizedString kPointersStrings[] = {
+ {"allMiceDisconnectedA11yLabel",
+ IDS_SETTINGS_PER_DEVICE_ALL_MICE_DISCONNECTED_A11Y_LABEL},
+ {"allTouchpadsDisconnectedA11yLabel",
+ IDS_SETTINGS_PER_DEVICE_ALL_TOUCHPADS_DISCONNECTED_A11Y_LABEL},
+ {"allPointingSticksDisconnectedA11yLabel",
+ IDS_SETTINGS_PER_DEVICE_ALL_POINTING_STICKS_DISCONNECTED_A11Y_LABEL},
+ {"deviceConnectedA11yLabel",
+ IDS_SETTINGS_PER_DEVICE_CONNECTED_A11Y_LABEL},
+ {"deviceDisconnectedA11yLabel",
+ IDS_SETTINGS_PER_DEVICE_DISCONNECTED_A11Y_LABEL},
{"mouseTitle", IDS_SETTINGS_MOUSE_TITLE},
{"builtInPointingStickName", IDS_SETTINGS_BUILT_IN_POINTING_STICK_NAME},
{"pointingStickTitle", IDS_SETTINGS_POINTING_STICK_TITLE},
@@ -1683,26 +1770,48 @@ void DeviceSection::AddDevicePointersStrings(
{"touchpadTitle", IDS_SETTINGS_TOUCHPAD_TITLE},
{"mouseAndTouchpadTitle", IDS_SETTINGS_MOUSE_AND_TOUCHPAD_TITLE},
{"touchpadTapToClickEnabledLabel",
- IDS_SETTINGS_TOUCHPAD_TAP_TO_CLICK_ENABLED_LABEL},
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_TOUCHPAD_TAP_TO_CLICK_LABEL
+ : IDS_SETTINGS_TOUCHPAD_TAP_TO_CLICK_ENABLED_LABEL},
+ {"touchpadTapToClickDescription",
+ IDS_OS_SETTINGS_REVAMP_TOUCHPAD_TAP_TO_CLICK_DESCRIPTION},
{"touchpadSpeed", IDS_SETTINGS_TOUCHPAD_SPEED_LABEL},
{"pointerSlow", IDS_SETTINGS_POINTER_SPEED_SLOW_LABEL},
{"pointerFast", IDS_SETTINGS_POINTER_SPEED_FAST_LABEL},
{"mouseScrollSpeed", IDS_SETTINGS_MOUSE_SCROLL_SPEED_LABEL},
{"mouseSpeed", IDS_SETTINGS_MOUSE_SPEED_LABEL},
+ {"cursorSpeed", IDS_SETTINGS_CURSOR_SPEED_LABEL},
{"pointingStickSpeed", IDS_SETTINGS_POINTING_STICK_SPEED_LABEL},
- {"mouseSwapButtons", IDS_SETTINGS_MOUSE_SWAP_BUTTONS_LABEL},
+ {"mouseSwapButtonsLabel",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_MOUSE_SWAP_BUTTONS_LABEL
+ : IDS_SETTINGS_MOUSE_SWAP_BUTTONS_LABEL},
+ {"mouseCursor", IDS_SETTINGS_MOUSE_CURSOR_LABEL},
+ {"mouseScrolling", IDS_SETTINGS_MOUSE_SCROLLING_LABEL},
{"pointingStickPrimaryButton",
IDS_SETTINGS_POINTING_STICK_PRIMARY_BUTTON_LABEL},
{"primaryMouseButtonLeft", IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_LEFT_LABEL},
{"primaryMouseButtonRight",
IDS_SETTINGS_PRIMARY_MOUSE_BUTTON_RIGHT_LABEL},
- {"mouseReverseScroll", IDS_SETTINGS_MOUSE_REVERSE_SCROLL_LABEL},
- {"mouseAccelerationLabel", IDS_SETTINGS_MOUSE_ACCELERATION_LABEL},
+ {"mouseReverseScrollLabel",
+ (kIsRevampEnabled || kIsAllowMouseScrollSettingsEnabled)
+ ? IDS_OS_SETTINGS_REVAMP_MOUSE_REVERSE_SCROLL_LABEL
+ : IDS_SETTINGS_MOUSE_REVERSE_SCROLL_LABEL},
+ {"mouseReverseScrollDescription",
+ IDS_OS_SETTINGS_REVAMP_MOUSE_REVERSE_SCROLL_DESCRIPTION},
+ {"mouseAccelerationLabel",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_MOUSE_ACCELERATION_LABEL
+ : IDS_SETTINGS_MOUSE_ACCELERATION_LABEL},
+ {"mouseAccelerationDescription",
+ IDS_OS_SETTINGS_REVAMP_MOUSE_ACCELERATION_DESCRIPTION},
+ {"cursorAccelerationLabel", IDS_SETTINGS_CURSOR_ACCELERATION_LABEL},
{"mouseScrollAccelerationLabel",
IDS_SETTINGS_MOUSE_SCROLL_ACCELERATION_LABEL},
{"pointingStickAccelerationLabel",
IDS_SETTINGS_POINTING_STICK_ACCELERATION_LABEL},
- {"touchpadAccelerationLabel", IDS_SETTINGS_TOUCHPAD_ACCELERATION_LABEL},
+ {"touchpadAccelerationLabel",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_TOUCHPAD_ACCELERATION_LABEL
+ : IDS_SETTINGS_TOUCHPAD_ACCELERATION_LABEL},
+ {"touchpadAccelerationDescription",
+ IDS_OS_SETTINGS_REVAMP_TOUCHPAD_ACCELERATION_DESCRIPTION},
{"touchpadHapticClickSensitivityLabel",
IDS_SETTINGS_TOUCHPAD_HAPTIC_CLICK_SENSITIVITY_LABEL},
{"touchpadHapticFeedbackTitle",
@@ -1713,42 +1822,81 @@ void DeviceSection::AddDevicePointersStrings(
IDS_SETTINGS_TOUCHPAD_HAPTIC_FIRM_CLICK_LABEL},
{"touchpadHapticLightClickLabel",
IDS_SETTINGS_TOUCHPAD_HAPTIC_LIGHT_CLICK_LABEL},
- {"touchpadScrollAccelerationLabel",
- IDS_SETTINGS_TOUCHPAD_SCROLL_ACCELERATION_LABEL},
- {"touchpadScrollSpeed", IDS_SETTINGS_TOUCHPAD_SCROLL_SPEED_LABEL},
{"touchpadSimulateRightClickLabel",
IDS_SETTINGS_TOUCHPAD_SIMULATE_RIGHT_CLICK_LABEL},
{"touchpadSimulateRightClickOptionAlt",
IDS_SETTINGS_TOUCHPAD_SIMULATE_RIGHT_CLICK_OPTION_ALT},
- {"touchpadSimulateRightClickOptionOff",
- IDS_SETTINGS_TOUCHPAD_SIMULATE_RIGHT_CLICK_OPTION_OFF},
- {"touchpadSimulateRightClickOptionSearch",
- IDS_SETTINGS_TOUCHPAD_SIMULATE_RIGHT_CLICK_OPTION_SEARCH},
+ {"touchpadSimulateRightClickOptionDisabled",
+ IDS_SETTINGS_TOUCHPAD_SIMULATE_RIGHT_CLICK_OPTION_DISABLED},
{"learnMoreLabel", IDS_SETTINGS_LEARN_MORE_LABEL},
{"modifierKeysLabel", IDS_SETTINGS_MODIFIER_KEYS_LABEL},
{"otherKeysLabel", IDS_SETTINGS_OTHER_KEYS_LABEL},
+ {"sixPackKeyLabelInsert", IDS_SETTINGS_SIX_PACK_KEY_INSERT},
+ {"sixPackKeyLabelHome", IDS_SETTINGS_SIX_PACK_KEY_HOME},
+ {"sixPackKeyLabelEnd", IDS_SETTINGS_SIX_PACK_KEY_END},
+ {"sixPackKeyLabelDelete", IDS_SETTINGS_SIX_PACK_KEY_DELETE},
+ {"sixPackKeyLabelPageUp", IDS_SETTINGS_SIX_PACK_KEY_PAGE_UP},
+ {"sixPackKeyLabelPageDown", IDS_SETTINGS_SIX_PACK_KEY_PAGE_DOWN},
+ {"sixPackKeyDeleteAlt", IDS_SETTINGS_SIX_PACK_KEY_DELETE_ALT},
+ {"sixPackKeyHomeAlt", IDS_SETTINGS_SIX_PACK_KEY_HOME_ALT},
+ {"sixPackKeyEndAlt", IDS_SETTINGS_SIX_PACK_KEY_END_ALT},
+ {"sixPackKeyPageUpAlt", IDS_SETTINGS_SIX_PACK_KEY_PAGE_UP_ALT},
+ {"sixPackKeyPageDownAlt", IDS_SETTINGS_SIX_PACK_KEY_PAGE_DOWN_ALT},
+ {"sixPackKeyPageDownSearch", IDS_SETTINGS_SIX_PACK_KEY_PAGE_DOWN_SEARCH},
+ {"sixPackKeyInsertSearch", IDS_SETTINGS_SIX_PACK_KEY_INSERT_SEARCH},
+ {"sixPackKeyDisabled", IDS_SETTINGS_SIX_PACK_KEY_OPTION_DISABLED},
};
html_source->AddLocalizedStrings(kPointersStrings);
html_source->AddString("naturalScrollLearnMoreLink",
GetHelpUrlWithBoard(chrome::kNaturalScrollHelpURL));
+ html_source->AddString(
+ "scrollAccelerationLearnMoreLink",
+ GetHelpUrlWithBoard(chrome::kScrollAccelerationHelpURL));
html_source->AddString("hapticFeedbackLearnMoreLink",
GetHelpUrlWithBoard(chrome::kHapticFeedbackHelpURL));
- html_source->AddBoolean("allowScrollSettings", AreScrollSettingsAllowed());
+ html_source->AddBoolean("allowScrollSettings",
+ features::IsAllowScrollSettingsEnabled());
}
void DeviceSection::AddDeviceGraphicsTabletStrings(
content::WebUIDataSource* html_source) const {
static constexpr webui::LocalizedString kGraphicsTabletStrings[] = {
+ {"customizePenButtonsLabel",
+ IDS_SETTINGS_GRAPHICS_TABLET_CUSTOMIZE_PEN_BUTTONS_LABEL},
+ {"customizeTabletButtonsLabel",
+ IDS_SETTINGS_GRAPHICS_TABLET_CUSTOMIZE_TABLET_BUTTONS_LABEL},
{"tabletTitle", IDS_SETTINGS_GRAPHICS_TABLET_TITLE},
};
html_source->AddLocalizedStrings(kGraphicsTabletStrings);
}
+void DeviceSection::AddCustomizeButtonsPageStrings(
+ content::WebUIDataSource* html_source) const {
+ static constexpr webui::LocalizedString kCustomizeButtonsPageStrings[] = {
+ {"buttonRemappingDialogInputLabel",
+ IDS_SETTINGS_CUSTOMIZE_BUTTONS_RENAMING_DIALOG_INPUT_LABEL},
+ {"buttonRemappingDialogCancelLabel",
+ IDS_SETTINGS_CUSTOMIZE_BUTTONS_DIALOG_CANCEL},
+ {"buttonRemappingDialogSaveLabel",
+ IDS_SETTINGS_CUSTOMIZE_BUTTONS_DIALOG_SAVE},
+ {"buttonRenamingDialogTitle",
+ IDS_SETTINGS_CUSTOMIZE_BUTTONS_RENAMING_DIALOG_TITLE},
+ {"customizeMouseButtonsTitle",
+ IDS_SETTINGS_CUSTOMIZE_MOUSE_BUTTONS_TITLE},
+ {"keyCombinationOptionLabel", IDS_SETTINGS_KEY_COMBINATION_OPTION_LABEL},
+ {"noRemappingOptionLabel", IDS_SETTINGS_NO_REMAPPING_OPTION_LABEL},
+ };
+ html_source->AddLocalizedStrings(kCustomizeButtonsPageStrings);
+}
+
void DeviceSection::AddDeviceDisplayStrings(
content::WebUIDataSource* html_source) const {
- static constexpr webui::LocalizedString kDisplayStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kDisplayStrings[] = {
{"displayAmbientColorTitle", IDS_SETTINGS_DISPLAY_AMBIENT_COLOR_TITLE},
{"displayAmbientColorSubtitle",
IDS_SETTINGS_DISPLAY_AMBIENT_COLOR_SUBTITLE},
@@ -1782,18 +1930,26 @@ void DeviceSection::AddDeviceDisplayStrings(
{"displayOverscanInstructions",
IDS_SETTINGS_DISPLAY_OVERSCAN_INSTRUCTIONS},
{"displayOverscanPageText", IDS_SETTINGS_DISPLAY_OVERSCAN_TEXT},
- {"displayOverscanPageTitle", IDS_SETTINGS_DISPLAY_OVERSCAN_TITLE},
+ {"displayOverscanPageTitle",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_DISPLAY_BOUNDARIES_TITLE
+ : IDS_SETTINGS_DISPLAY_OVERSCAN_TITLE},
{"displayOverscanPosition", IDS_SETTINGS_DISPLAY_OVERSCAN_POSITION},
{"displayOverscanResize", IDS_SETTINGS_DISPLAY_OVERSCAN_RESIZE},
{"displayOverscanReset", IDS_SETTINGS_DISPLAY_OVERSCAN_RESET},
- {"displayOverscanSubtitle", IDS_SETTINGS_DISPLAY_OVERSCAN_SUBTITLE},
+ {"displayOverscanSubtitle",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_DISPLAY_BOUNDARIES_DESCRIPTION
+ : IDS_SETTINGS_DISPLAY_OVERSCAN_SUBTITLE},
{"displayRefreshRateInterlacedMenuItem",
IDS_SETTINGS_DISPLAY_REFRESH_RATE_INTERLACED_MENU_ITEM},
{"displayRefreshRateMenuItem",
IDS_SETTINGS_DISPLAY_REFRESH_RATE_MENU_ITEM},
{"displayRefreshRateSublabel",
- IDS_SETTINGS_DISPLAY_REFRESH_RATE_SUBLABEL},
- {"displayRefreshRateTitle", IDS_SETTINGS_DISPLAY_REFRESH_RATE_TITLE},
+ kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_DISPLAY_REFRESH_RATE_DESCRIPTION
+ : IDS_SETTINGS_DISPLAY_REFRESH_RATE_SUBLABEL},
+ {"displayRefreshRateTitle",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_DISPLAY_REFRESH_RATE_TITLE
+ : IDS_SETTINGS_DISPLAY_REFRESH_RATE_TITLE},
{"displayResolutionInterlacedMenuItem",
IDS_SETTINGS_DISPLAY_RESOLUTION_INTERLACED_MENU_ITEM},
{"displayResolutionMenuItem", IDS_SETTINGS_DISPLAY_RESOLUTION_MENU_ITEM},
@@ -1810,7 +1966,8 @@ void DeviceSection::AddDeviceDisplayStrings(
{"displayScreenTitle", IDS_SETTINGS_DISPLAY_SCREEN},
{"displaySizeSliderMaxLabel", IDS_SETTINGS_DISPLAY_ZOOM_SLIDER_MAXIMUM},
{"displaySizeSliderMinLabel", IDS_SETTINGS_DISPLAY_ZOOM_SLIDER_MINIMUM},
- {"displayTitle", IDS_SETTINGS_DISPLAY_TITLE},
+ {"displayTitle", kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_DISPLAY_TITLE
+ : IDS_SETTINGS_DISPLAY_TITLE},
{"displayTouchCalibrationText",
IDS_SETTINGS_DISPLAY_TOUCH_CALIBRATION_TEXT},
{"displayTouchCalibrationTitle",
@@ -1824,8 +1981,12 @@ void DeviceSection::AddDeviceDisplayStrings(
IDS_SETTINGS_DISPLAY_ZOOM_LOGICAL_RESOLUTION_TEXT},
{"displayZoomNativeLogicalResolutionNativeText",
IDS_SETTINGS_DISPLAY_ZOOM_LOGICAL_RESOLUTION_NATIVE_TEXT},
- {"displayZoomSublabel", IDS_SETTINGS_DISPLAY_ZOOM_SUBLABEL},
- {"displayZoomTitle", IDS_SETTINGS_DISPLAY_ZOOM_TITLE},
+ {"displayZoomLabel", kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_DISPLAY_ZOOM_LABEL
+ : IDS_SETTINGS_DISPLAY_ZOOM_TITLE},
+ {"displayZoomDescription",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_DISPLAY_ZOOM_DESCRIPTION
+ : IDS_SETTINGS_DISPLAY_ZOOM_SUBLABEL},
{"displayZoomValue", IDS_SETTINGS_DISPLAY_ZOOM_VALUE},
};
html_source->AddLocalizedStrings(kDisplayStrings);
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/device_section.h b/chromium/chrome/browser/ui/webui/settings/ash/device_section.h
index ac36287fddb..cacdca163b4 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/device_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/device_section.h
@@ -43,18 +43,18 @@ class DeviceSection : public OsSettingsSection,
PrefService* pref_service);
~DeviceSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
// system::PointerDeviceObserver::Observer:
void TouchpadExists(bool exists) override;
void HapticTouchpadExists(bool exists) override;
@@ -87,6 +87,8 @@ class DeviceSection : public OsSettingsSection,
void AddDevicePointersStrings(content::WebUIDataSource* html_source);
void AddDeviceGraphicsTabletStrings(
content::WebUIDataSource* html_source) const;
+ void AddCustomizeButtonsPageStrings(
+ content::WebUIDataSource* html_source) const;
void AddDeviceDisplayStrings(content::WebUIDataSource* html_source) const;
raw_ptr<PrefService, ExperimentalAsh> pref_service_;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/device_section_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/device_section_unittest.cc
index b0738305488..01845278a0a 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/device_section_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/device_section_unittest.cc
@@ -8,8 +8,8 @@
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_identifier.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler.h b/chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler.h
index b549b9177e8..f6e3cc07d7f 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler.h
@@ -11,7 +11,7 @@
#include "base/scoped_observation.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/ash/arc/session/arc_session_manager_observer.h"
-#include "chrome/browser/ui/webui/settings/ash/calculator/size_calculator.h"
+#include "chrome/browser/ui/webui/ash/settings/calculator/size_calculator.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
#include "chromeos/ash/components/disks/disk_mount_manager.h"
#include "third_party/re2/src/re2/re2.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler_unittest.cc
index a8f9b0a5e03..18262cbb0c1 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/device_storage_handler_unittest.cc
@@ -23,10 +23,9 @@
#include "chrome/browser/ash/arc/test/test_arc_session_manager.h"
#include "chrome/browser/ash/borealis/borealis_prefs.h"
#include "chrome/browser/ash/borealis/testing/features.h"
-#include "chrome/browser/ash/file_manager/fake_disk_mount_manager.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/ui/webui/settings/ash/calculator/size_calculator_test_api.h"
+#include "chrome/browser/ui/webui/ash/settings/calculator/size_calculator_test_api.h"
#include "chrome/browser/ui/webui/settings/ash/device_storage_handler.h"
#include "chrome/browser/ui/webui/settings/ash/device_storage_util.h"
#include "chrome/common/chrome_features.h"
@@ -36,6 +35,8 @@
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/ash/components/dbus/concierge/concierge_client.h"
#include "chromeos/ash/components/dbus/spaced/spaced_client.h"
+#include "chromeos/ash/components/disks/disk_mount_manager.h"
+#include "chromeos/ash/components/disks/fake_disk_mount_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/test/browser_task_environment.h"
@@ -75,7 +76,7 @@ class StorageHandlerTest : public testing::Test {
// The storage handler requires an instance of DiskMountManager,
// ArcServiceManager and ArcSessionManager.
disks::DiskMountManager::InitializeForTesting(
- new file_manager::FakeDiskMountManager);
+ new disks::FakeDiskMountManager);
arc_service_manager_ = std::make_unique<arc::ArcServiceManager>();
arc_session_manager_ = arc::CreateTestArcSessionManager(
std::make_unique<arc::ArcSessionRunner>(
@@ -229,7 +230,7 @@ class StorageHandlerTest : public testing::Test {
ASSERT_EQ(expected_size, stat.st_size);
}
- raw_ptr<StorageHandler, ExperimentalAsh> handler_;
+ raw_ptr<StorageHandler, DanglingUntriaged | ExperimentalAsh> handler_;
std::unique_ptr<content::TestWebUI> web_ui_;
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestingProfileManager> profile_manager_;
@@ -243,7 +244,8 @@ class StorageHandlerTest : public testing::Test {
std::unique_ptr<DriveOfflineSizeTestAPI> drive_offline_size_test_api_;
std::unique_ptr<CrostiniSizeTestAPI> crostini_size_test_api_;
std::unique_ptr<OtherUsersSizeTestAPI> other_users_size_test_api_;
- raw_ptr<MockNewWindowDelegate, ExperimentalAsh> new_window_delegate_primary_;
+ raw_ptr<MockNewWindowDelegate, DanglingUntriaged | ExperimentalAsh>
+ new_window_delegate_primary_;
private:
std::unique_ptr<arc::ArcServiceManager> arc_service_manager_;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.cc
index 7a81be124ab..437c4c2aa75 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.cc
@@ -6,8 +6,8 @@
#include <sstream>
+#include "ash/webui/settings/public/constants/routes.mojom-shared.h"
#include "base/containers/contains.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-shared.h"
#include "chrome/grit/generated_resources.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -72,8 +72,8 @@ mojom::SearchResultIcon FakeOsSettingsSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kWifi;
}
-std::string FakeOsSettingsSection::GetSectionPath() const {
- return std::string();
+const char* FakeOsSettingsSection::GetSectionPath() const {
+ return "";
}
bool FakeOsSettingsSection::LogMetric(mojom::Setting setting,
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.h b/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.h
index 448c69325c4..6f128d72afc 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_section.h
@@ -8,9 +8,9 @@
#include <utility>
#include <vector>
+#include "ash/webui/settings/public/constants/setting.mojom-shared.h"
#include "base/values.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_section.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom-shared.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace ash::settings {
@@ -47,7 +47,7 @@ class FakeOsSettingsSection : public OsSettingsSection {
// These functions return arbitrary dummy values.
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_sections.cc b/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_sections.cc
index 419eacba51a..4fd854826c9 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_sections.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/fake_os_settings_sections.cc
@@ -4,10 +4,10 @@
#include "chrome/browser/ui/webui/settings/ash/fake_os_settings_sections.h"
+#include "ash/webui/settings/public/constants/routes.mojom-shared.h"
#include "base/rand_util.h"
-#include "chrome/browser/ui/webui/settings/ash/constants/constants_util.h"
+#include "chrome/browser/ui/webui/ash/settings/constants/constants_util.h"
#include "chrome/browser/ui/webui/settings/ash/fake_os_settings_section.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-shared.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace ash::settings {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/fast_pair_saved_devices_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/fast_pair_saved_devices_handler_unittest.cc
index 27e7a74fe8a..f02e235bbcb 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/fast_pair_saved_devices_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/fast_pair_saved_devices_handler_unittest.cc
@@ -281,7 +281,9 @@ class FastPairSavedDevicesHandlerTest : public testing::Test {
base::HistogramTester histogram_tester_;
std::unique_ptr<quick_pair::FakeFastPairRepository> fast_pair_repository_;
gfx::Image test_image_;
- raw_ptr<quick_pair::MockFastPairImageDecoder, ExperimentalAsh> mock_decoder_;
+ raw_ptr<quick_pair::MockFastPairImageDecoder,
+ DanglingUntriaged | ExperimentalAsh>
+ mock_decoder_;
std::unique_ptr<content::TestWebUI> test_web_ui_;
std::unique_ptr<TestFastPairSavedDevicesHandler> handler_;
base::WeakPtrFactory<FastPairSavedDevicesHandlerTest> weak_ptr_factory_{this};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_handler_browsertest.cc
index c9ed2bf30cc..abdee5e2349 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_handler_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_handler_browsertest.cc
@@ -5,17 +5,21 @@
#include <initializer_list>
#include "ash/constants/ash_features.h"
+#include "base/files/file_util.h"
+#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
+#include "base/threading/thread_restrictions.h"
#include "chrome/browser/ash/drive/drive_integration_service_browser_test_base.h"
+#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_browser_test_mixin.h"
#include "chrome/browser/ui/webui/settings/ash/files_page/mojom/google_drive_handler.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/os_settings_browser_test_mixin.h"
#include "chrome/test/data/webui/settings/chromeos/test_api.test-mojom-test-utils.h"
#include "chromeos/ash/components/dbus/spaced/fake_spaced_client.h"
#include "chromeos/ash/components/drivefs/fake_drivefs.h"
@@ -28,6 +32,7 @@
using base::test::RunOnceCallback;
using testing::_;
using testing::DoAll;
+using testing::InSequence;
using testing::Return;
namespace ash::settings {
@@ -86,8 +91,10 @@ class GoogleDriveHandlerTest
: public drive::DriveIntegrationServiceBrowserTestBase {
public:
GoogleDriveHandlerTest() : receiver_(&fake_search_query_) {
- scoped_feature_list_.InitWithFeatures({ash::features::kDriveFsBulkPinning},
- {});
+ scoped_feature_list_.InitWithFeatures(
+ {ash::features::kDriveFsBulkPinning,
+ ash::features::kFeatureManagementDriveFsBulkPinning},
+ {});
}
GoogleDriveHandlerTest(const GoogleDriveHandlerTest&) = delete;
@@ -122,6 +129,30 @@ class GoogleDriveHandlerTest
});
}
+ base::FilePath CreateFileInContentCache(int file_size_in_bytes) {
+ auto* const service =
+ drive::util::GetIntegrationServiceByProfile(browser()->profile());
+ {
+ // Ensure the content cache directory exists.
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ if (!base::DirectoryExists(service->GetDriveFsContentCachePath())) {
+ EXPECT_TRUE(
+ base::CreateDirectory(service->GetDriveFsContentCachePath()));
+ }
+ }
+ const base::FilePath file_path =
+ service->GetDriveFsContentCachePath().Append("foo.txt");
+ {
+ // Create a file of `file_size_in_bytes` bytes in the content_cache
+ // directory.
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ EXPECT_TRUE(base::WriteFile(file_path,
+ base::RandBytesAsString(file_size_in_bytes)));
+ }
+
+ return file_path;
+ }
+
protected:
OSSettingsBrowserTestMixin os_settings_mixin_{&mixin_host_};
FakeSearchQuery fake_search_query_;
@@ -138,10 +169,6 @@ IN_PROC_BROWSER_TEST_F(GoogleDriveHandlerTest,
SetUpSearchResultExpectations();
fake_search_query_.SetSearchResults({});
- auto* fake_drivefs = GetFakeDriveFsForProfile(browser()->profile());
- EXPECT_CALL(*fake_drivefs, GetOfflineFilesSpaceUsage(_))
- .WillOnce(RunOnceCallback<0>(drive::FILE_ERROR_OK, 1));
-
// Expect the free space to be 1 GB (1,073,741,824 bytes), the required space
// to be 0 KB (0 items).
int64_t free_space = 1024 * 1024 * 1024;
@@ -160,9 +187,6 @@ IN_PROC_BROWSER_TEST_F(GoogleDriveHandlerTest,
OnlyUnpinnedResultsUpdateTheSpaceRequirements) {
SetUpSearchResultExpectations();
- auto* fake_drivefs = GetFakeDriveFsForProfile(browser()->profile());
- EXPECT_CALL(*fake_drivefs, GetOfflineFilesSpaceUsage(_))
- .WillOnce(RunOnceCallback<0>(drive::FILE_ERROR_OK, 1));
// Each item is 125 MB in size, total required space should be 500 MB.
int64_t file_size = 125 * 1024 * 1024;
@@ -172,94 +196,48 @@ IN_PROC_BROWSER_TEST_F(GoogleDriveHandlerTest,
{{.size = file_size}, {.size = file_size}});
fake_search_query_.SetSearchResults({});
- int64_t free_space = 1024 * 1024 * 1024;
+ int64_t free_space = int64_t(3) << 30; // 3 GB.
auto required_space = FormatBytesToString(file_size * 4);
- auto remaining_space = FormatBytesToString(free_space - (file_size * 4));
-
- ash::FakeSpacedClient::Get()->set_free_disk_space(free_space);
- auto google_drive_settings = OpenGoogleDriveSettings();
- google_drive_settings.AssertBulkPinningSpace(required_space, remaining_space);
-}
-
-IN_PROC_BROWSER_TEST_F(GoogleDriveHandlerTest,
- NegativeRemainingSpaceReturnsEmptyStrings) {
- SetUpSearchResultExpectations();
-
- auto* fake_drivefs = GetFakeDriveFsForProfile(browser()->profile());
- EXPECT_CALL(*fake_drivefs, GetOfflineFilesSpaceUsage(_))
- .WillOnce(RunOnceCallback<0>(drive::FILE_ERROR_OK, 1));
-
- // Each item is 250 MB in size, total required space should be 1 GB.
- int64_t file_size = 250 << 20;
- fake_search_query_.SetSearchResults(
- {{.size = file_size}, {.size = file_size}});
- fake_search_query_.SetSearchResults(
- {{.size = file_size}, {.size = file_size}});
- fake_search_query_.SetSearchResults({});
+ auto free_space_str = FormatBytesToString(free_space);
- // Mock negative remaining space, the required space is 1 GB and the free
- // space is 500 MB, so the remaining space ends up being -500MB. This is
- // indicated by a -1 on the UI layer.
- int64_t free_space = 500 << 20;
ash::FakeSpacedClient::Get()->set_free_disk_space(free_space);
-
auto google_drive_settings = OpenGoogleDriveSettings();
- google_drive_settings.AssertBulkPinningSpace(
- FormatBytesToString(file_size * 4),
- /*remaining_space=*/"-1");
+ google_drive_settings.AssertBulkPinningSpace(required_space, free_space_str);
}
IN_PROC_BROWSER_TEST_F(GoogleDriveHandlerTest,
TotalPinnedSizeUpdatesValueOnElement) {
- SetUpSearchResultExpectations();
-
- // Mock no search results are returned (this avoids the call to
- // `CalculateRequiredSpace` from being ran here).
- fake_search_query_.SetSearchResults({});
- ash::FakeSpacedClient::Get()->set_free_disk_space(1024 * 1024 * 1024);
-
- int64_t pinned_size = 1024 * 1024;
- auto* fake_drivefs = GetFakeDriveFsForProfile(browser()->profile());
- EXPECT_CALL(*fake_drivefs, GetOfflineFilesSpaceUsage(_))
- .WillOnce(RunOnceCallback<0>(drive::FILE_ERROR_OK, pinned_size));
-
- auto google_drive_settings = OpenGoogleDriveSettings();
- google_drive_settings.AssertBulkPinningPinnedSize(
- FormatBytesToString(pinned_size));
-}
-
-IN_PROC_BROWSER_TEST_F(GoogleDriveHandlerTest,
- InvalidSizeUpdatesRemainingSizeToUnknown) {
- SetUpSearchResultExpectations();
-
// Mock no search results are returned (this avoids the call to
// `CalculateRequiredSpace` from being ran here).
fake_search_query_.SetSearchResults({});
- ash::FakeSpacedClient::Get()->set_free_disk_space(1024 * 1024 * 1024);
+ ash::FakeSpacedClient::Get()->set_free_disk_space(int64_t(3) << 30);
- auto* fake_drivefs = GetFakeDriveFsForProfile(browser()->profile());
- EXPECT_CALL(*fake_drivefs, GetOfflineFilesSpaceUsage(_))
- .WillOnce(RunOnceCallback<0>(drive::FILE_ERROR_OK, -1));
+ CreateFileInContentCache(32);
auto google_drive_settings = OpenGoogleDriveSettings();
- google_drive_settings.AssertBulkPinningPinnedSize("Unknown");
+ google_drive_settings.AssertBulkPinningPinnedSize(FormatBytesToString(4096));
}
IN_PROC_BROWSER_TEST_F(GoogleDriveHandlerTest,
ClearingOfflineFilesCallsProperMethods) {
- SetUpSearchResultExpectations();
-
// Mock no search results are returned (this avoids the call to
// `CalculateRequiredSpace` from being ran here).
fake_search_query_.SetSearchResults({});
- ash::FakeSpacedClient::Get()->set_free_disk_space(1024 * 1024 * 1024);
+ ash::FakeSpacedClient::Get()->set_free_disk_space(int64_t(3) << 30);
+
+ const base::FilePath file_path = CreateFileInContentCache(32);
- int64_t pinned_size = 1024 * 1024;
auto* fake_drivefs = GetFakeDriveFsForProfile(browser()->profile());
- EXPECT_CALL(*fake_drivefs, GetOfflineFilesSpaceUsage(_))
- .Times(2)
- .WillOnce(RunOnceCallback<0>(drive::FILE_ERROR_OK, pinned_size))
- .WillOnce(RunOnceCallback<0>(drive::FILE_ERROR_OK, 0));
+ EXPECT_CALL(*fake_drivefs, ClearOfflineFiles(_))
+ .WillOnce(
+ [&file_path](
+ drivefs::mojom::DriveFs::ClearOfflineFilesCallback callback) {
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ ASSERT_TRUE(base::DeleteFile(file_path));
+ }
+ std::move(callback).Run(drive::FILE_ERROR_OK);
+ });
auto google_drive_settings = OpenGoogleDriveSettings();
google_drive_settings.ClickClearOfflineFilesAndAssertNewSize(
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.cc
index a1b42eb4526..2dab6888f28 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.cc
@@ -5,31 +5,35 @@
#include "chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
+#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ui/webui/settings/ash/files_page/mojom/google_drive_handler.mojom.h"
+#include "chromeos/ash/components/drivefs/drivefs_pin_manager.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/text/bytes_formatting.h"
namespace ash::settings {
+namespace {
+using drive::DriveIntegrationService;
+using drivefs::pinning::PinManager;
using drivefs::pinning::Progress;
-namespace {
-
google_drive::mojom::StatusPtr CreateStatusPtr(const Progress& progress) {
auto status = google_drive::mojom::Status::New();
status->required_space =
(progress.required_space >= 0)
? base::UTF16ToUTF8(ui::FormatBytes(progress.required_space))
: "";
- int64_t remaining_space = progress.free_space - progress.required_space;
- status->remaining_space =
- (remaining_space >= 0)
- ? base::UTF16ToUTF8(
- ui::FormatBytes(progress.free_space - progress.required_space))
+ status->free_space =
+ (progress.free_space >= 0)
+ ? base::UTF16ToUTF8(ui::FormatBytes(progress.free_space))
: "";
status->stage = progress.stage;
+ status->listed_files = progress.listed_files;
status->is_error = progress.IsError();
return status;
}
@@ -43,51 +47,48 @@ GoogleDrivePageHandler::GoogleDrivePageHandler(
: profile_(profile),
page_(std::move(page)),
receiver_(this, std::move(receiver)) {
- if (drive::DriveIntegrationService* const drive_service = GetDriveService()) {
- drive_service->AddObserver(this);
+ if (DriveIntegrationService* const service = GetDriveService()) {
+ service->AddObserver(this);
}
}
GoogleDrivePageHandler::~GoogleDrivePageHandler() {
- if (drive::DriveIntegrationService* const drive_service = GetDriveService()) {
- drive_service->RemoveObserver(this);
+ if (DriveIntegrationService* const service = GetDriveService()) {
+ service->RemoveObserver(this);
}
}
void GoogleDrivePageHandler::CalculateRequiredSpace() {
- auto* const pin_manager = GetPinManager();
+ PinManager* const pin_manager = GetPinManager();
if (!pin_manager) {
page_->OnServiceUnavailable();
return;
}
+
NotifyProgress(pin_manager->GetProgress());
- pin_manager->CalculateRequiredSpace();
+ if (!pin_manager->CalculateRequiredSpace()) {
+ page_->OnServiceUnavailable();
+ return;
+ }
}
void GoogleDrivePageHandler::NotifyProgress(const Progress& progress) {
page_->OnProgress(CreateStatusPtr(progress));
}
-drive::DriveIntegrationService* GoogleDrivePageHandler::GetDriveService() {
- drive::DriveIntegrationService* service =
+DriveIntegrationService* GoogleDrivePageHandler::GetDriveService() {
+ DriveIntegrationService* const service =
drive::DriveIntegrationServiceFactory::FindForProfile(profile_);
- if (!service || !service->IsMounted()) {
- return nullptr;
- }
- return service;
+ return service && service->IsMounted() ? service : nullptr;
}
-drivefs::pinning::PinManager* GoogleDrivePageHandler::GetPinManager() {
- drive::DriveIntegrationService* service = GetDriveService();
- if (!service || !service->GetPinManager()) {
- return nullptr;
- }
- return service->GetPinManager();
+PinManager* GoogleDrivePageHandler::GetPinManager() {
+ DriveIntegrationService* const service = GetDriveService();
+ return service ? service->GetPinManager() : nullptr;
}
void GoogleDrivePageHandler::OnBulkPinProgress(const Progress& progress) {
- auto* const pin_manager = GetPinManager();
- if (!pin_manager) {
+ if (!GetPinManager()) {
page_->OnServiceUnavailable();
return;
}
@@ -95,29 +96,29 @@ void GoogleDrivePageHandler::OnBulkPinProgress(const Progress& progress) {
NotifyProgress(progress);
}
-void GoogleDrivePageHandler::GetTotalPinnedSize(
- GetTotalPinnedSizeCallback callback) {
+void GoogleDrivePageHandler::GetContentCacheSize(
+ GetContentCacheSizeCallback callback) {
if (!GetDriveService()) {
page_->OnServiceUnavailable();
std::move(callback).Run(absl::nullopt);
return;
}
- // If Drive crashes, this callback may not get invoked so in that instance
- // ensure it gets invoked with "-1" to signal an error case.
- auto on_total_pinned_size_callback =
- mojo::WrapCallbackWithDefaultInvokeIfNotRun(
- base::BindOnce(&GoogleDrivePageHandler::OnGetTotalPinnedSize,
- weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
- /*size=*/-1);
- GetDriveService()->GetTotalPinnedSize(
- std::move(on_total_pinned_size_callback));
+ const base::FilePath content_cache_path =
+ GetDriveService()->GetDriveFsContentCachePath();
+
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::BindOnce(&drive::util::ComputeDriveFsContentCacheSize,
+ content_cache_path),
+ base::BindOnce(&GoogleDrivePageHandler::OnGetContentCacheSize,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
-void GoogleDrivePageHandler::OnGetTotalPinnedSize(
- GetTotalPinnedSizeCallback callback,
+void GoogleDrivePageHandler::OnGetContentCacheSize(
+ GetContentCacheSizeCallback callback,
int64_t size) {
- if (size == -1) {
+ if (size < 0) {
std::move(callback).Run(absl::nullopt);
return;
}
@@ -148,4 +149,9 @@ void GoogleDrivePageHandler::OnClearPinnedFiles(
std::move(callback).Run();
}
+void GoogleDrivePageHandler::RecordBulkPinningEnabledMetric() {
+ drivefs::pinning::RecordBulkPinningEnabledSource(
+ drivefs::pinning::BulkPinningEnabledSource::kSystemSettings);
+}
+
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.h b/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.h
index 41d8903873e..5db2bdf5993 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler.h
@@ -35,8 +35,9 @@ class GoogleDrivePageHandler : public google_drive::mojom::PageHandler,
private:
// google_drive::mojom::PageHandler:
void CalculateRequiredSpace() override;
- void GetTotalPinnedSize(GetTotalPinnedSizeCallback callback) override;
+ void GetContentCacheSize(GetContentCacheSizeCallback callback) override;
void ClearPinnedFiles(ClearPinnedFilesCallback callback) override;
+ void RecordBulkPinningEnabledMetric() override;
// drive::DriveIntegrationServiceObserver
void OnBulkPinProgress(const drivefs::pinning::Progress& progress) override;
@@ -44,7 +45,8 @@ class GoogleDrivePageHandler : public google_drive::mojom::PageHandler,
void NotifyServiceUnavailable();
void NotifyProgress(const drivefs::pinning::Progress& progress);
- void OnGetTotalPinnedSize(GetTotalPinnedSizeCallback callback, int64_t size);
+ void OnGetContentCacheSize(GetContentCacheSizeCallback callback,
+ int64_t size);
void OnClearPinnedFiles(ClearPinnedFilesCallback callback,
drive::FileError error);
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/files_page/mojom/google_drive_handler.mojom b/chromium/chrome/browser/ui/webui/settings/ash/files_page/mojom/google_drive_handler.mojom
index b9c8a671d2c..2a48e8c347e 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/files_page/mojom/google_drive_handler.mojom
+++ b/chromium/chrome/browser/ui/webui/settings/ash/files_page/mojom/google_drive_handler.mojom
@@ -14,12 +14,15 @@ struct Status {
// pinned.
string required_space;
- // The remaining space is the free disk space minus the `required_space`.
- string remaining_space;
+ // The free space available on the disk.
+ string free_space;
// The current stage the pin manager is going through.
drivefs.pin_manager_types.mojom.Stage stage;
+ // The number of listed files during the `kListingFiles` stage.
+ uint64 listed_files;
+
// Whether the stage returned is an error.
bool is_error;
};
@@ -39,13 +42,15 @@ interface PageHandler {
// method exposed below.
CalculateRequiredSpace();
- // Returns the current size used by all the pinned files for DriveFS as a
- // string. This string includes the units, e.g. 500 MB.
- GetTotalPinnedSize() => (string? size);
+ // Returns the current size of the users GCache content_cache, e.g. 500 MB.
+ GetContentCacheSize() => (string? size);
// Unpins all files then force evicts them from the Drive cache (does not
// force evict hosted documents).
ClearPinnedFiles() => ();
+
+ // Record metric when bulk pinning is enabled.
+ RecordBulkPinningEnabledMetric();
};
// Interface for the Google drive settings page. Implemented in Javascript and
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/files_page/one_drive_page_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/files_page/one_drive_page_handler.cc
index ca6121c228f..80ff9add9ed 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/files_page/one_drive_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/files_page/one_drive_page_handler.cc
@@ -14,7 +14,9 @@
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
#include "chrome/browser/ui/webui/settings/ash/files_page/mojom/one_drive_handler.mojom.h"
+#include "chrome/common/extensions/extension_constants.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace ash::settings {
@@ -22,20 +24,19 @@ namespace ash::settings {
namespace {
void OnGetEmailAddress(
OneDrivePageHandler::GetUserEmailAddressCallback callback,
- const ash::file_system_provider::Actions& actions,
- base::File::Error result) {
- if (result != base::File::Error::FILE_OK) {
- LOG(ERROR) << "Failed to get actions: " << result;
+ base::expected<cloud_upload::ODFSMetadata, base::File::Error>
+ metadata_or_error) {
+ if (!metadata_or_error.has_value()) {
+ LOG(ERROR) << "Failed to get user email: " << metadata_or_error.error();
std::move(callback).Run(absl::nullopt);
return;
}
- for (const file_system_provider::Action& action : actions) {
- if (action.id == cloud_upload::kUserEmailActionId) {
- std::move(callback).Run(action.title);
- return;
- }
+ if (metadata_or_error->user_email.empty()) {
+ LOG(ERROR) << "User email is empty";
+ std::move(callback).Run(absl::nullopt);
+ return;
}
- std::move(callback).Run(absl::nullopt);
+ std::move(callback).Run(metadata_or_error->user_email);
}
void OnShowItemInFolder(
@@ -70,45 +71,21 @@ OneDrivePageHandler::~OneDrivePageHandler() {
void OneDrivePageHandler::GetUserEmailAddress(
GetUserEmailAddressCallback callback) {
- file_system_provider::Service* service =
- file_system_provider::Service::Get(profile_);
- file_system_provider::ProviderId provider_id =
- file_system_provider::ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile_));
- std::vector<file_system_provider::ProvidedFileSystemInfo>
- odfs_file_system_infos =
- service->GetProvidedFileSystemInfoList(provider_id);
- if (odfs_file_system_infos.size() == 0) {
- // ODFS is not mounted.
+ file_system_provider::ProvidedFileSystemInterface* file_system =
+ cloud_upload::GetODFS(profile_);
+ if (!file_system) {
std::move(callback).Run(absl::nullopt);
return;
}
- if (odfs_file_system_infos.size() != 1u) {
- LOG(ERROR) << "One and only one filesystem should be mounted for the ODFS "
- "extension";
- std::move(callback).Run(absl::nullopt);
- return;
- }
- auto* file_system = service->GetProvidedFileSystem(
- provider_id, odfs_file_system_infos[0].file_system_id());
- file_system->GetActions(
- {base::FilePath("/")},
- base::BindOnce(&OnGetEmailAddress, std::move(callback)));
+ cloud_upload::GetODFSMetadata(
+ file_system, base::BindOnce(&OnGetEmailAddress, std::move(callback)));
}
void OneDrivePageHandler::ConnectToOneDrive(
ConnectToOneDriveCallback callback) {
- file_system_provider::Service* service =
- file_system_provider::Service::Get(profile_);
- // First check if OneDrive is already mounted.
- CHECK(service);
- file_system_provider::ProviderId provider_id =
- file_system_provider::ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile_));
- std::vector<file_system_provider::ProvidedFileSystemInfo>
- odfs_file_system_infos =
- service->GetProvidedFileSystemInfoList(provider_id);
- if (odfs_file_system_infos.size() > 0) {
+ absl::optional<file_system_provider::ProvidedFileSystemInfo>
+ odfs_file_system_info = cloud_upload::GetODFSInfo(profile_);
+ if (odfs_file_system_info.has_value()) {
// ODFS is already mounted.
std::move(callback).Run(false);
return;
@@ -126,43 +103,29 @@ void OneDrivePageHandler::ConnectToOneDrive(
void OneDrivePageHandler::DisconnectFromOneDrive(
DisconnectFromOneDriveCallback callback) {
- file_system_provider::Service* service =
- file_system_provider::Service::Get(profile_);
- CHECK(service);
- file_system_provider::ProviderId provider_id =
- file_system_provider::ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile_));
- std::vector<file_system_provider::ProvidedFileSystemInfo>
- odfs_file_system_infos =
- service->GetProvidedFileSystemInfoList(provider_id);
- if (odfs_file_system_infos.size() == 0) {
+ absl::optional<file_system_provider::ProvidedFileSystemInfo> file_system =
+ cloud_upload::GetODFSInfo(profile_);
+ if (!file_system) {
// ODFS is not mounted.
std::move(callback).Run(false);
return;
}
- std::move(callback).Run(
- service->RequestUnmount(odfs_file_system_infos[0].provider_id(),
- odfs_file_system_infos[0].file_system_id()));
+ auto* service = file_system_provider::Service::Get(profile_);
+ std::move(callback).Run(service->RequestUnmount(
+ file_system->provider_id(), file_system->file_system_id()));
}
void OneDrivePageHandler::OpenOneDriveFolder(
OpenOneDriveFolderCallback callback) {
- file_system_provider::Service* service =
- file_system_provider::Service::Get(profile_);
- CHECK(service);
- file_system_provider::ProviderId provider_id =
- file_system_provider::ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile_));
- std::vector<file_system_provider::ProvidedFileSystemInfo>
- odfs_file_system_infos =
- service->GetProvidedFileSystemInfoList(provider_id);
- if (odfs_file_system_infos.size() == 0) {
+ absl::optional<file_system_provider::ProvidedFileSystemInfo> file_system =
+ cloud_upload::GetODFSInfo(profile_);
+ if (!file_system) {
// ODFS is not mounted.
std::move(callback).Run(false);
return;
}
file_manager::util::ShowItemInFolder(
- profile_, odfs_file_system_infos[0].mount_path(),
+ profile_, file_system->mount_path(),
base::BindOnce(&OnShowItemInFolder, std::move(callback)));
}
@@ -172,7 +135,7 @@ void OneDrivePageHandler::OnProvidedFileSystemMount(
base::File::Error error) {
file_system_provider::ProviderId odfs_provider_id =
file_system_provider::ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile_));
+ extension_misc::kODFSExtensionId);
// Only observe successful mount events for ODFS.
if (file_system_info.provider_id() != odfs_provider_id ||
error != base::File::FILE_OK) {
@@ -186,7 +149,7 @@ void OneDrivePageHandler::OnProvidedFileSystemUnmount(
base::File::Error error) {
file_system_provider::ProviderId odfs_provider_id =
file_system_provider::ProviderId::CreateFromExtensionId(
- file_manager::file_tasks::GetODFSExtensionId(profile_));
+ extension_misc::kODFSExtensionId);
// Only observe successful unmount events for ODFS.
if (file_system_info.provider_id() != odfs_provider_id ||
error != base::File::FILE_OK) {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/files_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/files_section.cc
index e24667a82d9..5988695b7a9 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/files_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/files_section.cc
@@ -10,10 +10,10 @@
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
-#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
+#include "chrome/browser/chromeos/upload_office_to_cloud/upload_office_to_cloud.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/ash/smb_shares/smb_handler.h"
#include "chrome/browser/ui/webui/ash/smb_shares/smb_shares_localized_strings_provider.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
@@ -92,10 +92,12 @@ FilesSection::FilesSection(Profile* profile,
: OsSettingsSection(profile, search_tag_registry) {
SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
updater.AddSearchTags(GetFilesSearchConcepts());
- if (cloud_upload::IsEligibleAndEnabledUploadOfficeToCloud(profile)) {
+ if (chromeos::IsEligibleAndEnabledUploadOfficeToCloud(profile)) {
updater.AddSearchTags(GetFilesOfficeSearchConcepts());
}
- if (drive::util::IsDriveFsBulkPinningEnabled(profile)) {
+ if (drive::util::IsDriveFsBulkPinningEnabled(profile) ||
+ base::FeatureList::IsEnabled(
+ ash::features::kFilesGoogleDriveSettingsPage)) {
updater.AddSearchTags(GetFilesGoogleDriveSearchConcepts());
}
}
@@ -106,41 +108,71 @@ void FilesSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
static constexpr webui::LocalizedString kLocalizedStrings[] = {
{"disconnectGoogleDriveAccount", IDS_SETTINGS_DISCONNECT_GOOGLE_DRIVE},
{"googleDriveLabel", IDS_SETTINGS_GOOGLE_DRIVE},
- {"googleDriveDisabledLabel", IDS_SETTINGS_GOOGLE_DRIVE_DISABLED},
- {"googleDriveDisconnectLabel", IDS_SETTINGS_GOOGLE_DRIVE_DISCONNECT},
{"googleDriveConnectLabel", IDS_SETTINGS_GOOGLE_DRIVE_CONNECT},
- {"googleDriveOfflineTitle", IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_TITLE},
- {"googleDriveOfflineSubtitle",
- IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_SUBTITLE},
+ {"googleDriveRemoveAccessDialogTitle",
+ IDS_SETTINGS_GOOGLE_DRIVE_REMOVE_ACCESS_DIALOG_TITLE},
+ {"googleDriveRemoveAccessDialogBody",
+ IDS_SETTINGS_GOOGLE_DRIVE_REMOVE_ACCESS_DIALOG_BODY},
+ {"googleDriveRemoveButtonText",
+ IDS_SETTINGS_GOOGLE_DRIVE_REMOVE_BUTTON_TEXT},
+ {"googleDriveRemoveDriveAccessButtonText",
+ IDS_SETTINGS_GOOGLE_DRIVE_REMOVE_ACCESS_BUTTON_LABEL},
+ {"googleDriveFileSyncTitle", IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_TITLE},
+ {"googleDriveFileSyncSubtitleWithStorage",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_SUBTITLE_WITH_STORAGE},
+ {"googleDriveFileSyncSubtitleWithoutStorage",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_SUBTITLE_WITHOUT_STORAGE},
{"googleDriveOfflineStorageTitle",
IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_STORAGE_TITLE},
- {"googleDriveOfflineSpaceSubtitle",
- IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_STORAGE_REQUIRED_SUBTITLE},
+ {"googleDriveOfflineStorageSpaceTaken",
+ IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_STORAGE_SPACE_TAKEN},
{"googleDriveOfflineClearCalculatingSubtitle",
IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAR_CALCULATING_SUBTITLE},
{"googleDriveOfflineClearErrorSubtitle",
IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAR_ERROR_SUBTITLE},
- {"googleDriveOfflineClearAction",
- IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAR_ACTION},
- {"googleDriveOfflineClearDialogTitle",
- IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAR_DIALOG_TITLE},
- {"googleDriveOfflineClearDialogBody",
- IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAR_DIALOG_BODY},
- {"googleDriveClearStorageDisabledTooltip",
- IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAR_DISABLED_TOOLTIP},
+ {"googleDriveCleanUpStorageAction",
+ IDS_SETTINGS_GOOGLE_DRIVE_CLEAN_UP_STORAGE_ACTION},
+ {"googleDriveOfflineCleanStorageDialogTitle",
+ IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAN_UP_STORAGE_TITLE},
+ {"googleDriveOfflineCleanStorageDialogBody",
+ IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAN_UP_STORAGE_BODY},
+ {"googleDriveCleanUpStorageDisabledTooltip",
+ IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAN_UP_STORAGE_DISABLED_TOOLTIP},
+ {"googleDriveCleanUpStorageDisabledUnknownStorageTooltip",
+ IDS_SETTINGS_GOOGLE_DRIVE_OFFLINE_CLEAN_UP_STORAGE_DISABLED_UNKNOWN_STORAGE_TOOLTIP},
{"googleDriveTurnOffLabel",
IDS_SETTINGS_GOOGLE_DRIVE_TURN_OFF_BUTTON_LABEL},
- {"googleDriveTurnOffTitle",
- IDS_SETTINGS_GOOGLE_DRIVE_TURN_OFF_TITLE_TEXT},
+ {"googleDriveFileSyncTurnOffTitle",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_TURN_OFF_TITLE_TEXT},
+ {"googleDriveFileSyncTurnOffBody",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_TURN_OFF_BODY_TEXT},
+ {"googleDriveFileSyncListingFilesTitle",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_LISTING_FILES_TITLE_TEXT},
+ {"googleDriveFileSyncListingFilesBody",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_LISTING_FILES_BODY_TEXT},
+ {"googleDriveFileSyncListingFilesItemsFoundBody",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_LISTING_FILES_ITEMS_FOUND_BODY_TEXT},
{"googleDriveNotEnoughSpaceTitle",
IDS_SETTINGS_GOOGLE_DRIVE_BULK_PINNING_NOT_ENOUGH_SPACE_TITLE_TEXT},
{"googleDriveNotEnoughSpaceBody",
IDS_SETTINGS_GOOGLE_DRIVE_BULK_PINNING_NOT_ENOUGH_SPACE_BODY_TEXT},
- {"googleDriveUnexpectedErrorTitle",
- IDS_SETTINGS_GOOGLE_DRIVE_BULK_PINNING_UNEXPECTED_ERROR_TITLE_TEXT},
- {"googleDriveUnexpectedErrorBody",
- IDS_SETTINGS_GOOGLE_DRIVE_BULK_PINNING_UNEXPECTED_ERROR_BODY_TEXT},
- {"googleDriveTurnOffBody", IDS_SETTINGS_GOOGLE_DRIVE_TURN_OFF_BODY_TEXT},
+ {"googleDriveFileSyncUnexpectedErrorTitle",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_UNEXPECTED_ERROR_TITLE_TEXT},
+ {"googleDriveFileSyncUnexpectedErrorBody",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_UNEXPECTED_ERROR_BODY_TEXT},
+ {"googleDriveFileSyncOfflineErrorTitle",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_OFFLINE_ERROR_TITLE_TEXT},
+ {"googleDriveFileSyncOfflineErrorBody",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_OFFLINE_ERROR_BODY_TEXT},
+ {"googleDriveDismissButtonText",
+ IDS_SETTINGS_GOOGLE_DRIVE_DISMISS_BUTTON_TEXT},
+ {"googleDriveOkButtonText", IDS_SETTINGS_GOOGLE_DRIVE_OK_BUTTON_TEXT},
+ {"googleDriveNotSignedInSublabel",
+ IDS_SETTINGS_GOOGLE_DRIVE_NOT_SIGNED_IN_SUBLABEL},
+ {"googleDriveFileSyncOnSublabel",
+ IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_ON_SUBLABEL},
+ {"googleDriveEnabledOnMeteredNetworkLabel",
+ IDS_SETTINGS_GOOGLE_DRIVE_ENABLED_ON_METERED_NETWORK_LABEL},
{"filesPageTitle", IDS_OS_SETTINGS_FILES},
{"smbSharesTitle", IDS_SETTINGS_DOWNLOADS_SMB_SHARES},
{"smbSharesLearnMoreLabel",
@@ -177,7 +209,8 @@ void FilesSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
IDS_SETTINGS_ALWAYS_MOVE_OFFICE_TO_DRIVE_PREFERENCE_LABEL},
{"alwaysMoveToOneDrivePreferenceLabel",
IDS_SETTINGS_ALWAYS_MOVE_OFFICE_TO_ONEDRIVE_PREFERENCE_LABEL},
- };
+ {"smbSharesTitleDescription",
+ IDS_OS_SETTINGS_REVAMP_DOWNLOADS_SMB_SHARES_DESCRIPTION}};
html_source->AddLocalizedStrings(kLocalizedStrings);
smb_dialog::AddLocalizedStrings(html_source);
@@ -185,9 +218,17 @@ void FilesSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
html_source->AddString("smbSharesLearnMoreURL",
GetHelpUrlWithBoard(chrome::kSmbSharesLearnMoreURL));
+ html_source->AddString(
+ "googleDriveCleanUpStorageLearnMoreLink",
+ GetHelpUrlWithBoard(chrome::kGoogleDriveCleanUpStorageLearnMoreURL));
+
+ html_source->AddString(
+ "googleDriveFileSyncLearnMoreLink",
+ GetHelpUrlWithBoard(chrome::kGoogleDriveOfflineLearnMoreURL));
+
html_source->AddBoolean(
"showOfficeSettings",
- cloud_upload::IsEligibleAndEnabledUploadOfficeToCloud(profile()));
+ chromeos::cloud_upload::IsMicrosoftOfficeCloudUploadAllowed(profile()));
const user_manager::User* user =
ProfileHelper::Get()->GetUserByProfile(profile());
@@ -201,14 +242,18 @@ void FilesSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
IDS_SETTINGS_GOOGLE_DRIVE_SIGNED_IN_AS,
base::ASCIIToUTF16(user->GetAccountId().GetUserEmail())));
html_source->AddString(
- "googleDriveDisconnectedFrom",
+ "googleDriveReconnectAs",
l10n_util::GetStringFUTF16(
- IDS_SETTINGS_GOOGLE_DRIVE_DISCONNECTED_FROM,
+ IDS_SETTINGS_GOOGLE_DRIVE_RECONNECT_AS,
base::ASCIIToUTF16(user->GetAccountId().GetUserEmail())));
}
html_source->AddBoolean("enableDriveFsBulkPinning",
drive::util::IsDriveFsBulkPinningEnabled(profile()));
+
+ html_source->AddBoolean("showGoogleDriveSettingsPage",
+ base::FeatureList::IsEnabled(
+ ash::features::kFilesGoogleDriveSettingsPage));
}
void FilesSection::AddHandlers(content::WebUI* web_ui) {
@@ -228,7 +273,7 @@ mojom::SearchResultIcon FilesSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kFolder;
}
-std::string FilesSection::GetSectionPath() const {
+const char* FilesSection::GetSectionPath() const {
return mojom::kFilesSectionPath;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/files_section.h b/chromium/chrome/browser/ui/webui/settings/ash/files_section.h
index d974553652b..49c7e2222d5 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/files_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/files_section.h
@@ -22,14 +22,13 @@ class FilesSection : public OsSettingsSection {
FilesSection(Profile* profile, SearchTagRegistry* search_tag_registry);
~FilesSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/fingerprint_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/fingerprint_handler.cc
index 7da7b1f0d10..c7754100a63 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/fingerprint_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/fingerprint_handler.cc
@@ -7,11 +7,13 @@
#include <algorithm>
#include <memory>
+#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "chrome/browser/ash/login/quick_unlock/auth_token.h"
#include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
@@ -19,6 +21,7 @@
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/grit/generated_resources.h"
+#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/device_service.h"
#include "ui/base/l10n/l10n_util.h"
@@ -137,6 +140,7 @@ void FingerprintHandler::OnSessionFailed() {
}
void FingerprintHandler::OnSessionStateChanged() {
+ TRACE_EVENT0("ui", "FingerprintHandler::OnSessionStateChanged");
SessionState state = SessionManager::Get()->session_state();
FireWebUIListener("on-screen-locked",
@@ -301,13 +305,19 @@ void FingerprintHandler::OnSetRecordLabel(const std::string& callback_id,
}
bool FingerprintHandler::CheckAuthTokenValidity(const std::string& auth_token) {
- quick_unlock::QuickUnlockStorage* quick_unlock_storage =
- quick_unlock::QuickUnlockFactory::GetForProfile(profile_);
- if (!quick_unlock_storage->GetAuthToken())
- return false;
- if (auth_token != quick_unlock_storage->GetAuthToken()->Identifier())
- return false;
- return true;
+ if (ash::features::ShouldUseAuthSessionStorage()) {
+ return ash::AuthSessionStorage::Get()->IsValid(auth_token);
+ } else {
+ quick_unlock::QuickUnlockStorage* quick_unlock_storage =
+ quick_unlock::QuickUnlockFactory::GetForProfile(profile_);
+ if (!quick_unlock_storage->GetAuthToken()) {
+ return false;
+ }
+ if (auth_token != quick_unlock_storage->GetAuthToken()->Identifier()) {
+ return false;
+ }
+ return true;
+ }
}
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/hierarchy.cc b/chromium/chrome/browser/ui/webui/settings/ash/hierarchy.cc
index 9c487cc276a..a8c98f8512d 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/hierarchy.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/hierarchy.cc
@@ -9,7 +9,7 @@
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
-#include "chrome/browser/ui/webui/settings/ash/constants/constants_util.h"
+#include "chrome/browser/ui/webui/ash/settings/constants/constants_util.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_section.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_sections.h"
#include "chrome/grit/generated_resources.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/hierarchy.h b/chromium/chrome/browser/ui/webui/settings/ash/hierarchy.h
index 5467bac998c..8bdb0935e6c 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/hierarchy.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/hierarchy.h
@@ -10,11 +10,11 @@
#include <utility>
#include <vector>
+#include "ash/webui/settings/public/constants/routes.mojom.h"
+#include "ash/webui/settings/public/constants/setting.mojom.h"
#include "base/memory/raw_ptr.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_identifier.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace ash::settings {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/hierarchy_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/hierarchy_unittest.cc
index 36f85388217..21787b21515 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/hierarchy_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/hierarchy_unittest.cc
@@ -6,8 +6,8 @@
#include <sstream>
+#include "ash/webui/settings/public/constants/routes.mojom-shared.h"
#include "chrome/browser/ui/webui/settings/ash/fake_os_settings_sections.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-shared.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash::settings {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
index d0a9fe7b83c..fc4afaf49c0 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
@@ -15,6 +15,7 @@
#include "base/containers/cxx20_erase_vector.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
+#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
@@ -223,6 +224,10 @@ class FakeInputDeviceSettingsController : public InputDeviceSettingsController {
override {
return mojo::Clone(pointing_sticks_);
}
+ std::vector<::ash::mojom::GraphicsTabletPtr> GetConnectedGraphicsTablets()
+ override {
+ return mojo::Clone(graphics_tablets_);
+ }
const ::ash::mojom::KeyboardSettings* GetKeyboardSettings(
DeviceId id) override {
return nullptr;
@@ -268,6 +273,12 @@ class FakeInputDeviceSettingsController : public InputDeviceSettingsController {
::ash::mojom::PointingStickSettingsPtr settings) override {
++num_times_set_pointing_stick_settings_called_;
}
+ // TODO(wangdanny): Implement SetGraphicsTabletSettings.
+ void SetGraphicsTabletSettings(
+ DeviceId id,
+ ::ash::mojom::GraphicsTabletSettingsPtr settings) override {
+ NOTIMPLEMENTED();
+ }
void OnLoginScreenFocusedPodChanged(const AccountId& account_id) override {}
@@ -363,6 +374,7 @@ class FakeInputDeviceSettingsController : public InputDeviceSettingsController {
std::vector<::ash::mojom::TouchpadPtr> touchpads_;
std::vector<::ash::mojom::MousePtr> mice_;
std::vector<::ash::mojom::PointingStickPtr> pointing_sticks_;
+ std::vector<::ash::mojom::GraphicsTabletPtr> graphics_tablets_;
::ash::mojom::KeyboardPoliciesPtr keyboard_policies_ =
::ash::mojom::KeyboardPolicies::New();
::ash::mojom::MousePoliciesPtr mouse_policies_ =
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/internet_handler.h b/chromium/chrome/browser/ui/webui/settings/ash/internet_handler.h
index f638cadc99d..5c67ac3ad86 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/internet_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/internet_handler.h
@@ -63,7 +63,7 @@ class InternetHandler
base::Value::List device_names_without_notifications_;
- const raw_ptr<Profile, ExperimentalAsh> profile_;
+ const raw_ptr<Profile, DanglingUntriaged | ExperimentalAsh> profile_;
raw_ptr<tether::GmsCoreNotificationsStateTracker, ExperimentalAsh>
gms_core_notifications_state_tracker_;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/internet_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/internet_section.cc
index f9b48cd10ab..6ac5e4b11e0 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/internet_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/internet_section.cc
@@ -9,16 +9,16 @@
#include "ash/public/cpp/network_config_service.h"
#include "ash/webui/network_ui/network_health_resource_provider.h"
#include "ash/webui/network_ui/traffic_counters_resource_provider.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/ui/webui/ash/cellular_setup/cellular_setup_localized_strings_provider.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/extension_control_handler.h"
#include "chrome/browser/ui/webui/settings/ash/internet_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/url_constants.h"
@@ -32,6 +32,7 @@
#include "content/public/browser/web_ui_data_source.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
+#include "ui/chromeos/devicetype_utils.h"
#include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
#include "ui/chromeos/strings/network/network_element_localized_strings_provider.h"
@@ -714,7 +715,10 @@ InternetSection::InternetSection(Profile* profile,
InternetSection::~InternetSection() = default;
void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ const bool isRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
{"deviceInfoPopupMenuItemTitle",
IDS_SETTINGS_DEVICE_INFO_POPUP_MENU_ITEM_TITLE},
{"deviceInfoPopupEidLabel", IDS_SETTINGS_DEVICE_INFO_EID_LABEL},
@@ -748,7 +752,9 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
IDS_SETTINGS_INTERNET_TOGGLE_WIFI_ACCESSIBILITY_LABEL},
{"knownNetworksAll", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_ALL},
{"knownNetworksButton", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_BUTTON},
- {"knownNetworksMessage", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MESSAGE},
+ {"knownNetworksMessage",
+ isRevampEnabled ? IDS_OS_SETTINGS_REVAMP_INTERNET_KNOWN_NETWORKS_MESSAGE
+ : IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MESSAGE},
{"knownNetworksPreferred",
IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_PREFFERED},
{"knownNetworksMenuAddPreferred",
@@ -805,6 +811,8 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
{"networkMeteredDesc", IDS_SETTINGS_INTERNET_NETWORK_METERED_DESC},
{"networkNameserversLearnMore", IDS_LEARN_MORE},
{"networkPrefer", IDS_SETTINGS_INTERNET_NETWORK_PREFER},
+ {"networkPreferDescription",
+ IDS_OS_SETTINGS_REVAMP_INTERNET_NETWORK_PREFER_DESCRIPTION},
{"networkPrimaryUserControlled",
IDS_SETTINGS_INTERNET_NETWORK_PRIMARY_USER_CONTROLLED},
{"networkA11yManagedByAdministrator",
@@ -839,7 +847,9 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
{"networkSectionProxyExpandA11yLabel",
IDS_SETTINGS_INTERNET_NETWORK_SECTION_PROXY_ACCESSIBILITY_LABEL},
{"networkShared", IDS_SETTINGS_INTERNET_NETWORK_SHARED},
- {"networkSharedOwner", IDS_SETTINGS_INTERNET_NETWORK_SHARED_OWNER},
+ {"networkSharedOwner",
+ isRevampEnabled ? IDS_OS_SETTINGS_REVAMP_INTERNET_NETWORK_SHARED_OWNER
+ : IDS_SETTINGS_INTERNET_NETWORK_SHARED_OWNER},
{"networkSharedNotOwner", IDS_SETTINGS_INTERNET_NETWORK_SHARED_NOT_OWNER},
{"networkVpnBuiltin", IDS_NETWORK_TYPE_VPN_BUILTIN},
{"networkOutOfRange", IDS_SETTINGS_INTERNET_WIFI_NETWORK_OUT_OF_RANGE},
@@ -963,8 +973,12 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
{"hotspotToggleA11yLabel",
IDS_SETTINGS_INTERNET_HOTSPOT_TOGGLE_A11Y_LABEL},
{"hotspotSummaryStateOn", IDS_SETTINGS_INTERNET_HOTSPOT_SUMMARY_STATE_ON},
+ {"hotspotSummaryStateTurningOn",
+ IDS_SETTINGS_INTERNET_HOTSPOT_SUMMARY_STATE_TURNING_ON},
{"hotspotSummaryStateOff",
IDS_SETTINGS_INTERNET_HOTSPOT_SUMMARY_STATE_OFF},
+ {"hotspotSummaryStateTurningOff",
+ IDS_SETTINGS_INTERNET_HOTSPOT_SUMMARY_STATE_TURNING_OFF},
{"hotspotEnabledA11yLabel",
IDS_SETTINGS_INTERNET_HOTSPOT_ENABLED_A11Y_LABEL},
{"hotspotDisabledA11yLabel",
@@ -978,10 +992,13 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
IDS_SETTINGS_INTERNET_HOTSPOT_AUTO_DISABLED_LABEL},
{"hotspotAutoDisableSublabel",
IDS_SETTINGS_INTERNET_HOTSPOT_AUTO_DISABLED_SUBLABEL},
- {"hotspotSettingsTitle", IDS_SETTINGS_INTERNET_HOTSPOT_SETTINGS_TITLE},
{"hotspotConfigNameLabel",
IDS_SETTINGS_INTERNET_HOTSPOT_CONFIG_NAME_LABEL},
{"hotspotConfigNameInfo", IDS_SETTINGS_INTERNET_HOTSPOT_CONFIG_NAME_INFO},
+ {"hotspotConfigNameEmptyInfo",
+ IDS_SETTINGS_INTERNET_HOTSPOT_CONFIG_NAME_EMPTY_INFO},
+ {"hotspotConfigNameTooLongInfo",
+ IDS_SETTINGS_INTERNET_HOTSPOT_CONFIG_NAME_TOO_LONG_INFO},
{"hotspotConfigPasswordLabel",
IDS_SETTINGS_INTERNET_HOTSPOT_CONFIG_PASSWORD_LABEL},
{"hotspotConfigPasswordInfo",
@@ -1106,6 +1123,7 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
"hotspotSubpageSubtitle",
l10n_util::GetStringFUTF16(
IDS_SETTINGS_INTERNET_HOTSPOT_SUBTITLE_WITH_LEARN_MORE_LINK,
+ ui::GetChromeOSDeviceName(),
GetHelpUrlWithBoard(chrome::kInstantTetheringLearnMoreURL)));
html_source->AddString(
"hotspotMobileDataNotSupportedSublabelWithLink",
@@ -1118,9 +1136,14 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
IDS_SETTINGS_INTERNET_HOTSPOT_NO_MOBILE_DATA_SUBLABEL_WITH_LEARN_MORE_LINK,
GetHelpUrlWithBoard(chrome::kInstantTetheringLearnMoreURL)));
html_source->AddString(
+ "hotspotSettingsTitle",
+ l10n_util::GetStringFUTF16(IDS_SETTINGS_INTERNET_HOTSPOT_SETTINGS_TITLE,
+ ui::GetChromeOSDeviceName()));
+ html_source->AddString(
"hotspotSettingsSubtitle",
l10n_util::GetStringFUTF16(
IDS_SETTINGS_INTERNET_HOTSPOT_SETTINGS_SUBTITLE_WITH_LEARN_MORE_LINK,
+ ui::GetChromeOSDeviceName(),
GetHelpUrlWithBoard(chrome::kInstantTetheringLearnMoreURL)));
html_source->AddBoolean("isUserLoggedIn", IsUserLoggedIn());
}
@@ -1142,7 +1165,7 @@ mojom::SearchResultIcon InternetSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kWifi;
}
-std::string InternetSection::GetSectionPath() const {
+const char* InternetSection::GetSectionPath() const {
return mojom::kNetworkSectionPath;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/internet_section.h b/chromium/chrome/browser/ui/webui/settings/ash/internet_section.h
index 822145c95f7..02a7c19c6bb 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/internet_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/internet_section.h
@@ -34,14 +34,13 @@ class InternetSection
InternetSection(Profile* profile, SearchTagRegistry* search_tag_registry);
~InternetSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
@@ -50,6 +49,7 @@ class InternetSection
OsSettingsIdentifier id,
const std::string& url_to_modify) const override;
+ private:
// network_config::CrosNetworkConfigObserver:
void OnActiveNetworksChanged(
std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr>
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.cc
index 11b88f447c1..b54c11a885c 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.cc
@@ -6,8 +6,8 @@
#include "base/no_destructor.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/kerberos_accounts_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
@@ -119,7 +119,7 @@ mojom::SearchResultIcon KerberosSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kAuthKey;
}
-std::string KerberosSection::GetSectionPath() const {
+const char* KerberosSection::GetSectionPath() const {
return mojom::kKerberosSectionPath;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.h b/chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.h
index 9f6741a616a..8c59843170e 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/kerberos_section.h
@@ -30,18 +30,18 @@ class KerberosSection : public OsSettingsSection,
KerberosCredentialsManager* kerberos_credentials_manager);
~KerberosSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
// KerberosCredentialsManager::Observer:
void OnAccountsChanged() override;
void OnKerberosEnabledStateChanged() override;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/languages_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/languages_section.cc
index 268503a4b93..c6d46284c0b 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/languages_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/languages_section.cc
@@ -6,15 +6,16 @@
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ash/input_method/editor_mediator.h"
#include "chrome/browser/ash/input_method/input_method_settings.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_features_util.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-forward.h"
#include "chrome/browser/ui/webui/settings/languages_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
@@ -40,6 +41,7 @@ using ::chromeos::settings::mojom::kJapaneseManageUserDictionarySubpagePath;
using ::chromeos::settings::mojom::kLanguagesAndInputSectionPath;
using ::chromeos::settings::mojom::kLanguagesSubpagePath;
using ::chromeos::settings::mojom::kSmartInputsSubpagePath;
+using ::chromeos::settings::mojom::kSystemPreferencesSectionPath;
using ::chromeos::settings::mojom::Section;
using ::chromeos::settings::mojom::Setting;
using ::chromeos::settings::mojom::Subpage;
@@ -165,10 +167,12 @@ void AddSmartInputsStrings(content::WebUIDataSource* html_source,
{"smartInputsTitle", IDS_SETTINGS_SUGGESTIONS_TITLE},
{"emojiSuggestionTitle", IDS_SETTINGS_SUGGESTIONS_EMOJI_SUGGESTION_TITLE},
{"emojiSuggestionDescription",
- IDS_SETTINGS_SUGGESTIONS_EMOJI_SUGGESTION_DESCRIPTION},
- };
+ IDS_SETTINGS_SUGGESTIONS_EMOJI_SUGGESTION_DESCRIPTION}};
html_source->AddLocalizedStrings(kLocalizedStrings);
+ html_source->AddBoolean(
+ "allowOrca", input_method::EditorMediator::HasInstance() &&
+ input_method::EditorMediator::Get()->IsAllowedForUse());
html_source->AddBoolean("allowEmojiSuggestion", is_emoji_suggestion_allowed);
}
@@ -195,6 +199,8 @@ void AddInputMethodOptionsStrings(
IDS_SETTINGS_INPUT_METHOD_OPTIONS_USER_DICTIONARIES},
{"inputMethodOptionsPrivacySectionTitle",
IDS_SETTINGS_INPUT_METHOD_OPTIONS_PRIVACY},
+ {"inputMethodOptionsVietnameseShorthandTypingTitle",
+ IDS_SETTINGS_INPUT_METHOD_HEADING_SHORTHAND_TYPING},
{"inputMethodOptionsJapaneseAutomaticallySwitchToHalfwidth",
IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_AUTOMATICALLY_SWITCH_TO_HALFWIDTH},
{"inputMethodOptionsJapaneseShiftKeyModeStyle",
@@ -339,6 +345,26 @@ void AddInputMethodOptionsStrings(
IDS_SETTINGS_INPUT_METHOD_OPTIONS_KEYBOARD_DVORAK},
{"inputMethodOptionsColemakKeyboard",
IDS_SETTINGS_INPUT_METHOD_OPTIONS_KEYBOARD_COLEMAK},
+ {"inputMethodOptionsVietnameseModernToneMarkPlacement",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_VIETNAMESE_MODERN_TONE_MARK_PLACEMENT},
+ {"inputMethodOptionsVietnameseModernToneMarkPlacementDescription",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_DESCRIPTION_VIETNAMESE_MODERN_TONE_MARK_PLACEMENT},
+ {"inputMethodOptionsVietnameseFlexibleTyping",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_VIETNAMESE_FLEXIBLE_TYPING},
+ {"inputMethodOptionsVietnameseTelexFlexibleTypingDescription",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_VIETNAMESE_TELEX_FLEXIBLE_TYPING_DESCRIPTION},
+ {"inputMethodOptionsVietnameseVniFlexibleTypingDescription",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_VIETNAMESE_VNI_FLEXIBLE_TYPING_DESCRIPTION},
+ {"inputMethodOptionsVietnameseVniUoHookShortcut",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_VIETNAMESE_VNI_UO_HOOK_SHORTCUT},
+ {"inputMethodOptionsVietnameseTelexUoHookShortcut",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_VIETNAMESE_TELEX_UO_HOOK_SHORTCUT},
+ {"inputMethodOptionsVietnameseTelexWShortcut",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_VIETNAMESE_TELEX_W_SHORTCUT},
+ {"inputMethodOptionsVietnameseShowUnderline",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_VIETNAMESE_SHOW_UNDERLINE},
+ {"inputMethodOptionsVietnameseShowUnderlineDescription",
+ IDS_SETTINGS_INPUT_METHOD_OPTIONS_DESCRIPTION_VIETNAMESE_SHOW_UNDERLINE},
};
html_source->AddLocalizedStrings(kLocalizedStrings);
html_source->AddBoolean("isPhysicalKeyboardAutocorrectAllowed",
@@ -357,6 +383,9 @@ void AddInputMethodOptionsStrings(
html_source->AddBoolean(
"autocorrectEnableByDefault",
base::FeatureList::IsEnabled(features::kAutocorrectByDefault));
+ html_source->AddBoolean(
+ "allowFirstPartyVietnameseInput",
+ base::FeatureList::IsEnabled(features::kFirstPartyVietnameseInput));
}
void AddLanguagesPageStringsV2(content::WebUIDataSource* html_source) {
@@ -515,8 +544,9 @@ LanguagesSection::LanguagesSection(Profile* profile,
if (IsEmojiSuggestionAllowed()) {
updater.AddSearchTags(GetSmartInputsSearchConcepts());
- if (IsEmojiSuggestionAllowed())
+ if (IsEmojiSuggestionAllowed()) {
updater.AddSearchTags(GetEmojiSuggestionSearchConcepts());
+ }
}
updater.AddSearchTags(GetAutoCorrectionSearchConcepts());
}
@@ -557,7 +587,6 @@ void LanguagesSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
AddLanguagesPageStringsV2(html_source);
AddInputPageStringsV2(html_source);
- html_source->AddBoolean("enableLanguageSettingsV2Update2", true);
html_source->AddBoolean(
"onDeviceGrammarCheckEnabled",
base::FeatureList::IsEnabled(features::kOnDeviceGrammarCheck));
@@ -578,15 +607,19 @@ int LanguagesSection::GetSectionNameMessageId() const {
}
mojom::Section LanguagesSection::GetSection() const {
- return mojom::Section::kLanguagesAndInput;
+ return ash::features::IsOsSettingsRevampWayfindingEnabled()
+ ? mojom::Section::kSystemPreferences
+ : mojom::Section::kLanguagesAndInput;
}
mojom::SearchResultIcon LanguagesSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kGlobe;
}
-std::string LanguagesSection::GetSectionPath() const {
- return mojom::kLanguagesAndInputSectionPath;
+const char* LanguagesSection::GetSectionPath() const {
+ return ash::features::IsOsSettingsRevampWayfindingEnabled()
+ ? mojom::kSystemPreferencesSectionPath
+ : mojom::kLanguagesAndInputSectionPath;
}
bool LanguagesSection::LogMetric(mojom::Setting setting,
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/languages_section.h b/chromium/chrome/browser/ui/webui/settings/ash/languages_section.h
index 14a95f5c904..ee245cd2ef0 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/languages_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/languages_section.h
@@ -31,18 +31,18 @@ class LanguagesSection : public OsSettingsSection,
PrefService* pref_service);
~LanguagesSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
bool IsEmojiSuggestionAllowed() const;
bool IsSpellCheckEnabled() const;
void UpdateSpellCheckSearchTags();
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/main_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/main_section.cc
index 16eb1028c62..23a7eaed5cd 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/main_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/main_section.cc
@@ -18,12 +18,12 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/metrics_handler.h"
#include "chrome/browser/ui/webui/plural_string_handler.h"
#include "chrome/browser/ui/webui/policy_indicator_localized_strings_provider.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_features_util.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_hats_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/send_search_feedback_handler.h"
#include "chrome/browser/ui/webui/settings/browser_lifetime_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
@@ -185,7 +185,7 @@ void MainSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
html_source->AddBoolean(
"isKioskModeActive",
user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp());
- html_source->AddBoolean("isChildAccount", profile()->IsChild());
+ html_source->AddBoolean("isChild", IsChildUser());
// Add the System Web App resources for Settings.
html_source->AddResourcePath("icon-192.png", IDR_SETTINGS_LOGO_192);
@@ -199,6 +199,10 @@ void MainSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
"entryPointEnumSize",
static_cast<int>(PersonalizationEntryPoint::kMaxValue) + 1);
+ html_source->AddString(
+ "chromeRefresh2023Attribute",
+ features::IsOsSettingsTestChromeRefresh() ? "chrome-refresh-2023" : "");
+
AddSearchInSettingsStrings(html_source);
AddChromeOSUserStrings(html_source);
AddUpdateRequiredEolStrings(html_source);
@@ -235,9 +239,9 @@ mojom::SearchResultIcon MainSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kMinValue;
}
-std::string MainSection::GetSectionPath() const {
+const char* MainSection::GetSectionPath() const {
NOTIMPLEMENTED();
- return std::string();
+ return "";
}
bool MainSection::LogMetric(mojom::Setting setting, base::Value& value) const {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/main_section.h b/chromium/chrome/browser/ui/webui/settings/ash/main_section.h
index b09037e4207..18d27a1646c 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/main_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/main_section.h
@@ -25,18 +25,18 @@ class MainSection : public OsSettingsSection {
ash::settings::SearchTagRegistry* search_tag_registry);
~MainSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
void AddChromeOSUserStrings(content::WebUIDataSource* html_source);
std::unique_ptr<PluralStringHandler> CreatePluralStringHandler();
};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler.h b/chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler.h
index a47bacf912e..929905c7d13 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler.h
@@ -57,9 +57,11 @@ class MetricsConsentHandler : public ::settings::SettingsPageUIHandler {
// device metrics consent.
bool ShouldUseUserConsent() const;
- const raw_ptr<Profile, ExperimentalAsh> profile_;
- const raw_ptr<metrics::MetricsService, ExperimentalAsh> metrics_service_;
- const raw_ptr<user_manager::UserManager, ExperimentalAsh> user_manager_;
+ const raw_ptr<Profile, DanglingUntriaged | ExperimentalAsh> profile_;
+ const raw_ptr<metrics::MetricsService, DanglingUntriaged | ExperimentalAsh>
+ metrics_service_;
+ const raw_ptr<user_manager::UserManager, DanglingUntriaged | ExperimentalAsh>
+ user_manager_;
// Used for callbacks.
base::WeakPtrFactory<MetricsConsentHandler> weak_ptr_factory_{this};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler_unittest.cc
index 01c08a5d5c6..37d38244b05 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/metrics_consent_handler_unittest.cc
@@ -260,10 +260,12 @@ class MetricsConsentHandlerTest : public testing::Test {
std::unique_ptr<content::TestWebUI> web_ui_;
// MetricsService.
- std::unique_ptr<metrics::MetricsStateManager> test_metrics_state_manager_;
- std::unique_ptr<TestUserMetricsServiceClient> test_metrics_service_client_;
+ // Dangling Pointer Prevention: test_enabled_state_provider_ must be listed
+ // before test_metrics_state_manager_ to avoid a dangling pointer.
std::unique_ptr<metrics::TestEnabledStateProvider>
test_enabled_state_provider_;
+ std::unique_ptr<metrics::MetricsStateManager> test_metrics_state_manager_;
+ std::unique_ptr<TestUserMetricsServiceClient> test_metrics_service_client_;
std::unique_ptr<metrics::MetricsService> test_metrics_service_;
// Set up stubs for StatsReportingController.
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler.cc
index 9aecc0ef152..e195adaf149 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler.cc
@@ -25,6 +25,7 @@
#include "chrome/browser/ui/ash/session_controller_client_impl.h"
#include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
+#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
#include "chromeos/ash/components/phonehub/browser_tabs_model_provider.h"
#include "chromeos/ash/components/phonehub/pref_names.h"
#include "chromeos/ash/components/phonehub/screen_lock_manager.h"
@@ -394,6 +395,8 @@ void MultideviceHandler::HandleShowMultiDeviceSetupDialog(
const base::Value::List& args) {
DCHECK(args.empty());
multidevice_setup::MultiDeviceSetupDialog::Show();
+ ash::phonehub::util::LogMultiDeviceSetupDialogEntryPoint(
+ ash::phonehub::util::MultiDeviceSetupDialogEntrypoint::kSettingsPage);
}
void MultideviceHandler::HandleGetPageContent(const base::Value::List& args) {
@@ -867,11 +870,15 @@ base::Value::Dict MultideviceHandler::GeneratePageContentDataDictionary() {
}
bool MultideviceHandler::IsAuthTokenValid(const std::string& auth_token) {
- Profile* profile = Profile::FromWebUI(web_ui());
- quick_unlock::QuickUnlockStorage* quick_unlock_storage =
- quick_unlock::QuickUnlockFactory::GetForProfile(profile);
- return quick_unlock_storage->GetAuthToken() &&
- auth_token == quick_unlock_storage->GetAuthToken()->Identifier();
+ if (ash::features::ShouldUseAuthSessionStorage()) {
+ return ash::AuthSessionStorage::Get()->IsValid(auth_token);
+ } else {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ quick_unlock::QuickUnlockStorage* quick_unlock_storage =
+ quick_unlock::QuickUnlockFactory::GetForProfile(profile);
+ return quick_unlock_storage->GetAuthToken() &&
+ auth_token == quick_unlock_storage->GetAuthToken()->Identifier();
+ }
}
multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler_unittest.cc
index cf3a042aa9e..b8505426fa8 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_handler_unittest.cc
@@ -156,8 +156,8 @@ class ScopedLacrosOnlyHandle {
crosapi::browser_util::ClearLacrosAvailabilityCacheForTest();
}
- raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_ =
- nullptr;
+ raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh>
+ fake_user_manager_ = nullptr;
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
};
@@ -981,7 +981,8 @@ class MultideviceHandlerTest : public testing::Test {
std::unique_ptr<eche_app::FakeAppsAccessManager> fake_apps_access_manager_;
std::unique_ptr<phonehub::FakeCameraRollManager> fake_camera_roll_manager_;
phonehub::FakeBrowserTabsModelProvider fake_browser_tabs_model_provider_;
- raw_ptr<MockNewWindowDelegate, ExperimentalAsh> new_window_delegate_primary_;
+ raw_ptr<MockNewWindowDelegate, DanglingUntriaged | ExperimentalAsh>
+ new_window_delegate_primary_;
std::unique_ptr<TestNewWindowDelegateProvider> new_window_provider_;
multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.cc
index 6960dfab97f..9c461604030 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.cc
@@ -16,9 +16,9 @@
#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/session_controller_client_impl.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/nearby_share/shared_resources.h"
#include "chrome/browser/ui/webui/settings/ash/multidevice_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
@@ -673,7 +673,7 @@ mojom::SearchResultIcon MultiDeviceSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kPhone;
}
-std::string MultiDeviceSection::GetSectionPath() const {
+const char* MultiDeviceSection::GetSectionPath() const {
return mojom::kMultiDeviceSectionPath;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.h b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.h
index 0ca85083148..c4fe7ca9043 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section.h
@@ -51,19 +51,20 @@ class MultiDeviceSection
eche_app::EcheAppManager* eche_app_manager);
~MultiDeviceSection() override;
- private:
- friend class MultiDeviceSectionTest;
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
+ friend class MultiDeviceSectionTest;
+
// multidevice_setup::MultiDeviceSetupClient::Observer:
void OnHostStatusChanged(
const multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice&
@@ -104,7 +105,8 @@ class MultiDeviceSection
raw_ptr<PrefService, ExperimentalAsh> pref_service_;
PrefChangeRegistrar pref_change_registrar_;
raw_ptr<eche_app::EcheAppManager, ExperimentalAsh> eche_app_manager_;
- raw_ptr<content::WebUIDataSource, ExperimentalAsh> html_source_;
+ raw_ptr<content::WebUIDataSource, DanglingUntriaged | ExperimentalAsh>
+ html_source_;
};
} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section_unittest.cc
index 0fc81080e3a..abd7f434262 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/multidevice_section_unittest.cc
@@ -10,7 +10,7 @@
#include "base/values.h"
#include "chrome/browser/ash/android_sms/android_sms_service_factory.h"
#include "chrome/browser/ash/eche_app/eche_app_manager_factory.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy.h"
@@ -69,6 +69,7 @@ class MockWebUIDataSource : public content::WebUIDataSource {
void EnableReplaceI18nInJS() override {}
std::string GetSource() override { return ""; }
void AddFrameAncestor(const GURL& frame_ancestor) override {}
+ void SetSupportedScheme(base::StringPiece scheme) override {}
};
class MultiDeviceSectionTest : public testing::Test {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.cc
index df71a649194..83ab6d34728 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.cc
@@ -68,7 +68,7 @@ AppNotificationHandler::AppNotificationHandler(
if (ash::MessageCenterAsh::Get()) {
ash::MessageCenterAsh::Get()->AddObserver(this);
}
- Observe(&app_service_proxy_->AppRegistryCache());
+ app_registry_cache_observer_.Observe(&app_service_proxy_->AppRegistryCache());
}
AppNotificationHandler::~AppNotificationHandler() {
@@ -151,7 +151,7 @@ void AppNotificationHandler::NotifyAppChanged(
void AppNotificationHandler::OnAppRegistryCacheWillBeDestroyed(
apps::AppRegistryCache* cache) {
- Observe(nullptr);
+ app_registry_cache_observer_.Reset();
}
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.h b/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.h
index 7d29f734f03..a5b77d78fab 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.h
@@ -7,6 +7,7 @@
#include "ash/public/cpp/message_center_ash.h"
#include "base/memory/raw_ptr.h"
+#include "base/scoped_observation.h"
#include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
#include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
@@ -60,6 +61,10 @@ class AppNotificationHandler
raw_ptr<apps::AppServiceProxy, ExperimentalAsh> app_service_proxy_;
+ base::ScopedObservation<apps::AppRegistryCache,
+ apps::AppRegistryCache::Observer>
+ app_registry_cache_observer_{this};
+
mojo::Receiver<app_notification::mojom::AppNotificationsHandler> receiver_{
this};
};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler_unittest.cc
index 01c3bdb86bd..5b3518c3bba 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler_unittest.cc
@@ -134,7 +134,8 @@ class AppNotificationHandlerTest : public testing::Test {
}
protected:
- raw_ptr<MockNewWindowDelegate, ExperimentalAsh> new_window_delegate_primary_;
+ raw_ptr<MockNewWindowDelegate, DanglingUntriaged | ExperimentalAsh>
+ new_window_delegate_primary_;
AppNotificationHandlerTestObserver* observer() { return observer_.get(); }
@@ -150,8 +151,7 @@ class AppNotificationHandlerTest : public testing::Test {
apps::AppType app_type,
apps::PermissionType permission_type,
bool permission_value = true) {
- apps::PermissionValuePtr fake_permission_value =
- std::make_unique<apps::PermissionValue>(permission_value);
+ apps::Permission::PermissionValue fake_permission_value = permission_value;
apps::PermissionPtr fake_permission = std::make_unique<apps::Permission>(
permission_type, std::move(fake_permission_value),
/*is_managed=*/false);
@@ -231,9 +231,8 @@ TEST_F(AppNotificationHandlerTest, TestAppListUpdated) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(observer()->app_list_changed(), 1);
EXPECT_EQ("arcAppWithNotifications", observer()->recently_updated_app()->id);
- EXPECT_TRUE(absl::get<bool>(observer()
- ->recently_updated_app()
- ->notification_permission->value->value));
+ EXPECT_TRUE(absl::get<bool>(
+ observer()->recently_updated_app()->notification_permission->value));
CreateAndStoreFakeApp("webAppWithNotifications", apps::AppType::kWeb,
apps::PermissionType::kNotifications,
@@ -243,9 +242,7 @@ TEST_F(AppNotificationHandlerTest, TestAppListUpdated) {
EXPECT_EQ(observer()->app_list_changed(), 2);
EXPECT_EQ("webAppWithNotifications", observer()->recently_updated_app()->id);
EXPECT_TRUE(absl::holds_alternative<bool>(
- observer()
- ->recently_updated_app()
- ->notification_permission->value->value));
+ observer()->recently_updated_app()->notification_permission->value));
CreateAndStoreFakeApp("arcAppWithCamera", apps::AppType::kArc,
apps::PermissionType::kCamera);
@@ -272,9 +269,8 @@ TEST_F(AppNotificationHandlerTest, TestAppListUpdated) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(observer()->app_list_changed(), 3);
EXPECT_EQ("arcAppWithNotifications", observer()->recently_updated_app()->id);
- EXPECT_FALSE(absl::get<bool>(observer()
- ->recently_updated_app()
- ->notification_permission->value->value));
+ EXPECT_FALSE(absl::get<bool>(
+ observer()->recently_updated_app()->notification_permission->value));
CreateAndStoreFakeApp("webAppWithNotifications", apps::AppType::kWeb,
apps::PermissionType::kNotifications,
@@ -283,9 +279,8 @@ TEST_F(AppNotificationHandlerTest, TestAppListUpdated) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(observer()->app_list_changed(), 4);
EXPECT_EQ("webAppWithNotifications", observer()->recently_updated_app()->id);
- EXPECT_FALSE(absl::get<bool>(observer()
- ->recently_updated_app()
- ->notification_permission->value->value));
+ EXPECT_FALSE(absl::get<bool>(
+ observer()->recently_updated_app()->notification_permission->value));
}
TEST_F(AppNotificationHandlerTest, TestOpenBrowserNotificationSettings) {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/BUILD.gn b/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/BUILD.gn
index ab6ecccc08a..c14c5fcc461 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/BUILD.gn
@@ -31,8 +31,7 @@ mojom("mojom") {
"//components/services/app_service/public/cpp/app_types.h",
]
traits_sources = [ "//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.cc" ]
- traits_public_deps =
- [ "//components/services/app_service/public/cpp:app_types" ]
+ traits_public_deps = [ "//components/services/app_service" ]
},
]
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_auto_screen_lock_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_auto_screen_lock_browsertest.cc
index 4e70680f5f8..07a060bb197 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_auto_screen_lock_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_auto_screen_lock_browsertest.cc
@@ -5,7 +5,7 @@
#include "ash/constants/ash_pref_names.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.h"
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
#include "chrome/test/data/webui/settings/chromeos/test_api.test-mojom-test-utils.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_test.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.cc
index 4aea754a9de..7f9df4bfb9d 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.cc
@@ -7,16 +7,33 @@
#include "ash/components/arc/arc_features.h"
#include "base/feature_list.h"
#include "chrome/browser/ash/arc/arc_util.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
+#include "components/policy/core/common/management/management_service.h"
+
#include "components/user_manager/user_manager.h"
+#include "chrome/browser/ash/login/users/chrome_user_manager.h"
+
namespace ash::settings {
bool IsGuestModeActive() {
- return user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
- user_manager::UserManager::Get()->IsLoggedInAsPublicAccount();
+ return ash::ChromeUserManager::Get()->IsLoggedInAsGuest() ||
+ ash::ChromeUserManager::Get()->IsLoggedInAsManagedGuestSession();
+}
+
+bool IsChildUser() {
+ return ash::ChromeUserManager::Get()->IsLoggedInAsChildUser();
+}
+
+bool IsDeviceEnterpriseManaged() {
+ return ash::ChromeUserManager::Get()->IsEnterpriseManaged();
+}
+
+bool IsPowerwashAllowed() {
+ return !IsDeviceEnterpriseManaged() && !IsGuestModeActive() && !IsChildUser();
}
bool ShouldShowParentalControlSettings(const Profile* profile) {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.h b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.h
index 12baf356320..cf5ee0a0f43 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util.h
@@ -11,6 +11,14 @@ namespace ash::settings {
bool IsGuestModeActive();
+bool IsChildUser();
+
+bool IsDeviceEnterpriseManaged();
+
+// Determines whether powerwash is allowed for this user. Powerwash is disabled
+// for guest users, child users, and managed users.
+bool IsPowerwashAllowed();
+
// Determines whether the Parental Controls section of People settings should be
// shown for |profile|.
bool ShouldShowParentalControlSettings(const Profile* profile);
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util_unittest.cc
new file mode 100644
index 00000000000..d1ccec2a926
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_features_util_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/settings/ash/os_settings_features_util.h"
+
+#include "chrome/browser/ui/webui/settings/ash/reset_section.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+
+// #include "components/user_manager/fake_chrome_user_manager.h"
+namespace ash::settings {
+
+class OsSettingsFeaturesUtilTest : public testing::Test {
+ public:
+ OsSettingsFeaturesUtilTest() = default;
+ ~OsSettingsFeaturesUtilTest() override = default;
+
+ void SetUp() override {
+ auto fake_chrome_user_manager =
+ std::make_unique<ash::FakeChromeUserManager>();
+ fake_chrome_user_manager_ = fake_chrome_user_manager.get();
+ scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
+ std::move(fake_chrome_user_manager));
+ }
+
+ void TearDown() override { scoped_user_manager_.reset(); }
+
+ ash::FakeChromeUserManager* FakeChromeUserManager() {
+ return fake_chrome_user_manager_;
+ }
+
+ const AccountId MakeAccountId() {
+ return AccountId::FromUserEmailGaiaId("test-user@testdomain.com",
+ "1234567890");
+ }
+
+ private:
+ // Owned by |scoped_user_manager_|.
+ raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh>
+ fake_chrome_user_manager_ = nullptr;
+
+ std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
+};
+
+TEST_F(OsSettingsFeaturesUtilTest, PowerwashAllowedForRegularUser) {
+ const AccountId account_id = MakeAccountId();
+ auto* fake_chrome_user_manager_ = FakeChromeUserManager();
+ fake_chrome_user_manager_->AddUser(account_id);
+ fake_chrome_user_manager_->LoginUser(account_id);
+
+ EXPECT_TRUE(IsPowerwashAllowed());
+}
+
+TEST_F(OsSettingsFeaturesUtilTest, PowerwashDisallowedForGuestUser) {
+ auto* fake_chrome_user_manager_ = FakeChromeUserManager();
+ auto* user = fake_chrome_user_manager_->AddGuestUser();
+ fake_chrome_user_manager_->LoginUser(user->GetAccountId());
+
+ EXPECT_FALSE(IsPowerwashAllowed());
+}
+
+TEST_F(OsSettingsFeaturesUtilTest, PowerwashDisallowedForChildUser) {
+ const AccountId account_id = MakeAccountId();
+ auto* fake_chrome_user_manager_ = FakeChromeUserManager();
+ fake_chrome_user_manager_->AddChildUser(account_id);
+ fake_chrome_user_manager_->set_current_user_child(true);
+ fake_chrome_user_manager_->LoginUser(account_id);
+
+ EXPECT_FALSE(IsPowerwashAllowed());
+}
+
+TEST_F(OsSettingsFeaturesUtilTest, PowerwashDisallowedForManagedUser) {
+ const AccountId account_id = MakeAccountId();
+ auto* fake_chrome_user_manager_ = FakeChromeUserManager();
+ fake_chrome_user_manager_->AddUser(account_id);
+ fake_chrome_user_manager_->set_is_enterprise_managed(true);
+ fake_chrome_user_manager_->LoginUser(account_id);
+
+ EXPECT_FALSE(IsPowerwashAllowed());
+}
+
+} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_identifier.h b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_identifier.h
index a4db5a72e42..76e6ec312a9 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_identifier.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_identifier.h
@@ -5,8 +5,8 @@
#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_OS_SETTINGS_IDENTIFIER_H_
#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_OS_SETTINGS_IDENTIFIER_H_
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
+#include "ash/webui/settings/public/constants/setting.mojom.h"
namespace ash::settings {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_authentication_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_authentication_browsertest.cc
index eb3aff146c5..37dfb8eb233 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_authentication_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_authentication_browsertest.cc
@@ -4,11 +4,15 @@
#include "base/metrics/histogram_base.h"
#include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.h"
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
#include "content/public/test/browser_test.h"
+namespace ash::settings {
+
namespace {
+using PasswordType = OSSettingsLockScreenBrowserTestBase::PasswordType;
+
// Name and value of the metric that records authentication on the lock screen
// page.
const char kPinUnlockUmaHistogramName[] = "Settings.PinUnlockSetup";
@@ -16,18 +20,28 @@ const base::HistogramBase::Sample kEnterPasswordCorrectly = 1;
} // namespace
-namespace ash::settings {
-
// Test of the authentication dialog in the lock screen page in os-settings.
class OSSettingsLockScreenAuthenticationTest
- : public OSSettingsLockScreenBrowserTestBase {
+ : public OSSettingsLockScreenBrowserTestBase,
+ public testing::WithParamInterface<PasswordType> {
public:
+ OSSettingsLockScreenAuthenticationTest()
+ : OSSettingsLockScreenBrowserTestBase(GetParam()) {}
+
+ // Password constants used in test cases. The correct password is the same
+ // one as the one set up through the OSSettingsLockScreenBrowserTestBase test
+ // fixture.
static constexpr const char* kCorrectPassword =
OSSettingsLockScreenBrowserTestBase::kPassword;
static constexpr char kIncorrectPassword[] = "incorrect-password";
};
-IN_PROC_BROWSER_TEST_F(OSSettingsLockScreenAuthenticationTest,
+INSTANTIATE_TEST_SUITE_P(OSSettingsLockScreenAuthenticationTests,
+ OSSettingsLockScreenAuthenticationTest,
+ testing::Values(PasswordType::kGaia,
+ PasswordType::kLocal));
+
+IN_PROC_BROWSER_TEST_P(OSSettingsLockScreenAuthenticationTest,
SuccessfulUnlock) {
base::HistogramTester histograms;
auto lock_screen_settings = OpenLockScreenSettings();
@@ -42,7 +56,7 @@ IN_PROC_BROWSER_TEST_F(OSSettingsLockScreenAuthenticationTest,
kEnterPasswordCorrectly, 1);
}
-IN_PROC_BROWSER_TEST_F(OSSettingsLockScreenAuthenticationTest, FailedUnlock) {
+IN_PROC_BROWSER_TEST_P(OSSettingsLockScreenAuthenticationTest, FailedUnlock) {
base::HistogramTester histograms;
auto lock_screen_settings = OpenLockScreenSettings();
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager.cc
index 7853a323154..7970c21315c 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager.cc
@@ -5,13 +5,14 @@
#include "chrome/browser/ui/webui/settings/ash/os_settings_manager.h"
#include "ash/public/cpp/input_device_settings_controller.h"
+#include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_handler.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/hierarchy.h"
#include "chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.h"
#include "chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_sections.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.h"
#include "chromeos/ash/components/phonehub/phone_hub_manager.h"
#include "chromeos/constants/chromeos_features.h"
@@ -63,16 +64,21 @@ OsSettingsManager::OsSettingsManager(
OsSettingsManager::~OsSettingsManager() = default;
void OsSettingsManager::AddLoadTimeData(content::WebUIDataSource* html_source) {
- for (const auto& section : sections_->sections())
+ for (const auto& section : sections_->sections()) {
section->AddLoadTimeData(html_source);
+ }
html_source->AddBoolean("isJellyEnabled",
chromeos::features::IsJellyEnabled());
+ html_source->AddBoolean("isCrosComponentsEnabled",
+ chromeos::features::IsCrosComponentsEnabled());
+ html_source->AddBoolean("isSelfShareEnabled", features::IsSelfShareEnabled());
html_source->UseStringsJs();
}
void OsSettingsManager::AddHandlers(content::WebUI* web_ui) {
- for (const auto& section : sections_->sections())
+ for (const auto& section : sections_->sections()) {
section->AddHandlers(web_ui);
+ }
}
void OsSettingsManager::Shutdown() {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.cc
index a9af04e0260..2a76417a4e8 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.cc
@@ -57,11 +57,12 @@ OsSettingsManagerFactory::OsSettingsManagerFactory()
OsSettingsManagerFactory::~OsSettingsManagerFactory() = default;
-KeyedService* OsSettingsManagerFactory::BuildServiceInstanceFor(
+std::unique_ptr<KeyedService>
+OsSettingsManagerFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
- return new OsSettingsManager(
+ return std::make_unique<OsSettingsManager>(
profile,
local_search_service::LocalSearchServiceProxyFactory::
GetForBrowserContext(context),
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.h b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.h
index dad2c9ce335..b0a43a14637 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.h
@@ -29,7 +29,7 @@ class OsSettingsManagerFactory : public ProfileKeyedServiceFactory {
OsSettingsManagerFactory& operator=(const OsSettingsManagerFactory&) = delete;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
bool ServiceIsNULLWhileTesting() const override;
};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_unittest.cc
index 69057eb21eb..15e5097ab34 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_manager_unittest.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/settings/ash/os_settings_manager.h"
#include "ash/constants/ash_features.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "base/containers/contains.h"
#include "base/metrics/histogram_base.h"
#include "base/test/metrics/histogram_enum_reader.h"
@@ -17,11 +18,10 @@
#include "chrome/browser/ash/phonehub/phone_hub_manager_factory.h"
#include "chrome/browser/ash/printing/cups_printers_manager_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/ui/webui/settings/ash/constants/constants_util.h"
+#include "chrome/browser/ui/webui/ash/settings/constants/constants_util.h"
#include "chrome/browser/ui/webui/settings/ash/hierarchy.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_sections.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
@@ -53,7 +53,6 @@ class OsSettingsManagerTest : public testing::Test {
void SetUp() override {
scoped_feature_list_.InitWithFeatures(
{::features::kAccessibilityChromeVoxPageMigration,
- ::features::kAccessibilitySelectToSpeakPageMigration,
ash::features::kInputDeviceSettingsSplit,
ash::features::kPeripheralCustomization},
{});
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.cc
index 8fd0adfde07..54849c2ce30 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.cc
@@ -5,21 +5,47 @@
#include "chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.h"
#include "base/metrics/histogram_functions.h"
+#include "chrome/browser/ash/login/login_pref_names.h"
#include "chrome/browser/ash/settings/cros_settings.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.h"
+#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "components/metrics/metrics_service.h"
namespace ash::settings {
namespace {
+// Used to check whether it has been one week since the user has finished OOBE
+// onboarding.
+constexpr base::TimeDelta kOneWeek = base::Days(7);
constexpr char kOsSettingsVerifiedAccessEnabledHistogramName[] =
"ChromeOS.Settings.Privacy.VerifiedAccessEnabled";
+constexpr char kOsSettingsLifetimeNumUniqueSettingsChangedFirstWeek[] =
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek";
+constexpr char kOsSettingsLifetimeNumUniqueSettingsChangedSubsequentWeeks[] =
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks";
+constexpr char kOsSettingsLifetimeNumUniqueSettingsChangedTotal[] =
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.Total";
+
} // namespace
void OsSettingsMetricsProvider::ProvideCurrentSessionData(
metrics::ChromeUserMetricsExtension* uma_proto_unused) {
// Log verified access enabled/disabled value for this session
+ LogVerifiedAccessEnabled();
+
+ // Log total unique Settings changed over the device's lifetime if allowed.
+ MaybeLogTotalUniqueSettingsChanged();
+}
+
+void OsSettingsMetricsProvider::LogVerifiedAccessEnabled() {
bool verified_access_enabled;
ash::CrosSettings::Get()->GetBoolean(
ash::kAttestationForContentProtectionEnabled, &verified_access_enabled);
@@ -27,4 +53,67 @@ void OsSettingsMetricsProvider::ProvideCurrentSessionData(
verified_access_enabled);
}
+void OsSettingsMetricsProvider::MaybeLogTotalUniqueSettingsChanged() {
+ // Log total unique Settings changed over the device's lifetime if allowed.
+ PrefService* profile_pref_service =
+ ProfileManager::GetActiveUserProfile()->GetPrefs();
+ DCHECK(profile_pref_service);
+
+ // We do not have consent to record the metrics.
+ if (!ShouldRecordMetrics(profile_pref_service)) {
+ return;
+ }
+
+ int total_unique_settings_changed_count =
+ profile_pref_service->GetDict(::prefs::kTotalUniqueOsSettingsChanged)
+ .size();
+
+ // prefs::kOobeOnboardingTime does not exist for users in guest mode.
+ if (profile_pref_service->HasPrefPath(prefs::kOobeOnboardingTime)) {
+ // We want to record the data in UMA even if no new unique Settings has
+ // changed because UMA clears the data after a certain timeframe, and we
+ // want to be able to still represent the user that have not used Settings
+ // frequently in the histogram.
+ if (IsTodayInFirst7Days(profile_pref_service)) {
+ base::UmaHistogramCounts1000(
+ kOsSettingsLifetimeNumUniqueSettingsChangedFirstWeek,
+ total_unique_settings_changed_count);
+ } else {
+ base::UmaHistogramCounts1000(
+ kOsSettingsLifetimeNumUniqueSettingsChangedSubsequentWeeks,
+ total_unique_settings_changed_count);
+ }
+ // Store the total unique Settings changed in .DeviceLifetime2.Total
+ // histogram.
+ base::UmaHistogramCounts1000(
+ kOsSettingsLifetimeNumUniqueSettingsChangedTotal,
+ total_unique_settings_changed_count);
+ }
+}
+
+bool OsSettingsMetricsProvider::ShouldRecordMetrics(
+ PrefService* profile_pref_service) {
+ if (profile_pref_service->GetBoolean(
+ ::prefs::kHasEverRevokedMetricsConsent)) {
+ // If the pref has been turned off at least once in the user's lifetime,
+ // clear the pref kTotalUniqueOsSettingsChanged.
+ profile_pref_service->ClearPref(::prefs::kTotalUniqueOsSettingsChanged);
+
+ // We do not have consent to record the user's metrics.
+ return false;
+ }
+ return true;
+}
+
+bool OsSettingsMetricsProvider::IsTodayInFirst7Days(
+ PrefService* profile_pref_service) {
+ // The pref kOobeOnboardingTime does not get set for users in guest mode.
+ // Because we are accessing the value of the pref, we must ensure that it does
+ // exist.
+ DCHECK(profile_pref_service->HasPrefPath(prefs::kOobeOnboardingTime));
+ return base::Time::Now() -
+ profile_pref_service->GetTime(::ash::prefs::kOobeOnboardingTime) <=
+ kOneWeek;
+}
+
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.h b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.h
index 544b0015e5c..1b62d711f5a 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.h
@@ -7,6 +7,8 @@
#include "components/metrics/metrics_provider.h"
+class PrefService;
+
namespace ash::settings {
class OsSettingsMetricsProvider : public metrics::MetricsProvider {
@@ -22,6 +24,13 @@ class OsSettingsMetricsProvider : public metrics::MetricsProvider {
// metrics::MetricsProvider:
void ProvideCurrentSessionData(
metrics::ChromeUserMetricsExtension* uma_proto) override;
+
+ private:
+ void LogVerifiedAccessEnabled();
+ void MaybeLogTotalUniqueSettingsChanged();
+ bool HasUserMetricsConsent();
+ bool ShouldRecordMetrics(PrefService* profile_pref_service);
+ bool IsTodayInFirst7Days(PrefService* profile_pref_service);
};
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_notification_settings_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_notification_settings_browsertest.cc
index 9aa1cf9e989..241daa82670 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_notification_settings_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_notification_settings_browsertest.cc
@@ -4,12 +4,12 @@
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
+#include "ash/webui/settings/public/constants/setting.mojom-shared.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom-shared.h"
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
#include "chrome/test/data/webui/settings/chromeos/test_api.test-mojom-test-utils.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_test.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_password_setup_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_password_setup_browsertest.cc
new file mode 100644
index 00000000000..f47424f6dfa
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_password_setup_browsertest.cc
@@ -0,0 +1,60 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
+#include "chrome/test/data/webui/settings/chromeos/os_people_page/password_settings_api.test-mojom-test-utils.h"
+#include "chrome/test/data/webui/settings/chromeos/test_api.test-mojom-test-utils.h"
+#include "content/public/test/browser_test.h"
+
+namespace ash::settings {
+
+class OSSettingsPasswordSetupTest : public OSSettingsLockScreenBrowserTestBase {
+ using OSSettingsLockScreenBrowserTestBase::
+ OSSettingsLockScreenBrowserTestBase;
+
+ public:
+ mojom::PasswordSettingsApiAsyncWaiter GoToPasswordSettings(
+ mojom::LockScreenSettingsAsyncWaiter& lock_screen_settings) {
+ password_settings_remote_ =
+ mojo::Remote(lock_screen_settings.GoToPasswordSettings());
+ return mojom::PasswordSettingsApiAsyncWaiter(
+ password_settings_remote_.get());
+ }
+
+ private:
+ mojo::Remote<mojom::PasswordSettingsApi> password_settings_remote_;
+};
+
+class OSSettingsPasswordSetupTestWithGaiaPassword
+ : public OSSettingsPasswordSetupTest {
+ public:
+ OSSettingsPasswordSetupTestWithGaiaPassword()
+ : OSSettingsPasswordSetupTest(PasswordType::kGaia) {}
+};
+class OSSettingsPasswordSetupTestWithLocalPassword
+ : public OSSettingsPasswordSetupTest {
+ public:
+ OSSettingsPasswordSetupTestWithLocalPassword()
+ : OSSettingsPasswordSetupTest(PasswordType::kLocal) {}
+};
+
+IN_PROC_BROWSER_TEST_F(OSSettingsPasswordSetupTestWithGaiaPassword, NotShown) {
+ mojom::LockScreenSettingsAsyncWaiter lock_screen_settings =
+ OpenLockScreenSettingsAndAuthenticate();
+ lock_screen_settings.AssertPasswordControlVisibility(false);
+}
+
+IN_PROC_BROWSER_TEST_F(OSSettingsPasswordSetupTestWithLocalPassword, Selected) {
+ mojom::LockScreenSettingsAsyncWaiter lock_screen_settings =
+ OpenLockScreenSettingsAndAuthenticate();
+ mojom::PasswordSettingsApiAsyncWaiter password_settings =
+ GoToPasswordSettings(lock_screen_settings);
+
+ // TODO(b/290917006): Reenable this check. This check did not succeed in a
+ // debug build, where the tests seems to have timed out.
+ //
+ // password_settings.AssertSelectedPasswordType(mojom::PasswordType::kLocal);
+}
+
+} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_pin_setup_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_pin_setup_browsertest.cc
index 2705575fa5d..fd27a2b1eab 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_pin_setup_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_pin_setup_browsertest.cc
@@ -3,10 +3,22 @@
// found in the LICENSE file.
#include "ash/constants/ash_pref_names.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/values.h"
+#include "chrome/browser/ash/login/quick_unlock/pin_storage_prefs.h"
+#include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
+#include "chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h"
+#include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.h"
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
#include "chrome/common/pref_names.h"
+#include "chrome/test/data/webui/settings/chromeos/os_people_page/pin_settings_api.test-mojom-test-utils.h"
+#include "chrome/test/data/webui/settings/chromeos/test_api.test-mojom-test-utils.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_test.h"
@@ -41,9 +53,39 @@ const base::HistogramBase::Sample kConfirmPin = 4;
} // namespace
+enum class PinType {
+ kPrefs,
+ kCryptohome,
+};
+
// Tests PIN-related settings in the ChromeOS settings page.
-class OSSettingsPinSetupTest : public OSSettingsLockScreenBrowserTestBase {
+class OSSettingsPinSetupTest : public OSSettingsLockScreenBrowserTestBase,
+ public testing::WithParamInterface<PinType> {
public:
+ OSSettingsPinSetupTest() : pin_type_(GetParam()) {
+ switch (pin_type_) {
+ case PinType::kPrefs:
+ cryptohome_.set_supports_low_entropy_credentials(false);
+ break;
+ case PinType::kCryptohome:
+ cryptohome_.set_supports_low_entropy_credentials(true);
+ break;
+ }
+ }
+
+ void SetUpInProcessBrowserTestFixture() override {
+ OSSettingsLockScreenBrowserTestBase::SetUpInProcessBrowserTestFixture();
+
+ // Override the policy provider for testing. The `ON_CALL` lines here are
+ // necessary because something inside the policy stack expects those return
+ // values.
+ ON_CALL(provider_, IsInitializationComplete(testing::_))
+ .WillByDefault(testing::Return(true));
+ ON_CALL(provider_, IsFirstPolicyLoadComplete(testing::_))
+ .WillByDefault(testing::Return(true));
+ policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
+ }
+
PrefService& Prefs() {
PrefService* service =
ProfileHelper::Get()->GetProfileByAccountId(GetAccountId())->GetPrefs();
@@ -54,139 +96,171 @@ class OSSettingsPinSetupTest : public OSSettingsLockScreenBrowserTestBase {
bool GetPinAutoSubmitState() {
return Prefs().GetBoolean(::prefs::kPinUnlockAutosubmitEnabled);
}
-};
-// Tests that the happy path of setting and removing PINs works, and that all
-// relevant metrics are recorded.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, SetRemove) {
- // Launch first instance of the settings page, setup and remove PIN a few
- // times.
- {
- auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
- lock_screen_settings.AssertIsUsingPin(false);
- EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
-
- // Remove the pin. Nothing should happen.
- lock_screen_settings.RemovePin();
- lock_screen_settings.AssertIsUsingPin(false);
- EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
-
- // Set a pin. Cryptohome should be aware of the pin, and we should record
- // that all stages of PIN setup were completed in UMA.
- {
- base::HistogramTester histograms;
- lock_screen_settings.SetPin(kFirstPin);
- EXPECT_EQ(true, cryptohome_.HasPinFactor(GetAccountId()));
- histograms.ExpectBucketCount(kPinUnlockUmaHistogramName,
- kChoosePinOrPassword, 1);
- // TODO(b/270962495): kEnterPin and kConfirmPin are currently not
- // recorded in the os-settings page, but this is likely a bug.
- histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kEnterPin, 0);
- histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kConfirmPin, 0);
- histograms.ExpectTotalCount(kPinUnlockUmaHistogramName, 1);
+ // Returns whether or not a PIN is configured in the backend.
+ bool IsPinConfigured() {
+ switch (pin_type_) {
+ case PinType::kPrefs:
+ return !Prefs().GetString(prefs::kQuickUnlockPinSecret).empty() &&
+ !Prefs().GetString(prefs::kQuickUnlockPinSalt).empty();
+ case PinType::kCryptohome:
+ return cryptohome_.HasPinFactor(GetAccountId());
+ }
+ }
+
+ void SetPinLocked() {
+ switch (pin_type_) {
+ case PinType::kPrefs: {
+ quick_unlock::QuickUnlockStorage* qus =
+ quick_unlock::QuickUnlockFactory::GetForAccountId(GetAccountId());
+ CHECK(qus);
+ quick_unlock::PinStoragePrefs* psp = qus->pin_storage_prefs();
+ CHECK(psp);
+ // Make sure to add enough unlock attempts so that PIN is locked out.
+ for (int i = 0;
+ i != quick_unlock::PinStoragePrefs::kMaximumUnlockAttempts; ++i) {
+ psp->AddUnlockAttempt();
+ }
+ CHECK(!psp->IsPinAuthenticationAvailable(quick_unlock::Purpose::kAny));
+ break;
+ }
+ case PinType::kCryptohome: {
+ cryptohome_.SetPinLocked(GetAccountId(), true);
+ break;
+ }
}
- // TODO(b/243696986): Lock the screen or sign out and check that the PIN
- // works.
-
- // Change the pin.
- lock_screen_settings.SetPin(kSecondPin);
- EXPECT_EQ(true, cryptohome_.HasPinFactor(GetAccountId()));
-
- // Change the pin, but to the same value.
- lock_screen_settings.SetPin(kSecondPin);
- EXPECT_EQ(true, cryptohome_.HasPinFactor(GetAccountId()));
-
- // Remove the pin.
- lock_screen_settings.RemovePin();
- // TODO(b/256584110): We can't reliable test the following:
- //
- // EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
- //
- // because the UI reports the pin as being removed before it's actually
- // removed.
-
- // Setting up a pin should still work.
- lock_screen_settings.SetPin(kFirstPin);
- lock_screen_settings.AssertIsUsingPin(true);
- EXPECT_EQ(true, cryptohome_.HasPinFactor(GetAccountId()));
}
- // Opening the settings page again should display that a PIN is configured.
+ mojom::PinSettingsApiAsyncWaiter GoToPinSettings(
+ mojom::LockScreenSettingsAsyncWaiter& lock_screen_settings) {
+ pin_settings_remote_ = mojo::Remote(lock_screen_settings.GoToPinSettings());
+ return mojom::PinSettingsApiAsyncWaiter(pin_settings_remote_.get());
+ }
+
+ void SetPinDisabledPolicy(bool disabled) {
+ policy::PolicyMap policies;
+ base::Value policy_value{disabled ? base::Value::List()
+ : base::Value::List().Append("PIN")};
+
+ policies.Set("QuickUnlockModeAllowlist", policy::POLICY_LEVEL_MANDATORY,
+ policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
+ policy_value.Clone(),
+ /*external_data_fetcher=*/nullptr);
+
+ policies.Set("WebAuthnFactors", policy::POLICY_LEVEL_MANDATORY,
+ policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
+ policy_value.Clone(),
+ /*external_data_fetcher=*/nullptr);
+
+ provider_.UpdateChromePolicy(policies);
+ }
+
+ private:
+ PinType pin_type_;
+ mojo::Remote<mojom::PinSettingsApi> pin_settings_remote_;
+ testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ OSSettingsPinSetupTest,
+ testing::Values(PinType::kPrefs,
+ PinType::kCryptohome));
+
+// Tests that adding a PIN works.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, AddPin) {
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
- lock_screen_settings.AssertIsUsingPin(true);
- EXPECT_EQ(true, cryptohome_.HasPinFactor(GetAccountId()));
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+
+ pin_settings.AssertHasPin(false);
+ EXPECT_EQ(false, IsPinConfigured());
+
+ pin_settings.SetPin(kFirstPin);
+
+ pin_settings.AssertHasPin(true);
+ EXPECT_EQ(true, IsPinConfigured());
}
-// Tests that nothing is persisted when just selecting the "PIN and password"
-// checkbox. This only makes another "setup PIN" button appear, and ony after
-// clicking that button and going through the PIN setup flow is the PIN fully
-// set up.
-// We do have to check that the relevant metric is recorded though.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, SelectPinAndPassword) {
+// Tests that changing a PIN works.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, ChangePin) {
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ pin_settings.SetPin(kFirstPin);
+ pin_settings.AssertHasPin(true);
+ EXPECT_EQ(true, IsPinConfigured());
- lock_screen_settings.AssertIsUsingPin(false);
- EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
+ pin_settings.SetPin(kSecondPin);
- base::HistogramTester histograms;
- lock_screen_settings.SelectPinAndPassword();
+ pin_settings.AssertHasPin(true);
+ EXPECT_EQ(true, IsPinConfigured());
+}
- lock_screen_settings.AssertIsUsingPin(true);
- EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
- // TODO(b/270962495): See Comment #2 in the linked bug. We don't record
- // kChoosePinOrPassword when the user clicks on "PIN and password" but when
- // they click "setup pin". That might be a bug.
- histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kChoosePinOrPassword,
- 0);
- histograms.ExpectTotalCount(kPinUnlockUmaHistogramName, 0);
+// Tests that removing a PIN works.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, RemovePin) {
+ auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ pin_settings.SetPin(kFirstPin);
+ pin_settings.AssertHasPin(true);
+ EXPECT_EQ(true, IsPinConfigured());
+
+ pin_settings.RemovePin();
+
+ EXPECT_EQ(false, IsPinConfigured());
+ pin_settings.AssertHasPin(false);
}
-// Tests that nothing is persisted when cancelling the PIN setup dialog after
-// entering the PIN only once. We should record this in UMA though.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, SetPinButCancelConfirmation) {
+// Tests that PIN changes are persistent over relaunching os-settings.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, PinPersists) {
+ {
+ auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ pin_settings.SetPin(kFirstPin);
+
+ pin_settings.AssertHasPin(true);
+ EXPECT_EQ(true, IsPinConfigured());
+ }
+
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
- lock_screen_settings.AssertIsUsingPin(false);
- EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
+ pin_settings.AssertHasPin(true);
+ EXPECT_EQ(true, IsPinConfigured());
+}
- base::HistogramTester histograms;
- lock_screen_settings.SetPinButCancelConfirmation(kFirstPin);
+// Tests that nothing is persisted when cancelling the PIN setup dialog after
+// entering the PIN only once.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, SetPinButCancelConfirmation) {
+ auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ pin_settings.AssertHasPin(false);
+ EXPECT_EQ(false, IsPinConfigured());
- EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
- histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kChoosePinOrPassword,
- 1);
- // TODO(b/270962495): kEnterPin and kConfirmPin are currently not
- // recorded in the os-settings page, but this is likely a bug.
- histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kEnterPin, 0);
- histograms.ExpectTotalCount(kPinUnlockUmaHistogramName, 1);
+ pin_settings.SetPinButCancelConfirmation(kFirstPin);
+
+ pin_settings.AssertHasPin(false);
+ EXPECT_EQ(false, IsPinConfigured());
}
// Tests that nothing is persisted during setup when the PIN that is entered
// the second time for confirmation does not match the first PIN. We should
// record this in UMA though.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, SetPinButFailConfirmation) {
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, SetPinButFailConfirmation) {
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ pin_settings.AssertHasPin(false);
+ EXPECT_EQ(false, IsPinConfigured());
- lock_screen_settings.AssertIsUsingPin(false);
- EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
-
- base::HistogramTester histograms;
- lock_screen_settings.SetPinButFailConfirmation(kFirstPin, kIncorrectPin);
+ pin_settings.SetPinButFailConfirmation(kFirstPin, kIncorrectPin);
- EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
- histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kChoosePinOrPassword,
- 1);
- // TODO(b/270962495): kEnterPin and kConfirmPin are currently not
- // recorded in the os-settings page, but this is likely a bug.
- histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kEnterPin, 0);
- histograms.ExpectTotalCount(kPinUnlockUmaHistogramName, 1);
+ pin_settings.AssertHasPin(false);
+ EXPECT_EQ(false, IsPinConfigured());
}
// Tests that PIN setup UI validates minimal pin lengths.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, MinimumPinLength) {
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, MinimumPinLength) {
Prefs().SetInteger(prefs::kPinUnlockMinimumLength, kMinimumPinLengthForTest);
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
// Check that a minimum length PIN is accepted, but that the PIN obtained by
// removing the last digit is rejected.
@@ -194,15 +268,15 @@ IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, MinimumPinLength) {
too_short_pin.pop_back();
// SetPinButTooShort checks that a warning is displayed.
- lock_screen_settings.SetPinButTooShort(std::move(too_short_pin),
- kMinimumLengthPin);
+ pin_settings.SetPinButTooShort(std::move(too_short_pin), kMinimumLengthPin);
}
// Tests that PIN setup UI validates maximal pin lengths.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, MaximumPinLength) {
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, MaximumPinLength) {
Prefs().SetInteger(prefs::kPinUnlockMaximumLength, kMaximumPinLengthForTest);
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
// Check that a maximum length PIN is accepted, but that the PIN obtained by
// duplicating the last digit is rejected.
@@ -210,119 +284,205 @@ IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, MaximumPinLength) {
too_long_pin += too_long_pin.back();
// SetPinButTooLong checks that a warning is displayed.
- lock_screen_settings.SetPinButTooLong(std::move(too_long_pin),
- kMaximumLengthPin);
+ pin_settings.SetPinButTooLong(std::move(too_long_pin), kMaximumLengthPin);
}
// Tests that a warning is displayed when setting up a weak PIN, but that it is
// still possible.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, WeakPinWarning) {
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, WeakPinWarning) {
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
// SetPinWithWarning checks that a warning is displayed.
- lock_screen_settings.SetPinWithWarning(kWeakPin);
+ pin_settings.SetPinWithWarning(kWeakPin);
- lock_screen_settings.AssertIsUsingPin(true);
- EXPECT_EQ(true, cryptohome_.HasPinFactor(GetAccountId()));
+ pin_settings.AssertHasPin(true);
+ EXPECT_EQ(true, IsPinConfigured());
}
// Tests that the PIN setup dialog handles key events appropriately.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, PressKeysInPinSetupDialog) {
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, PressKeysInPinSetupDialog) {
+ auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+
+ pin_settings.CheckPinSetupDialogKeyInput();
+}
+
+// Tests that all relevant metrics are recorded when cancelling PIN setup
+// immediately.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, SetPinMetricsCancelImmediately) {
+ auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ base::HistogramTester histograms;
+
+ pin_settings.SetPinButCancelImmediately();
+
+ // The UI doesn't wait for the asynchronous metrics calls to finish, which is
+ // why we need the RunLoop here:
+ base::RunLoop().RunUntilIdle();
+ histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kChoosePinOrPassword,
+ 1);
+ histograms.ExpectTotalCount(kPinUnlockUmaHistogramName, 1);
+}
+
+// Tests that all relevant metrics are recorded when cancelling PIN setup in
+// the confirmation step.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest,
+ SetPinMetricsCancelConfirmation) {
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
- // CheckPinSetupDialogKeyInput checks that some relevant keys (digit,
- // character, function) have an effect or have no effect.
- lock_screen_settings.CheckPinSetupDialogKeyInput();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ base::HistogramTester histograms;
+
+ pin_settings.SetPinButCancelConfirmation(kFirstPin);
+
+ // The UI doesn't wait for the asynchronous metrics calls to finish, which is
+ // why we need the RunLoop here:
+ base::RunLoop().RunUntilIdle();
+ histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kChoosePinOrPassword,
+ 1);
+ histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kEnterPin, 1);
+ histograms.ExpectTotalCount(kPinUnlockUmaHistogramName, 2);
+}
+
+// Tests that all relevant metrics are recorded when adding a PIN.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, SetPinMetricsFull) {
+ auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ base::HistogramTester histograms;
+
+ pin_settings.SetPin(kFirstPin);
+
+ // The UI doesn't wait for the asynchronous metrics calls to finish, which is
+ // why we need the RunLoop here:
+ base::RunLoop().RunUntilIdle();
+ histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kChoosePinOrPassword,
+ 1);
+ histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kEnterPin, 1);
+ histograms.ExpectBucketCount(kPinUnlockUmaHistogramName, kConfirmPin, 1);
+ histograms.ExpectTotalCount(kPinUnlockUmaHistogramName, 3);
+}
+
+// Tests that the PIN control is disabled when PIN is disabled by policy.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, PinDisabledByPolicy) {
+ SetPinDisabledPolicy(true);
+
+ auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+
+ pin_settings.AssertDisabled(true);
+}
+
+// Tests that the PIN control is enabled when PIN is allowed policy.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, PinNotDisabledByPolicy) {
+ SetPinDisabledPolicy(false);
+
+ auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+
+ pin_settings.AssertDisabled(false);
+}
+
+// Tests that the PIN control gets disabled when the policy disabling PIN is
+// activated while the settings page is opened.
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, PinDisabledPolicyWhileOpen) {
+ auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ pin_settings.AssertDisabled(false);
+
+ SetPinDisabledPolicy(true);
+
+ pin_settings.AssertDisabled(true);
}
// Tests enabling and disabling autosubmit.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, Autosubmit) {
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, Autosubmit) {
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
// Set a pin. Autosubmit should be enabled.
- lock_screen_settings.SetPin(kFirstPin);
- lock_screen_settings.AssertPinAutosubmitEnabled(true);
+ pin_settings.SetPin(kFirstPin);
+ pin_settings.AssertPinAutosubmitEnabled(true);
EXPECT_EQ(true, GetPinAutoSubmitState());
// Change, remove and add pin again. Nothing of this should affect the pin
// autosubmit pref.
- lock_screen_settings.SetPin(kSecondPin);
- lock_screen_settings.AssertPinAutosubmitEnabled(true);
+ pin_settings.SetPin(kSecondPin);
+ pin_settings.AssertPinAutosubmitEnabled(true);
EXPECT_EQ(true, GetPinAutoSubmitState());
- lock_screen_settings.RemovePin();
- lock_screen_settings.AssertPinAutosubmitEnabled(true);
+ pin_settings.RemovePin();
+ pin_settings.AssertPinAutosubmitEnabled(true);
EXPECT_EQ(true, GetPinAutoSubmitState());
- lock_screen_settings.SetPin(kSecondPin);
- lock_screen_settings.AssertPinAutosubmitEnabled(true);
+ pin_settings.SetPin(kSecondPin);
+ pin_settings.AssertPinAutosubmitEnabled(true);
EXPECT_EQ(true, GetPinAutoSubmitState());
// Disable pin autosubmit. This should turn the pref off, but the pin should
// still be active.
- lock_screen_settings.DisablePinAutosubmit();
- lock_screen_settings.AssertPinAutosubmitEnabled(false);
+ pin_settings.DisablePinAutosubmit();
+ pin_settings.AssertPinAutosubmitEnabled(false);
EXPECT_EQ(false, GetPinAutoSubmitState());
- EXPECT_EQ(true, cryptohome_.HasPinFactor(GetAccountId()));
+ EXPECT_EQ(true, IsPinConfigured());
// Try to enable pin autosubmit using the wrong pin. This should not succeed.
- lock_screen_settings.EnablePinAutosubmitIncorrectly(kIncorrectPin);
- lock_screen_settings.AssertPinAutosubmitEnabled(false);
+ pin_settings.EnablePinAutosubmitIncorrectly(kIncorrectPin);
+ pin_settings.AssertPinAutosubmitEnabled(false);
EXPECT_EQ(false, GetPinAutoSubmitState());
// Try to enable pin autosubmit using the correct pin. This should succeed.
- lock_screen_settings.EnablePinAutosubmit(kSecondPin);
- lock_screen_settings.AssertPinAutosubmitEnabled(true);
+ pin_settings.EnablePinAutosubmit(kSecondPin);
+ pin_settings.AssertPinAutosubmitEnabled(true);
EXPECT_EQ(true, GetPinAutoSubmitState());
// Even after we have authenticated with the correct pin, we should be able
// to remove the pin.
- lock_screen_settings.RemovePin();
- lock_screen_settings.AssertIsUsingPin(false);
- // TODO(b/256584110): We can't reliable test the following:
- //
- // EXPECT_EQ(false, cryptohome_.HasPinFactor(GetAccountId()));
- //
- // because the UI reports the pin as being removed before it's actually
- // removed.
+ pin_settings.RemovePin();
+ pin_settings.AssertHasPin(false);
+ EXPECT_EQ(false, IsPinConfigured());
}
// Tests the maximum length of PINs for autosubmit.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, MaximumLengthAutosubmit) {
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, MaximumLengthAutosubmit) {
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
// Set a maximum length pin. Autosubmit should be enabled.
- lock_screen_settings.SetPin(kMaximumLengthPinForAutosubmit);
- lock_screen_settings.AssertPinAutosubmitEnabled(true);
+ pin_settings.SetPin(kMaximumLengthPinForAutosubmit);
+ pin_settings.AssertPinAutosubmitEnabled(true);
EXPECT_EQ(true, GetPinAutoSubmitState());
// Remove the PIN again.
- lock_screen_settings.RemovePin();
+ pin_settings.RemovePin();
// Set an overly long PIN. Autosubmit should be disabled, and we shouldn't be
// able to turn it on.
- lock_screen_settings.SetPin(kTooLongPinForAutosubmit);
- lock_screen_settings.AssertPinAutosubmitEnabled(false);
+ pin_settings.SetPin(kTooLongPinForAutosubmit);
+ pin_settings.AssertPinAutosubmitEnabled(false);
EXPECT_EQ(false, GetPinAutoSubmitState());
- lock_screen_settings.EnablePinAutosubmitTooLong(kTooLongPinForAutosubmit);
- lock_screen_settings.AssertPinAutosubmitEnabled(false);
+ pin_settings.EnablePinAutosubmitTooLong(kTooLongPinForAutosubmit);
+ pin_settings.AssertPinAutosubmitEnabled(false);
EXPECT_EQ(false, GetPinAutoSubmitState());
}
// Tests that the user is asked to reauthenticate when trying to enable PIN
// autosubmit but with a locked-out PIN.
-IN_PROC_BROWSER_TEST_F(OSSettingsPinSetupTest, AutosubmitWithLockedPin) {
+IN_PROC_BROWSER_TEST_P(OSSettingsPinSetupTest, AutosubmitWithLockedPin) {
auto lock_screen_settings = OpenLockScreenSettingsAndAuthenticate();
-
- lock_screen_settings.SetPin(kFirstPin);
+ auto pin_settings = GoToPinSettings(lock_screen_settings);
+ pin_settings.SetPin(kFirstPin);
// We disable autosubmit so that we can try to reenable.
- lock_screen_settings.DisablePinAutosubmit();
+ pin_settings.DisablePinAutosubmit();
+ SetPinLocked();
+
+ pin_settings.TryEnablePinAutosubmit(kFirstPin);
- cryptohome_.SetPinLocked(GetAccountId(), true);
+ lock_screen_settings.AssertAuthenticated(false);
- lock_screen_settings.TryEnablePinAutosubmitWithLockedPin(
- kFirstPin, OSSettingsLockScreenBrowserTestBase::kPassword);
+ lock_screen_settings.Authenticate(
+ OSSettingsLockScreenBrowserTestBase::kPassword);
EXPECT_EQ(false, GetPinAutoSubmitState());
- lock_screen_settings.AssertPinAutosubmitEnabled(false);
+ pin_settings.AssertPinAutosubmitEnabled(false);
}
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_recovery_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_recovery_browsertest.cc
index 514c4d38c8f..a4b115a2c85 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_recovery_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_recovery_browsertest.cc
@@ -4,8 +4,9 @@
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
+#include "ash/webui/settings/public/constants/setting.mojom-shared.h"
#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/webui/settings/ash/os_settings_lock_screen_browser_test_base.h"
+#include "chrome/browser/ui/webui/ash/settings/test_support/os_settings_lock_screen_browser_test_base.h"
#include "chrome/test/data/webui/settings/chromeos/test_api.test-mojom-test-utils.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
@@ -126,6 +127,17 @@ IN_PROC_BROWSER_TEST_F(OSSettingsRecoveryTestWithFeature,
EXPECT_FALSE(cryptohome_.HasRecoveryFactor(GetAccountId()));
}
+// Check that the kDataRecovery deep link id can navigate to
+// the recovery toggle.
+IN_PROC_BROWSER_TEST_F(OSSettingsRecoveryTestWithFeature,
+ NavigaionToRecoveryToggle) {
+ cryptohome_.AddRecoveryFactor(GetAccountId());
+ mojom::LockScreenSettingsAsyncWaiter lock_screen_settings =
+ OpenLockScreenSettingsDeepLinkAndAuthenticate(base::NumberToString(
+ static_cast<int>(chromeos::settings::mojom::Setting::kDataRecovery)));
+ lock_screen_settings.AssertRecoveryControlFocused();
+}
+
// Check that trying to change recovery with an invalidated auth session shows
// the password prompt again.
// TODO(crbug.com/1436858): Re-enable this test
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.cc
index 82e085e11b7..07cf7e02c1f 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.cc
@@ -8,7 +8,7 @@
#include "base/check.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.h b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.h
index 2a537abeaee..bba6760d227 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section.h
@@ -8,14 +8,14 @@
#include <string>
#include <vector>
+#include "ash/webui/settings/public/constants/routes.mojom.h"
+#include "ash/webui/settings/public/constants/setting.mojom.h"
#include "base/containers/span.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/values.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_concept.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/search.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_concept.h"
class Profile;
@@ -120,8 +120,9 @@ class OsSettingsSection {
// Provides the icon for this section.
virtual mojom::SearchResultIcon GetSectionIcon() const = 0;
- // Provides the path for this section.
- virtual std::string GetSectionPath() const = 0;
+ // Provides the path for this section. Must return a string constant defined
+ // in `routes.mojom`.
+ virtual const char* GetSectionPath() const = 0;
// Logs metrics for the updated |setting| with optional |value|. Returns
// whether the setting change was logged.
@@ -180,7 +181,8 @@ class OsSettingsSection {
OsSettingsIdentifier id,
const std::string& url_to_modify);
- const raw_ptr<Profile, ExperimentalAsh> profile_ = nullptr;
+ const raw_ptr<Profile, DanglingUntriaged | ExperimentalAsh> profile_ =
+ nullptr;
const raw_ptr<SearchTagRegistry, ExperimentalAsh> search_tag_registry_ =
nullptr;
};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section_unittest.cc
index 7e07852b689..a1ab6f1096e 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_section_unittest.cc
@@ -4,7 +4,7 @@
#include "chrome/browser/ui/webui/settings/ash/os_settings_section.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
+#include "ash/webui/settings/public/constants/setting.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash::settings {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.cc
index 3e4db6d579c..763c9cef43c 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/ui/webui/settings/ash/os_settings_sections.h"
+#include "ash/constants/ash_features.h"
#include "base/containers/contains.h"
#include "build/branding_buildflags.h"
#include "chrome/browser/profiles/profile.h"
@@ -26,6 +27,7 @@
#include "chrome/browser/ui/webui/settings/ash/privacy_section.h"
#include "chrome/browser/ui/webui/settings/ash/reset_section.h"
#include "chrome/browser/ui/webui/settings/ash/search_section.h"
+#include "chrome/browser/ui/webui/settings/ash/system_preferences_section.h"
#include "chromeos/ash/components/phonehub/phone_hub_manager.h"
namespace ash::settings {
@@ -76,9 +78,6 @@ OsSettingsSections::OsSettingsSections(
std::make_unique<PersonalizationSection>(
profile, search_tag_registry, prefs));
- AddSection(mojom::Section::kSearchAndAssistant,
- std::make_unique<SearchSection>(profile, search_tag_registry));
-
AddSection(mojom::Section::kApps, std::make_unique<AppsSection>(
profile, search_tag_registry, prefs,
arc_app_list_prefs, app_service_proxy));
@@ -87,17 +86,10 @@ OsSettingsSections::OsSettingsSections(
mojom::Section::kCrostini,
std::make_unique<CrostiniSection>(profile, search_tag_registry, prefs));
- AddSection(mojom::Section::kDateAndTime,
- std::make_unique<DateTimeSection>(profile, search_tag_registry));
-
AddSection(
mojom::Section::kPrivacyAndSecurity,
std::make_unique<PrivacySection>(profile, search_tag_registry, prefs));
- AddSection(
- mojom::Section::kLanguagesAndInput,
- std::make_unique<LanguagesSection>(profile, search_tag_registry, prefs));
-
AddSection(mojom::Section::kFiles,
std::make_unique<FilesSection>(profile, search_tag_registry));
@@ -109,9 +101,6 @@ OsSettingsSections::OsSettingsSections(
std::make_unique<AccessibilitySection>(
profile, search_tag_registry, prefs));
- AddSection(mojom::Section::kReset,
- std::make_unique<ResetSection>(profile, search_tag_registry));
-
AddSection(mojom::Section::kAboutChromeOs,
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
std::make_unique<AboutSection>(profile, search_tag_registry, prefs)
@@ -123,6 +112,25 @@ OsSettingsSections::OsSettingsSections(
AddSection(mojom::Section::kKerberos,
std::make_unique<KerberosSection>(profile, search_tag_registry,
kerberos_credentials_manager));
+
+ if (ash::features::IsOsSettingsRevampWayfindingEnabled()) {
+ AddSection(mojom::Section::kSystemPreferences,
+ std::make_unique<SystemPreferencesSection>(
+ profile, search_tag_registry, prefs));
+ } else {
+ AddSection(mojom::Section::kDateAndTime,
+ std::make_unique<DateTimeSection>(profile, search_tag_registry));
+
+ AddSection(mojom::Section::kLanguagesAndInput,
+ std::make_unique<LanguagesSection>(profile, search_tag_registry,
+ prefs));
+
+ AddSection(mojom::Section::kReset,
+ std::make_unique<ResetSection>(profile, search_tag_registry));
+
+ AddSection(mojom::Section::kSearchAndAssistant,
+ std::make_unique<SearchSection>(profile, search_tag_registry));
+ }
}
OsSettingsSections::OsSettingsSections() = default;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.h b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.h
index 6e574081ef5..d4242ba9392 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_sections.h
@@ -9,9 +9,9 @@
#include <vector>
#include "ash/webui/eche_app_ui/eche_app_manager.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_section.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
class ArcAppListPrefs;
class Profile;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.cc b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.cc
index 6cab9488424..9f066be2fd6 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.cc
@@ -13,20 +13,22 @@
#include "ash/public/cpp/esim_manager.h"
#include "ash/public/cpp/hotspot_config_service.h"
#include "ash/public/cpp/network_config_service.h"
+#include "ash/webui/common/trusted_types_util.h"
#include "ash/webui/personalization_app/search/search.mojom.h"
#include "ash/webui/personalization_app/search/search_handler.h"
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/login/quick_unlock/pin_backend.h"
#include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
-#include "chrome/browser/ash/web_applications/personalization_app/personalization_app_manager.h"
-#include "chrome/browser/ash/web_applications/personalization_app/personalization_app_manager_factory.h"
-#include "chrome/browser/ash/web_applications/personalization_app/personalization_app_utils.h"
+#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_manager.h"
+#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_manager_factory.h"
+#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_utils.h"
#include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h"
#include "chrome/browser/nearby_sharing/nearby_receive_manager.h"
#include "chrome/browser/nearby_sharing/nearby_share_settings.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service_impl.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_handler.h"
#include "chrome/browser/ui/webui/managed_ui_handler.h"
#include "chrome/browser/ui/webui/settings/ash/device_storage_handler.h"
#include "chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.h"
@@ -35,7 +37,6 @@
#include "chrome/browser/ui/webui/settings/ash/os_settings_manager.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_manager_factory.h"
#include "chrome/browser/ui/webui/settings/ash/pref_names.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_handler.h"
#include "chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/webui_url_constants.h"
@@ -105,13 +106,13 @@ OSSettingsUI::OSSettingsUI(content::WebUI* web_ui)
html_source,
base::make_span(kOsSettingsResources, kOsSettingsResourcesSize),
IDR_OS_SETTINGS_OS_SETTINGS_HTML);
+ ash::EnableTrustedTypesCSP(html_source);
#if !BUILDFLAG(OPTIMIZE_WEBUI)
html_source->AddResourcePaths(
base::make_span(kSettingsSharedResources, kSettingsSharedResourcesSize));
#endif
- html_source->DisableTrustedTypesCSP();
html_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::WorkerSrc,
"worker-src blob: chrome://resources 'self';");
@@ -300,6 +301,12 @@ void OSSettingsUI::BindInterface(
}
void OSSettingsUI::BindInterface(
+ mojo::PendingReceiver<auth::mojom::PasswordFactorEditor> receiver) {
+ auth::BindToPasswordFactorEditor(
+ std::move(receiver), quick_unlock::QuickUnlockFactory::GetDelegate());
+}
+
+void OSSettingsUI::BindInterface(
mojo::PendingReceiver<google_drive::mojom::PageHandlerFactory> receiver) {
// The PageHandlerFactory is reused across same-origin navigations, so ensure
// any existing factories are reset.
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.h b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.h
index 016571b0a95..72524e419e6 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/os_settings_ui.h
@@ -11,6 +11,7 @@
#include "base/time/time.h"
#include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
#include "chrome/browser/ui/webui/app_management/app_management_page_handler_factory.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/user_action_recorder.mojom-forward.h"
#include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h"
#include "chrome/browser/ui/webui/settings/ash/files_page/google_drive_page_handler_factory.h"
#include "chrome/browser/ui/webui/settings/ash/files_page/mojom/google_drive_handler.mojom-forward.h"
@@ -18,7 +19,6 @@
#include "chrome/browser/ui/webui/settings/ash/files_page/one_drive_page_handler_factory.h"
#include "chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom.h"
#include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom-forward.h"
-#include "chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom-forward.h"
#include "chrome/browser/ui/webui/webui_load_timer.h"
#include "chrome/common/webui_url_constants.h"
#include "chromeos/ash/components/audio/public/mojom/cros_audio_config.mojom-forward.h"
@@ -162,6 +162,8 @@ class OSSettingsUI : public ui::MojoWebUIController {
mojo::PendingReceiver<auth::mojom::RecoveryFactorEditor> receiver);
void BindInterface(
mojo::PendingReceiver<auth::mojom::PinFactorEditor> receiver);
+ void BindInterface(
+ mojo::PendingReceiver<auth::mojom::PasswordFactorEditor> receiver);
// Binds to the Jelly dynamic color Mojo
void BindInterface(
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/pdf_ocr_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/pdf_ocr_handler_unittest.cc
index b589855edbf..59ecd0b2d47 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/pdf_ocr_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/pdf_ocr_handler_unittest.cc
@@ -36,7 +36,7 @@ class TestScreenAIInstallState : public screen_ai::ScreenAIInstallState {
void SetLastUsageTime() override {}
- void DownloadComponent() override {}
+ void DownloadComponentInternal() override {}
};
class TestPdfOcrHandler : public PdfOcrHandler {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/people_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/people_section.cc
index 78b10eac276..89a7f089b9c 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/people_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/people_section.cc
@@ -25,19 +25,20 @@
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/ash/sync/os_sync_handler.h"
#include "chrome/browser/ui/webui/settings/ash/account_manager_ui_handler.h"
#include "chrome/browser/ui/webui/settings/ash/fingerprint_handler.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_features_util.h"
#include "chrome/browser/ui/webui/settings/ash/parental_controls_handler.h"
#include "chrome/browser/ui/webui/settings/ash/quick_unlock_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/people_handler.h"
#include "chrome/browser/ui/webui/settings/profile_info_handler.h"
#include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/browser_resources.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/account_manager/account_manager_factory.h"
@@ -250,10 +251,16 @@ void AddLockScreenPageStrings(content::WebUIDataSource* html_source,
{"lockScreenEditFingerprints",
IDS_SETTINGS_PEOPLE_LOCK_SCREEN_EDIT_FINGERPRINTS},
{"lockScreenPasswordOnly", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_ONLY},
+ {"lockScreenChangePasswordButton",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_CHANGE_PASSWORD_BUTTON},
{"lockScreenChangePinButton",
IDS_SETTINGS_PEOPLE_LOCK_SCREEN_CHANGE_PIN_BUTTON},
+ {"lockScreenDevicePasswordOptionLabel",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_DEVICE_PASSWORD_OPTION_LABEL},
{"lockScreenEditFingerprintsDescription",
IDS_SETTINGS_PEOPLE_LOCK_SCREEN_EDIT_FINGERPRINTS_DESCRIPTION},
+ {"lockScreenGoogleAccountPasswordOptionLabel",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_GOOGLE_ACCOUNT_PASSWORD_OPTION_LABEL},
{"lockScreenNone", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NONE},
{"lockScreenFingerprintNewName",
IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NEW_FINGERPRINT_DEFAULT_NAME},
@@ -262,8 +269,19 @@ void AddLockScreenPageStrings(content::WebUIDataSource* html_source,
{"lockScreenOptionsLock", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_OPTIONS_LOCK},
{"lockScreenOptionsLoginLock",
IDS_SETTINGS_PEOPLE_LOCK_SCREEN_OPTIONS_LOGIN_LOCK},
+ {"lockScreenRemovePinButton",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_REMOVE_PIN_BUTTON},
+ {"lockScreenPasswordLabel",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_LABEL},
+ {"lockScreenPasswordDescription",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_DESCRIPTION},
+ {"lockScreenPinLabel", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PIN_LABEL},
+ {"lockScreenSetupPasswordButton",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_SETUP_PASSWORD_BUTTON},
{"lockScreenSetupPinButton",
IDS_SETTINGS_PEOPLE_LOCK_SCREEN_SETUP_PIN_BUTTON},
+ {"lockScreenSignInOptions",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_SIGN_IN_OPTIONS},
{"lockScreenTitleLock", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_TITLE_LOCK},
{"lockScreenTitleLoginLock",
IDS_SETTINGS_PEOPLE_LOCK_SCREEN_TITLE_LOGIN_LOCK_V2},
@@ -285,6 +303,14 @@ void AddLockScreenPageStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_PEOPLE_RECOVERY_DISABLE_DIALOG_MESSAGE},
{"recoveryNotSupportedMessage",
IDS_SETTINGS_PEOPLE_RECOVERY_NOT_SUPPORTED_MESSAGE},
+ {"setLocalPasswordPlaceholder",
+ IDS_AUTH_SETUP_SET_LOCAL_PASSWORD_PLACEHOLDER},
+ {"setLocalPasswordConfirmPlaceholder",
+ IDS_AUTH_SETUP_SET_LOCAL_PASSWORD_CONFIRM_PLACEHOLDER},
+ {"setLocalPasswordMinCharsHint",
+ IDS_AUTH_SETUP_SET_LOCAL_PASSWORD_MIN_CHARS_HINT},
+ {"setLocalPasswordNoMatchError",
+ IDS_AUTH_SETUP_SET_LOCAL_PASSWORD_NO_MATCH_ERROR},
};
html_source->AddLocalizedStrings(kLocalizedStrings);
@@ -328,6 +354,9 @@ void AddFingerprintResources(content::WebUIDataSource* html_source,
html_source->AddBoolean("fingerprintUnlockEnabled",
are_fingerprint_settings_allowed);
+ html_source->AddResourcePath("fingerprint_scanner_animation.json",
+ IDR_FINGERPRINT_DEFAULT_ANIMATION);
+
if (are_fingerprint_settings_allowed)
quick_unlock::AddFingerprintResources(html_source);
@@ -434,12 +463,21 @@ void AddSyncControlsStrings(content::WebUIDataSource* html_source) {
}
void AddUsersStrings(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
{"usersModifiedByOwnerLabel", IDS_SETTINGS_USERS_MODIFIED_BY_OWNER_LABEL},
- {"guestBrowsingLabel", IDS_SETTINGS_USERS_GUEST_BROWSING_LABEL},
+ {"guestBrowsingLabel",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_USERS_GUEST_BROWSING_LABEL
+ : IDS_SETTINGS_USERS_GUEST_BROWSING_LABEL},
{"settingsManagedLabel", IDS_SETTINGS_USERS_MANAGED_LABEL},
{"showOnSigninLabel", IDS_SETTINGS_USERS_SHOW_ON_SIGNIN_LABEL},
- {"restrictSigninLabel", IDS_SETTINGS_USERS_RESTRICT_SIGNIN_LABEL},
+ {"restrictSigninLabel",
+ kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_USERS_RESTRICT_SIGNIN_LABEL
+ : IDS_SETTINGS_USERS_RESTRICT_SIGNIN_LABEL},
+ {"restrictSigninDescription",
+ IDS_OS_SETTINGS_REVAMP_USERS_RESTRICT_SIGNIN_DESCRIPTION},
{"deviceOwnerLabel", IDS_SETTINGS_USERS_DEVICE_OWNER_LABEL},
{"removeUserTooltip", IDS_SETTINGS_USERS_REMOVE_USER_TOOLTIP},
{"userRemovedMessage", IDS_SETTINGS_USERS_USER_REMOVED_MESSAGE},
@@ -470,9 +508,6 @@ void AddParentalControlStrings(content::WebUIDataSource* html_source,
html_source->AddBoolean("showParentalControls",
are_parental_control_settings_allowed);
-
- bool is_child = user_manager::UserManager::Get()->IsLoggedInAsChildUser();
- html_source->AddBoolean("isChild", is_child);
}
bool IsSameAccount(const ::account_manager::AccountKey& account_key,
@@ -649,7 +684,7 @@ mojom::SearchResultIcon PeopleSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kAvatar;
}
-std::string PeopleSection::GetSectionPath() const {
+const char* PeopleSection::GetSectionPath() const {
return mojom::kPeopleSectionPath;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/people_section.h b/chromium/chrome/browser/ui/webui/settings/ash/people_section.h
index 5d378dd6570..7b353165bb1 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/people_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/people_section.h
@@ -51,18 +51,18 @@ class PeopleSection : public OsSettingsSection,
PrefService* pref_service);
~PeopleSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
// AccountManagerFacade::Observer:
void OnAccountUpserted(const ::account_manager::Account& account) override;
void OnAccountRemoved(const ::account_manager::Account& account) override;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.cc b/chromium/chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.cc
index efbb6e34add..01a89dcb991 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.cc
@@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h"
+#include "chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/ash/login/login_pref_names.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/common/pref_names.h"
+#include "components/metrics/metrics_service.h"
#include "components/prefs/scoped_user_pref_update.h"
namespace ash::settings {
@@ -41,10 +44,12 @@ void LogDurationMetric(const char* metric_name, base::TimeDelta duration) {
} // namespace
PerSessionSettingsUserActionTracker::PerSessionSettingsUserActionTracker(
- PrefService* pref_service)
+ PrefService* profile_pref_service)
: metric_start_time_(base::TimeTicks::Now()),
window_last_active_timestamp_(base::TimeTicks::Now()),
- pref_service_(pref_service) {}
+ profile_pref_service_(profile_pref_service) {
+ DCHECK(profile_pref_service_);
+}
PerSessionSettingsUserActionTracker::~PerSessionSettingsUserActionTracker() {
RecordPageActiveTime();
@@ -54,6 +59,46 @@ PerSessionSettingsUserActionTracker::~PerSessionSettingsUserActionTracker() {
base::UmaHistogramCounts1000(
"ChromeOS.Settings.NumUniqueSettingsChanged.PerSession",
changed_settings_.size());
+}
+
+bool PerSessionSettingsUserActionTracker::HasUserMetricsConsent() {
+ DCHECK(g_browser_process);
+
+ metrics::MetricsService* metrics_service_ =
+ g_browser_process->metrics_service();
+
+ // Metrics consent can be either per-device, ie. the profile that is the owner
+ // of the device, or per-user. If no per-user consent is provided, we will use
+ // the device settings.
+ if (metrics_service_->GetCurrentUserMetricsConsent().has_value()) {
+ return metrics_service_->GetCurrentUserMetricsConsent().value();
+ }
+
+ // Return the device setting.
+ return ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
+}
+
+void PerSessionSettingsUserActionTracker::
+ SetHasUserEverRevokedMetricsConsent() {
+ bool metric_consent_pref_value_ = HasUserMetricsConsent();
+
+ // We will not keep a record of the user's total number of unique Settings
+ // changed if they have turned off UMA. We will not transmit the old data when
+ // the user revokes their consent for UMA. The pref gets cleared and will
+ // never record the Device Lifetime metric again.
+ if (profile_pref_service_
+ ->FindPreference(::prefs::kHasEverRevokedMetricsConsent)
+ ->IsDefaultValue()) {
+ profile_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent,
+ !metric_consent_pref_value_);
+ } else if (!metric_consent_pref_value_) {
+ profile_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent,
+ true);
+ }
+}
+
+void PerSessionSettingsUserActionTracker::RecordLifetimeMetricToPref() {
+ SetHasUserEverRevokedMetricsConsent();
// The pref kHasResetFirst7DaysSettingsUsedCount indicates whether the pref
// kTotalUniqueOsSettingsChanged has been cleared once after 1 week has passed
@@ -62,66 +107,43 @@ PerSessionSettingsUserActionTracker::~PerSessionSettingsUserActionTracker() {
// is the first time since one week after OOBE that the user has opened and
// changed Settings. In this case, clear the pref
// kTotalUniqueOsSettingsChanged to prepare it for the
- // ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.SubsequentWeeks
+ // ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.SubsequentWeeks
// histogram.
// NOTE: prefs::kOobeOnboardingTime does not exist for users in guest mode.
- if (!pref_service_->GetBoolean(
+ if (!profile_pref_service_->GetBoolean(
::prefs::kHasResetFirst7DaysSettingsUsedCount) &&
- pref_service_->HasPrefPath(prefs::kOobeOnboardingTime) &&
+ profile_pref_service_->HasPrefPath(prefs::kOobeOnboardingTime) &&
!IsTodayInFirst7Days()) {
- pref_service_->SetBoolean(::prefs::kHasResetFirst7DaysSettingsUsedCount,
- true);
+ profile_pref_service_->SetBoolean(
+ ::prefs::kHasResetFirst7DaysSettingsUsedCount, true);
ClearTotalUniqueSettingsChangedPref();
}
- // Record number of unique settings changed in this session.
- absl::optional<int> total_unique_settings_changed_count =
- UpdateSettingsPrefTotalUniqueChanged();
-
- // If the number of total unique setting used increased, flagged by the
- // optional variable total_unique_settings_changed_count having a value, add
- // the datapoint to the histogram.
- // NOTE: prefs::kOobeOnboardingTime does not exist for users in guest mode.
- if (pref_service_->HasPrefPath(prefs::kOobeOnboardingTime) &&
- total_unique_settings_changed_count.has_value()) {
- if (IsTodayInFirst7Days()) {
- base::UmaHistogramCounts1000(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.FirstWeek",
- total_unique_settings_changed_count.value());
- } else {
- base::UmaHistogramCounts1000(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime."
- "SubsequentWeeks",
- total_unique_settings_changed_count.value());
- }
- // Store the total unique Settings changed in .DeviceLifetime histogram.
- base::UmaHistogramCounts1000(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.Total",
- total_unique_settings_changed_count.value());
- }
+ UpdateSettingsPrefTotalUniqueChanged();
}
bool PerSessionSettingsUserActionTracker::IsTodayInFirst7Days() {
// The pref kOobeOnboardingTime does not get set for users in guest mode.
// Because we are accessing the value of the pref, we must ensure that it does
// exist.
- DCHECK(pref_service_->HasPrefPath(prefs::kOobeOnboardingTime));
- return base::Time::Now() -
- pref_service_->GetTime(::ash::prefs::kOobeOnboardingTime) <=
+ DCHECK(profile_pref_service_->HasPrefPath(prefs::kOobeOnboardingTime));
+ return base::Time::Now() - profile_pref_service_->GetTime(
+ ::ash::prefs::kOobeOnboardingTime) <=
kOneWeek;
}
void PerSessionSettingsUserActionTracker::
ClearTotalUniqueSettingsChangedPref() {
- pref_service_->ClearPref(::prefs::kTotalUniqueOsSettingsChanged);
+ profile_pref_service_->ClearPref(::prefs::kTotalUniqueOsSettingsChanged);
}
void PerSessionSettingsUserActionTracker::RecordPageFocus() {
const base::TimeTicks now = base::TimeTicks::Now();
window_last_active_timestamp_ = now;
- if (last_blur_timestamp_.is_null())
+ if (last_blur_timestamp_.is_null()) {
return;
+ }
// Log the duration of being blurred.
const base::TimeDelta blurred_duration = now - last_blur_timestamp_;
@@ -167,6 +189,10 @@ void PerSessionSettingsUserActionTracker::RecordSettingChange(
if (setting.has_value()) {
changed_settings_.insert(
base::NumberToString(static_cast<int>(setting.value())));
+
+ // Record the total unique Settings changed to the histogram
+ // ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.{Time}.
+ RecordLifetimeMetricToPref();
}
base::TimeTicks now = base::TimeTicks::Now();
@@ -174,8 +200,9 @@ void PerSessionSettingsUserActionTracker::RecordSettingChange(
// If it has been less than |kMinSubsequentChange| since the last recorded
// setting change, this change is discarded. See https://crbug.com/1073714
// for details.
- if (now - last_record_setting_changed_timestamp_ < kMinSubsequentChange)
+ if (now - last_record_setting_changed_timestamp_ < kMinSubsequentChange) {
return;
+ }
base::UmaHistogramCounts1000(
"ChromeOS.Settings.NumClicksUntilChange.SubsequentChange",
@@ -213,34 +240,25 @@ void PerSessionSettingsUserActionTracker::ResetMetricsCountersAndTimestamp() {
num_searches_since_start_time_ = 0u;
}
-absl::optional<int>
-PerSessionSettingsUserActionTracker::UpdateSettingsPrefTotalUniqueChanged() {
+void PerSessionSettingsUserActionTracker::
+ UpdateSettingsPrefTotalUniqueChanged() {
// Fetch the dictionary from the pref.
ScopedDictPrefUpdate total_unique_settings_changed_(
- pref_service_, ::prefs::kTotalUniqueOsSettingsChanged);
+ profile_pref_service_, ::prefs::kTotalUniqueOsSettingsChanged);
base::Value::Dict& pref_data = total_unique_settings_changed_.Get();
- int current_count = pref_data.size();
// Set the dictionary.
// Value is a constant 1 since we only want to know which Setting has been
// used, not how many times it has been used.
+ //
+ // The value of pref_data will automatically get stored to
+ // profile_pref_service_ upon destruction.
constexpr int value = 1;
for (const std::string& setting_string : changed_settings_) {
if (!pref_data.contains(setting_string)) {
pref_data.Set(setting_string, value);
}
}
-
- int new_count = pref_data.size();
-
- // If the new size of the pref dictionary is the same as before, we do not
- // want to record that in UMA so we will return a nullopt to flag not to add
- // to histogram bucket.
- //
- // The value of pref_data will automatically get stored to pref_service_ upon
- // destruction.
- return current_count == new_count ? absl::nullopt
- : absl::optional<int>{new_count};
}
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h b/chromium/chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.h
index 1bf9a1d1644..d13ffaa16ac 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.h
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
#include <set>
+#include "ash/webui/settings/public/constants/setting.mojom.h"
#include "base/time/time.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
#include "components/prefs/pref_service.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -20,7 +20,11 @@ namespace ash::settings {
// should be created for that new session.
class PerSessionSettingsUserActionTracker {
public:
- explicit PerSessionSettingsUserActionTracker(PrefService* pref_service);
+ // The parameter must be specifically a per-user profile pref, as we will be
+ // retrieving and setting profile-specific prefs in this class which do not
+ // exist in local prefs.
+ explicit PerSessionSettingsUserActionTracker(
+ PrefService* profile_pref_service);
PerSessionSettingsUserActionTracker(
const PerSessionSettingsUserActionTracker& other) = delete;
PerSessionSettingsUserActionTracker& operator=(
@@ -57,8 +61,8 @@ class PerSessionSettingsUserActionTracker {
// Clears the pref kTotalUniqueOsSettingsChanged after 7 days have passed
// since the user finished OOBE. We will track the changes made within the
// first week in
- // ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.FirstWeek, and
- // ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.SubsequentWeeks
+ // ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek, and
+ // ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.SubsequentWeeks
// for the weeks following the first week.
void ClearTotalUniqueSettingsChangedPref();
@@ -67,12 +71,11 @@ class PerSessionSettingsUserActionTracker {
// which is set once the user finished the OOBE.
bool IsTodayInFirst7Days();
+ void RecordLifetimeMetricToPref();
void ResetMetricsCountersAndTimestamp();
-
- // Returns the size of the pref dict if it changes. Otherwise, no value will
- // get returned if if there were no new unique settings changed in the
- // session.
- absl::optional<int> UpdateSettingsPrefTotalUniqueChanged();
+ void UpdateSettingsPrefTotalUniqueChanged();
+ void SetHasUserEverRevokedMetricsConsent();
+ bool HasUserMetricsConsent();
// Time at which the last setting change metric was recorded since the window
// has been focused, or null if no setting change has been recorded since the
@@ -107,9 +110,9 @@ class PerSessionSettingsUserActionTracker {
// The point in time which the Settings page was last active and in focus.
base::TimeTicks window_last_active_timestamp_;
- raw_ptr<PrefService> pref_service_;
+ raw_ptr<PrefService> profile_pref_service_;
};
} // namespace ash::settings
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker_unittest.cc
index ddc32391b90..654ce1c3c9c 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker_unittest.cc
@@ -2,45 +2,127 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h"
+#include "chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.h"
+#include "ash/constants/ash_features.h"
+#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "chrome/browser/ash/login/login_pref_names.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/metrics/chrome_metrics_service_client.h"
+#include "chrome/browser/ui/webui/settings/ash/os_settings_metrics_provider.h"
+#include "chrome/common/pref_names.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/ash/components/login/login_state/login_state.h"
+#include "components/account_id/account_id.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/test/test_enabled_state_provider.h"
+#include "components/metrics/test/test_metrics_service_client.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "components/user_manager/user.h"
#include "testing/gtest/include/gtest/gtest.h"
using chromeos::settings::mojom::Setting;
+namespace {
+
+class TestUserMetricsServiceClient
+ : public ::metrics::TestMetricsServiceClient {
+ public:
+ absl::optional<bool> GetCurrentUserMetricsConsent() const override {
+ return current_user_metrics_consent_;
+ }
+
+ void UpdateCurrentUserMetricsConsent(bool metrics_consent) override {
+ current_user_metrics_consent_ = metrics_consent;
+ }
+
+ private:
+ bool current_user_metrics_consent_ = true;
+};
+} // namespace
+
namespace ash::settings {
constexpr char kProfileName[] = "user@gmail.com";
class PerSessionSettingsUserActionTrackerTest : public testing::Test {
protected:
- PerSessionSettingsUserActionTrackerTest() = default;
+ PerSessionSettingsUserActionTrackerTest() {
+ feature_list_.InitAndEnableFeature(::ash::features::kPerUserMetrics);
+ }
~PerSessionSettingsUserActionTrackerTest() override = default;
void SetUp() override {
+ std::unique_ptr<FakeChromeUserManager> user_manager =
+ std::make_unique<FakeChromeUserManager>();
+ FakeChromeUserManager* fake_user_manager = user_manager.get();
+ scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
+ std::move(user_manager));
+
+ LoginState::Initialize();
+
profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
ASSERT_TRUE(profile_manager_->SetUp());
- testing_profile_ = profile_manager_->CreateTestingProfile(kProfileName);
+ LoginTestUser(fake_user_manager);
+
test_pref_service_ = testing_profile_->GetPrefs();
+
+ // MetricsService.
+ local_state_ = std::make_unique<TestingPrefServiceSimple>();
+ metrics::MetricsService::RegisterPrefs(local_state_->registry());
+ test_enabled_state_provider_ =
+ std::make_unique<metrics::TestEnabledStateProvider>(true, true);
+ test_metrics_state_manager_ = metrics::MetricsStateManager::Create(
+ local_state_.get(), test_enabled_state_provider_.get(), std::wstring(),
+ base::FilePath());
+ test_metrics_service_client_ =
+ std::make_unique<TestUserMetricsServiceClient>();
+ test_metrics_service_ = std::make_unique<metrics::MetricsService>(
+ test_metrics_state_manager_.get(), test_metrics_service_client_.get(),
+ local_state_.get());
+ TestingBrowserProcess::GetGlobal()->SetMetricsService(
+ test_metrics_service_.get());
+
+ // Needs to be set for metrics service.
+ base::SetRecordActionTaskRunner(
+ task_environment_.GetMainThreadTaskRunner());
+
tracker_ = std::make_unique<PerSessionSettingsUserActionTracker>(
test_pref_service_);
+
+ tracker_metrics_provider_ = std::make_unique<OsSettingsMetricsProvider>();
}
void TearDown() override {
- if (tracker_) {
- tracker_.reset();
- }
- profile_manager_->DeleteTestingProfile(kProfileName);
- testing_profile_ = nullptr;
- profile_manager_.reset();
+ TestingBrowserProcess::GetGlobal()->SetMetricsService(nullptr);
+
+ LoginState::Shutdown();
+ }
+
+ // Creates regular_user profile and user then sets that account to active.
+ void LoginTestUser(FakeChromeUserManager* fake_user_manager) {
+ const AccountId id = AccountId::FromUserEmail(kProfileName);
+ auto* user = fake_user_manager->AddUser(id);
+ testing_profile_ = profile_manager_->CreateTestingProfile(kProfileName);
+ ProfileHelper::Get()->SetUserToProfileMappingForTesting(user,
+ testing_profile_);
+ LoginAndSetActiveUserInUserManager(id, fake_user_manager);
+ }
+
+ // Helper to set active user in the UserManager.
+ void LoginAndSetActiveUserInUserManager(
+ const AccountId& id,
+ FakeChromeUserManager* fake_user_manager) {
+ fake_user_manager->LoginUser(id);
+ fake_user_manager->SwitchActiveUser(id);
}
std::string SettingAsIntString(Setting setting) {
@@ -50,10 +132,26 @@ class PerSessionSettingsUserActionTrackerTest : public testing::Test {
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::HistogramTester histogram_tester_;
- TestingProfile* testing_profile_;
- raw_ptr<PrefService> test_pref_service_;
std::unique_ptr<TestingProfileManager> profile_manager_;
+ raw_ptr<TestingProfile> testing_profile_;
+ raw_ptr<PrefService> test_pref_service_;
std::unique_ptr<PerSessionSettingsUserActionTracker> tracker_;
+ std::unique_ptr<OsSettingsMetricsProvider> tracker_metrics_provider_;
+ metrics::ChromeUserMetricsExtension uma_proto_;
+
+ // This needs to be initialized before any tasks running on other threads
+ // access the feature list, and destroyed after |task_environment_|, to avoid
+ // data races.
+ base::test::ScopedFeatureList feature_list_;
+
+ // MetricsService.
+ std::unique_ptr<TestingPrefServiceSimple> local_state_;
+ std::unique_ptr<metrics::TestEnabledStateProvider>
+ test_enabled_state_provider_;
+ std::unique_ptr<metrics::MetricsStateManager> test_metrics_state_manager_;
+ std::unique_ptr<TestUserMetricsServiceClient> test_metrics_service_client_;
+ std::unique_ptr<metrics::MetricsService> test_metrics_service_;
+ std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
};
TEST_F(PerSessionSettingsUserActionTrackerTest, TestRecordMetrics) {
@@ -222,6 +320,8 @@ TEST_F(PerSessionSettingsUserActionTrackerTest, TestEndSessionWithBlur) {
}
TEST_F(PerSessionSettingsUserActionTrackerTest, TestUniqueChangedSettings) {
+ // Simulate that the user has granted UMA consent by default.
+ test_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent, false);
std::set<std::string> expected_set;
// Flip the WiFi toggle in Settings, this is a unique Setting that is changing
@@ -298,9 +398,16 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
// Simulate that the user has taken OOBE.
test_pref_service_->SetTime(::ash::prefs::kOobeOnboardingTime,
base::Time::Now());
+ // Simulate that the user has granted UMA consent by default.
+ test_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent, false);
std::set<std::string> expected_set;
+ // We will record the total number of unique Settings changed to the histogram
+ // ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.{Time}
+ // every time a user changes a Setting, no matter if the number of unique
+ // Settings used has increased or not.
+
// Flip the WiFi toggle in Settings, this is a unique Setting that is changing
// so the number of unique settings that have been changed increases by 1 for
// a total of 1.
@@ -308,23 +415,31 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
expected_set = {SettingAsIntString(Setting::kWifiOnOff)};
EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
- // Destruct tracker_ to trigger recording the data to the histogram.
+ // Destruct tracker_.
tracker_.reset();
+ // Trigger recording the data to the histogram. We will now record the unique
+ // changes made to .FirstWeek and .Total histogram.
+ //
+ // .FirstWeek: 1 total unique settings changed with 1 count.
+ //
+ // .Total: 1 total unique settings changed with 1 count.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
// The time is still in the first week, so the data gets recorded to
// .FirstWeek histogram.
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.FirstWeek",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
/*sample=*/1,
/*count=*/1);
// There are no data in the .SubsequentWeeks histogram.
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime."
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
"SubsequentWeeks",
/*sample=*/1,
/*count=*/0);
// Overall total unique Settings changed in the lifetime of the Device.
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.Total",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.Total",
/*sample=*/1,
/*count=*/1);
@@ -350,21 +465,31 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
expected_set = {SettingAsIntString(Setting::kDoNotDisturbOnOff)};
EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
- // Destruct tracker_ to trigger recording the data to the histogram.
+ // Destruct tracker_ to simulate Settings app closing.
tracker_.reset();
+
+ // Trigger recording the data to the histogram. We will now record the unique
+ // changes made to .SubsequentWeeks and .Total histogram.
+ //
+ // .SubsequentWeeks: 1 total unique settings changed with 1 count.
+ //
+ // .Total: We are recording that 1 unique settings has changed, so the count
+ // for sample 1 is now accumulated to 2.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
// .FirstWeek will not change
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.FirstWeek",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
/*sample=*/1,
/*count=*/1);
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime."
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
"SubsequentWeeks",
/*sample=*/1,
/*count=*/1);
// Overall total unique Settings changed in the lifetime of the Device.
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.Total",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.Total",
/*sample=*/1,
/*count=*/2);
@@ -377,6 +502,17 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
expected_set = {};
EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
+ // Simulate that metrics provider uploads the data.
+ // Trigger recording the data to the histogram. We will now record the unique
+ // changes made to .SubsequentWeeks and .Total histogram.
+ //
+ // .SubsequentWeeks: 1 total unique settings changed with accumulated 2
+ // counts.
+ //
+ // .Total: We are recording that 1 unique settings has changed, so the count
+ // for sample 1 is now accumulated to 3.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
// Flip the Do Not Disturb and WiFi toggles in Settings, this is a unique
// Setting that is changing so the number of unique settings that have been
// changed increases by 1. Note that we are still past the 1 week point, so we
@@ -387,30 +523,41 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
SettingAsIntString(Setting::kWifiOnOff)};
EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
- // Destruct tracker_ to trigger recording the data to the histogram.
+ // Destruct tracker_.
tracker_.reset();
+ // Trigger recording the data to the histogram. We will now record the unique
+ // changes made to .SubsequentWeeks and .Total histogram.
+ //
+ // .SubsequentWeeks: We will now have a new sample since we have 2 different
+ // unique settings, so sample 1 remains at 2, and sample 2 now has a new count
+ // of 1.
+ //
+ // .Total: We are recording that 1 unique settings has changed, so the count
+ // for sample 1 remains at 3, and sample 2 is now 1.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
// .FirstWeek will not change
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.FirstWeek",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
/*sample=*/1,
/*count=*/1);
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime."
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
"SubsequentWeeks",
/*sample=*/1,
- /*count=*/1);
+ /*count=*/2);
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime."
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
"SubsequentWeeks",
/*sample=*/2,
/*count=*/1);
// Overall total unique Settings changed in the lifetime of the Device.
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.Total",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.Total",
/*sample=*/1,
- /*count=*/2);
+ /*count=*/3);
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.Total",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.Total",
/*sample=*/2,
/*count=*/1);
@@ -424,37 +571,64 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
// unique settings that have been changed does not increase. The bucket sample
// 2 should now have 2 counts.
tracker_->RecordSettingChange(Setting::kDoNotDisturbOnOff);
+
+ // Simulate that metrics provider uploads the data.
+ //
+ // Trigger recording the data to the histogram. We will now record the unique
+ // changes made to .SubsequentWeeks and .Total histogram.
+ //
+ // .SubsequentWeeks: No new unique change has been made, so sample 1 remains
+ // at 2, and sample 2 now has a new count of 2.
+ //
+ // .Total: No new unique change has been made, so the count for sample 1
+ // remains at 3, and sample 2 has a new count of 2.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
+ // User is still in the current Settings session.
tracker_->RecordSettingChange(Setting::kWifiOnOff);
tracker_->RecordSettingChange(Setting::kDoNotDisturbOnOff);
+
// expected_set will not change
EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
- // Destruct tracker_ to trigger recording the data to the histogram.
+ // Destruct tracker_ to simulate Settings app closing.
tracker_.reset();
+ // Simulate that metrics provider uploads the data.
+ //
+ // Trigger recording the data to the histogram. We will now record the unique
+ // changes made to .SubsequentWeeks and .Total histogram.
+ //
+ // .SubsequentWeeks: No new unique change has been made, so sample 1 remains
+ // at 2, and sample 2 now has a new count of 3.
+ //
+ // .Total: No new unique change has been made, so the count for sample 1
+ // remains at 3, and sample 2 has a new count of 3.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.FirstWeek",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
/*sample=*/1,
/*count=*/1);
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime."
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
"SubsequentWeeks",
/*sample=*/1,
- /*count=*/1);
+ /*count=*/2);
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime."
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
"SubsequentWeeks",
/*sample=*/2,
- /*count=*/1);
+ /*count=*/3);
// Overall total unique Settings changed in the lifetime of the Device.
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.Total",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.Total",
/*sample=*/1,
- /*count=*/2);
+ /*count=*/3);
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.Total",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.Total",
/*sample=*/2,
- /*count=*/1);
+ /*count=*/3);
}
TEST_F(PerSessionSettingsUserActionTrackerTest,
@@ -462,6 +636,8 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
// Simulate that the user has taken OOBE.
test_pref_service_->SetTime(::ash::prefs::kOobeOnboardingTime,
base::Time::Now());
+ // Simulate that the user has granted UMA consent by default.
+ test_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent, false);
std::set<std::string> expected_set;
// Flip the Do Not Disturb and WiFi toggles in Settings, these are unique
@@ -473,21 +649,29 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
SettingAsIntString(Setting::kWifiOnOff)};
EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
- // Destruct tracker_ to trigger recording the data to the histogram.
+ // Destruct tracker_.
tracker_.reset();
+ // Trigger recording the data to the histogram. We will now record the unique
+ // changes made to .FirstWeek and .Total histogram.
+ //
+ // .FirstWeek: 2 total unique settings changed with 1 count.
+ //
+ // .Total: 2 total unique settings changed with 1 count.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.FirstWeek",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
/*sample=*/2,
/*count=*/1);
// This is within the first week, no data should be recorded in the
// .SubsequentWeeks histogram
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime."
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
"SubsequentWeeks",
/*sample=*/2,
/*count=*/0);
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.Total",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.Total",
/*sample=*/2,
/*count=*/1);
}
@@ -497,6 +681,8 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
// Simulate that the user has taken OOBE.
test_pref_service_->SetTime(::ash::prefs::kOobeOnboardingTime,
base::Time::Now());
+ // Simulate that the user has granted UMA consent by default.
+ test_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent, false);
std::set<std::string> expected_set;
// Fast forward the time for 7 days and 1 second. We will now record data to
// .SubsequentWeeks instead of .FirstWeek.
@@ -511,27 +697,38 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
SettingAsIntString(Setting::kWifiOnOff)};
EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
- // Destruct tracker_ to trigger recording the data to the histogram.
+ // Destruct tracker_.
tracker_.reset();
- histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime."
- "SubsequentWeeks",
- /*sample=*/2,
- /*count=*/1);
+
+ // Trigger recording the data to the histogram. We will now record the unique
+ // changes made to .SubsequentWeeks and .Total histogram.
+ //
+ // .SubsequentWeeks: 2 total unique settings changed with 2 count.
+ //
+ // .Total: 2 total unique settings changed with 2 count.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
// This is after the first week, no data should be recorded in the
// .FirstWeek histogram
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.FirstWeek",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
/*sample=*/2,
/*count=*/0);
histogram_tester_.ExpectBucketCount(
- "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime.Total",
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/2,
+ /*count=*/1);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.Total",
/*sample=*/2,
/*count=*/1);
}
TEST_F(PerSessionSettingsUserActionTrackerTest,
TestNoTimeDeltaOpenCloseSettings) {
+ // Simulate that the user has granted UMA consent by default.
+ test_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent, false);
// Focus on page, close the page immediately. total_time_session_active_
// should be 0 seconds.
tracker_->RecordPageFocus();
@@ -547,6 +744,8 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
TEST_F(PerSessionSettingsUserActionTrackerTest,
TestTotalTimeSessionActiveWithBlurAndFocus) {
+ // Simulate that the user has granted UMA consent by default.
+ test_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent, false);
// Focus on page, wait for 16 seconds to pass, and blur the page.
// total active time should be 16 seconds.
tracker_->RecordPageFocus();
@@ -585,6 +784,8 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
TEST_F(PerSessionSettingsUserActionTrackerTest,
TestMultipleTotalTimeSessionActive) {
+ // Simulate that the user has granted UMA consent by default.
+ test_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent, false);
// Focus on page, wait for 22 seconds to pass.
tracker_->RecordPageFocus();
task_environment_.FastForwardBy(base::Seconds(22));
@@ -637,4 +838,287 @@ TEST_F(PerSessionSettingsUserActionTrackerTest,
/*count=*/2);
}
+TEST_F(PerSessionSettingsUserActionTrackerTest,
+ TestRecordedDataWhenUMAConsentRevokedAndRegrantedWithSettingsChange) {
+ // Simulate that the user has taken OOBE.
+ test_pref_service_->SetTime(::ash::prefs::kOobeOnboardingTime,
+ base::Time::Now());
+ // Simulate the initial state, ie the user has not yet set their consent pref.
+ test_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent, false);
+
+ // Simulate that the user has granted UMA consent initially.
+ // NOTE: revoking UMA consent will clear the pref that stores the current
+ // unique settings that the user has used. Now that the consent has been
+ // granted, we will record that Settings changes to .DeviceLifetime histogram.
+ test_metrics_service_client_->UpdateCurrentUserMetricsConsent(true);
+ std::set<std::string> expected_set;
+
+ tracker_->RecordPageFocus();
+
+ // Flip the WiFi toggle in Settings, the is a unique Setting that is changing
+ // so the number of unique settings that have been changed is 1.
+ tracker_->RecordSettingChange(Setting::kWifiOnOff);
+ expected_set = {SettingAsIntString(Setting::kWifiOnOff)};
+ EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
+
+ // Destruct tracker_ to simulate Settings app closing.
+ tracker_.reset();
+
+ // Simulate that metrics provider uploads the data.
+ //
+ // Trigger recording the data to the histogram. We will now record the unique
+ // changes made to .FirstWeek and .Total histogram.
+ //
+ // .FirstWeek: 1 unique change has been made, so sample 1 will have a count
+ // of 1.
+ // .Total: 1 unique change has been made, so sample 1 will have a count of 1.
+ //
+ // Other samples in the three histograms .FirstWeek, .SubsequentWeeks, and
+ // .Total should have a count of 0 since no data has been recorded to them.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
+ /*sample=*/1,
+ /*count=*/1);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
+ /*sample=*/2,
+ /*count=*/0);
+ // This is within the first week, no data should be recorded in the
+ // .SubsequentWeeks histogram
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/1,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/2,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "Total",
+ /*sample=*/1,
+ /*count=*/1);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "Total",
+ /*sample=*/2,
+ /*count=*/0);
+
+ // Create a new PerSessionSettingsUserActionTracker to imitate a newly opened
+ // Settings page.
+ tracker_ =
+ std::make_unique<PerSessionSettingsUserActionTracker>(test_pref_service_);
+
+ // Simulate that the user has revoked UMA consent, no data will be recorded
+ // to UMA.
+ test_metrics_service_client_->UpdateCurrentUserMetricsConsent(false);
+
+ // Flip the WiFi toggle in Settings, the is a unique Setting that is changing
+ // so the number of unique settings that have been changed is 1.
+ tracker_->RecordSettingChange(Setting::kWifiOnOff);
+ expected_set = {SettingAsIntString(Setting::kWifiOnOff)};
+ EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
+
+ // Destruct tracker_.
+ tracker_.reset();
+ // The metrics provider only gets called to upload the data when the user has
+ // granted UMA consent.
+
+ // UMA consent has been revoked so no recording should take place and the
+ // histogram data should not change. the data in the histogram does not change
+ // since the user has not consented to sharing their info to UMA.
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
+ /*sample=*/1,
+ /*count=*/1);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
+ /*sample=*/2,
+ /*count=*/0);
+ // This is within the first week, no data should be recorded in the
+ // .SubsequentWeeks histogram
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/1,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/2,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "Total",
+ /*sample=*/1,
+ /*count=*/1);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "Total",
+ /*sample=*/2,
+ /*count=*/0);
+
+ // Create a new PerSessionSettingsUserActionTracker to imitate a newly opened
+ // Settings page.
+ tracker_ =
+ std::make_unique<PerSessionSettingsUserActionTracker>(test_pref_service_);
+
+ // Simulate that the user has re-granted UMA consent, no data will be recorded
+ // to UMA because they have previously revoked their consent and have made a
+ // change in Settings. The data we are recording is no longer accurate so we
+ // will no longer record the data to UMA. See
+ // ::prefs::kHasEverRevokedMetricsConsent for more information.
+ test_metrics_service_client_->UpdateCurrentUserMetricsConsent(true);
+
+ // Flip the WiFi toggle in Settings, the is a unique Setting that is changing
+ // so the number of unique settings that have been changed is 1.
+ tracker_->RecordSettingChange(Setting::kWifiOnOff);
+ expected_set = {SettingAsIntString(Setting::kWifiOnOff)};
+ EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
+
+ // Destruct tracker_.
+ tracker_.reset();
+ // Simulate that metrics provider uploads the data.
+ //
+ // Trigger recording the data to the histogram. UMA consent has been revoked
+ // before, so no recording should take place and the histogram data should not
+ // change.
+ //
+ // .FirstWeek: Sample 1 will remain at count of 1.
+ // .Total: Sample 1 will remain at count of 1.
+ //
+ // Other samples in the three histograms .FirstWeek, .SubsequentWeeks, and
+ // .Total should have a count of 0 since no data has been recorded to them.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
+ // the data in the histogram does not change since the user has revoked their
+ // consent for UMA at least once in the lifetime of their device.
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
+ /*sample=*/0,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
+ /*sample=*/1,
+ /*count=*/1);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
+ /*sample=*/2,
+ /*count=*/0);
+ // This is within the first week, no data should be recorded in the
+ // .SubsequentWeeks histogram
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/0,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/1,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/2,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "Total",
+ /*sample=*/0,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "Total",
+ /*sample=*/1,
+ /*count=*/1);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "Total",
+ /*sample=*/2,
+ /*count=*/0);
+}
+
+TEST_F(PerSessionSettingsUserActionTrackerTest,
+ TestRecordedDataWhenUMAConsentRevokedAndRegrantedWithoutSettingsChange) {
+ // Simulate that the user has taken OOBE.
+ test_pref_service_->SetTime(::ash::prefs::kOobeOnboardingTime,
+ base::Time::Now());
+ // Simulate the initial state, ie the user has not yet set their consent pref.
+ test_pref_service_->SetBoolean(::prefs::kHasEverRevokedMetricsConsent, false);
+
+ // Simulate that the user has granted UMA consent initially.
+ // NOTE: revoking UMA consent will clear the pref that stores the current
+ // unique settings that the user has used. Now that the consent has been
+ // granted, we will record that Settings changes to .DeviceLifetime histogram.
+ test_metrics_service_client_->UpdateCurrentUserMetricsConsent(true);
+ std::set<std::string> expected_set;
+
+ // Simulate that the user has revoked UMA consent, the function
+ // ProvideCurrentSessionData will not get called and no data will be recorded
+ // to UMA.
+ test_metrics_service_client_->UpdateCurrentUserMetricsConsent(false);
+
+ // Simulate that the user has re-granted UMA consent, no data will be recorded
+ // to UMA because they have previously revoked their consent. Once a user
+ // revokes their consent, re-granting it without making a Setting change will
+ // result in recording of their data, since we will not have any discrepancy
+ // in the data we are recording.
+ test_metrics_service_client_->UpdateCurrentUserMetricsConsent(true);
+
+ // Flip the WiFi toggle in Settings, the is a unique Setting that is changing
+ // so the number of unique settings that have been changed is 1.
+ tracker_->RecordSettingChange(Setting::kWifiOnOff);
+ expected_set = {SettingAsIntString(Setting::kWifiOnOff)};
+ EXPECT_EQ(expected_set, tracker_->GetChangedSettingsForTesting());
+
+ // Destruct tracker_.
+ tracker_.reset();
+
+ // Simulate that metrics provider uploads the data.
+ //
+ // .FirstWeek: Sample 1 will have a count of 1
+ // .Total: Sample 1 will have a count of 1.
+ //
+ // All other samples in the three histograms .FirstWeek, .SubsequentWeeks, and
+ // .Total should have a count of 0 since no data has been recorded to them.
+ tracker_metrics_provider_->ProvideCurrentSessionData(&uma_proto_);
+
+ // the data in the histogram does not change since the user has revoked their
+ // consent for UMA at least once in the lifetime of their device.
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
+ /*sample=*/0,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.FirstWeek",
+ /*sample=*/1,
+ /*count=*/1);
+ // This is within the first week, no data should be recorded in the
+ // .SubsequentWeeks histogram
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/0,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "SubsequentWeeks",
+ /*sample=*/1,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "Total",
+ /*sample=*/0,
+ /*count=*/0);
+ histogram_tester_.ExpectBucketCount(
+ "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2."
+ "Total",
+ /*sample=*/1,
+ /*count=*/1);
+}
+
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/personalization_hub_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/personalization_hub_handler.cc
index b9763420f2d..e62a93f2055 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/personalization_hub_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/personalization_hub_handler.cc
@@ -7,7 +7,7 @@
#include "ash/constants/ash_features.h"
#include "ash/constants/personalization_entry_point.h"
#include "base/functional/bind.h"
-#include "chrome/browser/ash/web_applications/personalization_app/personalization_app_metrics.h"
+#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_metrics.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
#include "content/public/browser/web_ui.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/personalization_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/personalization_section.cc
index 20f57f1dd79..ab194503e03 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/personalization_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/personalization_section.cc
@@ -4,9 +4,10 @@
#include "chrome/browser/ui/webui/settings/ash/personalization_section.h"
+#include "ash/constants/ash_features.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/personalization_hub_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_ui.h"
@@ -24,15 +25,20 @@ PersonalizationSection::PersonalizationSection(
Profile* profile,
SearchTagRegistry* search_tag_registry,
PrefService* pref_service)
- : OsSettingsSection(profile, search_tag_registry) {}
+ : OsSettingsSection(profile, search_tag_registry),
+ isRevampEnabled_(ash::features::IsOsSettingsRevampWayfindingEnabled()) {}
PersonalizationSection::~PersonalizationSection() = default;
void PersonalizationSection::AddLoadTimeData(
content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
- {"personalizationPageTitle", IDS_OS_SETTINGS_PERSONALIZATION},
- {"personalizationHubTitle", IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB},
+ webui::LocalizedString kLocalizedStrings[] = {
+ {"personalizationPageTitle", isRevampEnabled_
+ ? IDS_OS_SETTINGS_REVAMP_PERSONALIZATION
+ : IDS_OS_SETTINGS_PERSONALIZATION},
+ {"personalizationHubTitle",
+ isRevampEnabled_ ? IDS_OS_SETTINGS_REVAMP_OPEN_PERSONALIZATION_HUB
+ : IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB},
{"personalizationHubSubtitle",
IDS_OS_SETTINGS_OPEN_PERSONALIZATION_HUB_SUBTITLE},
};
@@ -44,7 +50,8 @@ void PersonalizationSection::AddHandlers(content::WebUI* web_ui) {
}
int PersonalizationSection::GetSectionNameMessageId() const {
- return IDS_OS_SETTINGS_PERSONALIZATION;
+ return isRevampEnabled_ ? IDS_OS_SETTINGS_REVAMP_PERSONALIZATION
+ : IDS_OS_SETTINGS_PERSONALIZATION;
}
mojom::Section PersonalizationSection::GetSection() const {
@@ -55,7 +62,7 @@ mojom::SearchResultIcon PersonalizationSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kPaintbrush;
}
-std::string PersonalizationSection::GetSectionPath() const {
+const char* PersonalizationSection::GetSectionPath() const {
return mojom::kPersonalizationSectionPath;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/personalization_section.h b/chromium/chrome/browser/ui/webui/settings/ash/personalization_section.h
index a00499395c8..4326a4f38c2 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/personalization_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/personalization_section.h
@@ -29,19 +29,20 @@ class PersonalizationSection : public OsSettingsSection {
PrefService* pref_service);
~PersonalizationSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
PrefChangeRegistrar pref_change_registrar_;
+ bool isRevampEnabled_;
};
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/printing_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/printing_section.cc
index ec99d9f3a35..e16da3831ef 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/printing_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/printing_section.cc
@@ -5,10 +5,10 @@
#include "chrome/browser/ui/webui/settings/ash/printing_section.h"
#include "ash/constants/ash_features.h"
+#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
#include "base/no_destructor.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/cups_printers_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-forward.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/url_constants.h"
@@ -63,9 +63,12 @@ const std::vector<SearchConcept>& GetSavedPrintersSearchConcepts() {
}
const std::vector<SearchConcept>& GetPrintingManagementSearchConcepts() {
+ const char* url_path = ash::features::IsOsSettingsRevampWayfindingEnabled()
+ ? mojom::kPrintingDetailsSubpagePath
+ : mojom::kPrintingSectionPath;
static const base::NoDestructor<std::vector<SearchConcept>> tags({
{IDS_OS_SETTINGS_TAG_PRINT_MANAGEMENT,
- mojom::kPrintingSectionPath,
+ url_path,
mojom::SearchResultIcon::kPrinter,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSetting,
@@ -114,9 +117,16 @@ PrintingSection::~PrintingSection() {
}
void PrintingSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
{"printingPageTitle", IDS_SETTINGS_PRINT_AND_SCAN},
- {"cupsPrintersTitle", IDS_SETTINGS_PRINTING_CUPS_PRINTERS},
+ {"cupsPrintTitle", kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_PRINTING_CUPS_PRINT_TITLE
+ : IDS_SETTINGS_PRINTING_CUPS_PRINTERS},
+ {"cupsPrintDescription",
+ IDS_OS_SETTINGS_REVAMP_PRINTING_CUPS_PRINT_DESCRIPTION},
{"cupsPrintersLearnMoreLabel",
IDS_SETTINGS_PRINTING_CUPS_PRINTERS_LEARN_MORE_LABEL},
{"addCupsPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_ADD_PRINTER},
@@ -127,10 +137,7 @@ void PrintingSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
{"removePrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_REMOVE},
{"cupsPrintersViewPpd", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_VIEW_PPD},
{"setupPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTER_SETUP_BUTTON},
- {"setupPrinterAria",
- IDS_SETTINGS_PRINTING_CUPS_PRINTER_SETUP_BUTTON_ARIA},
{"savePrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTER_SAVE_BUTTON},
- {"savePrinterAria", IDS_SETTINGS_PRINTING_CUPS_PRINTER_SAVE_BUTTON_ARIA},
{"searchLabel", IDS_SETTINGS_PRINTING_CUPS_SEARCH_LABEL},
{"noSearchResults", IDS_SEARCH_NO_RESULTS},
{"printJobsTitle",
@@ -309,6 +316,7 @@ void PrintingSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
{"printerStatusStopped", IDS_SETTINGS_PRINTING_PRINTER_STATUS_STOPPED},
{"printerStatusTrayMissing",
IDS_SETTINGS_PRINTING_PRINTER_STATUS_TRAY_MISSING},
+ {"printerEntryAriaLabel", IDS_SETTINGS_PRINTING_PRINTER_ENTRY_ARIA_LABEL},
};
html_source->AddLocalizedStrings(kLocalizedStrings);
@@ -321,6 +329,8 @@ void PrintingSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
features::IsPrinterSettingsRevampEnabled());
html_source->AddBoolean("isPrinterSettingsPrinterStatusEnabled",
features::IsPrinterSettingsPrinterStatusEnabled());
+ html_source->AddBoolean("isPrintPreviewDiscoveredPrintersEnabled",
+ features::IsPrintPreviewDiscoveredPrintersEnabled());
}
void PrintingSection::AddHandlers(content::WebUI* web_ui) {
@@ -340,7 +350,7 @@ mojom::SearchResultIcon PrintingSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kPrinter;
}
-std::string PrintingSection::GetSectionPath() const {
+const char* PrintingSection::GetSectionPath() const {
return mojom::kPrintingSectionPath;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/printing_section.h b/chromium/chrome/browser/ui/webui/settings/ash/printing_section.h
index e2dedf74f18..0640886f83d 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/printing_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/printing_section.h
@@ -27,18 +27,18 @@ class PrintingSection : public OsSettingsSection,
CupsPrintersManager* printers_manager);
~PrintingSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
// CupsPrintersManager::Observer
void OnPrintersChanged(
chromeos::PrinterClass printer_class,
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.cc
index 7432a33af39..ee282f40f9a 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.cc
@@ -5,8 +5,10 @@
#include "chrome/browser/ui/webui/settings/ash/privacy_hub_handler.h"
#include "ash/constants/ash_features.h"
+#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
+#include "base/synchronization/condition_variable.h"
#include "chrome/browser/ash/privacy_hub/privacy_hub_hats_trigger.h"
#include "chrome/browser/ash/privacy_hub/privacy_hub_util.h"
#include "chrome/common/chrome_features.h"
@@ -28,10 +30,15 @@ void PrivacyHubHandler::RegisterMessages() {
base::BindRepeating(
&PrivacyHubHandler::HandleInitialMicrophoneSwitchState,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getCameraLedFallbackState",
+ base::BindRepeating(
+ &PrivacyHubHandler::HandleInitialCameraLedFallbackState,
+ base::Unretained(this)));
}
if (base::FeatureList::IsEnabled(
- ::features::kHappinessTrackingPrivacyHubBaseline)) {
+ ::features::kHappinessTrackingPrivacyHubPostLaunch)) {
web_ui()->RegisterMessageCallback(
"osPrivacyPageWasOpened",
base::BindRepeating(&PrivacyHubHandler::HandlePrivacyPageOpened,
@@ -53,47 +60,69 @@ void PrivacyHubHandler::NotifyJS(const std::string& event_name,
}
}
-void PrivacyHubHandler::HandleInitialMicrophoneSwitchState(
- const base::Value::List& args) {
- DCHECK(ash::features::IsCrosPrivacyHubEnabled());
- AllowJavascript();
-
- DCHECK_GE(1U, args.size()) << ": Did not expect arguments";
- DCHECK_EQ(1U, args.size()) << ": Callback ID is required";
- const auto& callback_id = args[0];
- const base::Value value =
- base::Value(privacy_hub_util::MicrophoneSwitchState());
-
- ResolveJavascriptCallback(callback_id, value);
-}
-
void PrivacyHubHandler::MicrophoneHardwareToggleChanged(bool muted) {
DCHECK(ash::features::IsCrosPrivacyHubEnabled());
NotifyJS("microphone-hardware-toggle-changed", base::Value(muted));
}
+void PrivacyHubHandler::SetPrivacyPageOpenedTimeStampForTesting(
+ base::TimeTicks time_stamp) {
+ privacy_page_opened_timestamp_ = time_stamp;
+}
+
void PrivacyHubHandler::HandlePrivacyPageOpened(const base::Value::List& args) {
DCHECK(args.empty());
DCHECK(base::FeatureList::IsEnabled(
- ::features::kHappinessTrackingPrivacyHubBaseline));
+ ::features::kHappinessTrackingPrivacyHubPostLaunch));
+ // TODO(b/290646585): Replace with a CHECK().
AllowJavascript();
- privacy_page_was_opened_ = true;
+ privacy_page_opened_timestamp_ = base::TimeTicks::Now();
}
void PrivacyHubHandler::HandlePrivacyPageClosed(const base::Value::List& args) {
DCHECK(args.empty());
DCHECK(base::FeatureList::IsEnabled(
- ::features::kHappinessTrackingPrivacyHubBaseline));
+ ::features::kHappinessTrackingPrivacyHubPostLaunch));
+ // TODO(b/290646585): Replace with a CHECK().
AllowJavascript();
TriggerHatsIfPageWasOpened();
}
+void PrivacyHubHandler::HandleInitialMicrophoneSwitchState(
+ const base::Value::List& args) {
+ const auto callback_id = ValidateArgs(args);
+ const auto value = base::Value(privacy_hub_util::MicrophoneSwitchState());
+ ResolveJavascriptCallback(callback_id, value);
+}
+
+void PrivacyHubHandler::HandleInitialCameraLedFallbackState(
+ const base::Value::List& args) {
+ const auto callback_id = ValidateArgs(args);
+ const auto value = base::Value(privacy_hub_util::UsingCameraLEDFallback());
+ ResolveJavascriptCallback(callback_id, value);
+}
+
+const base::ValueView PrivacyHubHandler::ValidateArgs(
+ const base::Value::List& args) {
+ CHECK(ash::features::IsCrosPrivacyHubEnabled());
+ // TODO(b/290646585): Replace with a CHECK().
+ AllowJavascript();
+
+ DCHECK_GE(1U, args.size()) << ": Did not expect arguments";
+ DCHECK_EQ(1U, args.size()) << ": Callback ID is required";
+
+ return args[0];
+}
+
void PrivacyHubHandler::TriggerHatsIfPageWasOpened() {
- if (privacy_page_was_opened_) {
+ if (const base::TimeTicks now = base::TimeTicks::Now();
+ (now - privacy_page_opened_timestamp_.value_or(now)) >=
+ base::Seconds(5)) {
+ privacy_page_opened_timestamp_.reset();
PrivacyHubHatsTrigger::Get().ShowSurveyAfterDelayElapsed();
}
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.h b/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.h
index b68ca8e025c..cb0fe8bf4e9 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler.h
@@ -8,8 +8,10 @@
#include <string>
#include "ash/public/cpp/privacy_hub_delegate.h"
+#include "base/time/time.h"
#include "base/values.h"
#include "content/public/browser/web_ui_message_handler.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace ash::settings {
@@ -26,22 +28,30 @@ class PrivacyHubHandler : public content::WebUIMessageHandler,
// PrivacyHubDelegate
void MicrophoneHardwareToggleChanged(bool muted) override;
+ void SetPrivacyPageOpenedTimeStampForTesting(base::TimeTicks time_stamp);
+
protected:
// content::WebUIMessageHandler
void RegisterMessages() override;
void NotifyJS(const std::string& event_name, const base::Value& value);
- void HandleInitialMicrophoneSwitchState(const base::Value::List& args);
-
void HandlePrivacyPageOpened(const base::Value::List& args);
void HandlePrivacyPageClosed(const base::Value::List& args);
+ void HandleInitialMicrophoneSwitchState(const base::Value::List& args);
+
+ void HandleInitialCameraLedFallbackState(const base::Value::List& args);
+
private:
+ // return the callback_id
+ const base::ValueView ValidateArgs(const base::Value::List& args);
+
void TriggerHatsIfPageWasOpened();
- bool privacy_page_was_opened_ = false;
+ absl::optional<base::TimeTicks> privacy_page_opened_timestamp_;
+ base::WeakPtrFactory<PrivacyHubHandler> weak_factory_{this};
};
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler_unittest.cc
index f8f5a6c2d2b..3c33483dff7 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/privacy_hub_handler_unittest.cc
@@ -10,6 +10,7 @@
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chrome/browser/ash/privacy_hub/privacy_hub_hats_trigger.h"
+#include "chrome/browser/ash/privacy_hub/privacy_hub_util.h"
#include "chrome/common/chrome_features.h"
#include "chromeos/ash/components/audio/cras_audio_handler.h"
#include "content/public/test/test_web_ui.h"
@@ -22,6 +23,7 @@ class TestPrivacyHubHandler : public PrivacyHubHandler {
public:
using content::WebUIMessageHandler::set_web_ui;
+ using PrivacyHubHandler::HandleInitialCameraLedFallbackState;
using PrivacyHubHandler::HandleInitialMicrophoneSwitchState;
using PrivacyHubHandler::HandlePrivacyPageClosed;
using PrivacyHubHandler::HandlePrivacyPageOpened;
@@ -124,11 +126,27 @@ class PrivacyHubHandlerMicrophoneTest
}
};
+class PrivacyHubHandlerCameraLedFallbackTest
+ : public PrivacyHubHandlerTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ PrivacyHubHandlerCameraLedFallbackTest()
+ : scoped_camera_led_fallback_(GetParam()) {}
+
+ void ExpectValueMatchesBoolParam(const base::Value& value) const {
+ PrivacyHubHandlerTest::ExpectValueMatchesBoolParam(GetParam(), value);
+ }
+
+ private:
+ privacy_hub_util::ScopedCameraLedFallbackForTesting
+ scoped_camera_led_fallback_;
+};
+
class PrivacyHubHandlerHatsTest : public PrivacyHubHandlerTest {
public:
PrivacyHubHandlerHatsTest() {
feature_list_.InitAndEnableFeature(
- ::features::kHappinessTrackingPrivacyHubBaseline);
+ ::features::kHappinessTrackingPrivacyHubPostLaunch);
}
bool IsTimerStarted() {
@@ -167,7 +185,24 @@ INSTANTIATE_TEST_SUITE_P(HardwareSwitchStates,
testing::Values(true, false),
testing::PrintToStringParamName());
-TEST_F(PrivacyHubHandlerHatsTest, OnlyTriggerHatsIfPageWasVisited) {
+TEST_P(PrivacyHubHandlerCameraLedFallbackTest,
+ HandleInitialCameraLedCallbackState) {
+ base::Value::List args;
+ args.Append(this_test_name_);
+
+ privacy_hub_handler_.HandleInitialCameraLedFallbackState(args);
+
+ const base::Value data = GetLastWebUIResponse(this_test_name_);
+
+ ExpectValueMatchesBoolParam(data);
+}
+
+INSTANTIATE_TEST_SUITE_P(CameraLedFallback,
+ PrivacyHubHandlerCameraLedFallbackTest,
+ testing::Values(true, false),
+ testing::PrintToStringParamName());
+
+TEST_F(PrivacyHubHandlerHatsTest, OnlyTriggerHatsIfPageWasVisitedLongEnough) {
const base::Value::List args;
EXPECT_FALSE(IsTimerStarted());
@@ -181,11 +216,35 @@ TEST_F(PrivacyHubHandlerHatsTest, OnlyTriggerHatsIfPageWasVisited) {
privacy_hub_handler_.HandlePrivacyPageOpened(args);
EXPECT_FALSE(IsTimerStarted());
+ // Simulate the user stays on the page for 5 seconds.
+ privacy_hub_handler_.SetPrivacyPageOpenedTimeStampForTesting(
+ base::TimeTicks::Now() - base::Seconds(5));
+ EXPECT_FALSE(IsTimerStarted());
+
// And leaves it again, now the survey should be triggered.
privacy_hub_handler_.HandlePrivacyPageClosed(args);
EXPECT_TRUE(IsTimerStarted());
}
+TEST_F(PrivacyHubHandlerHatsTest, DontTriggerHatsIfUserLeftEarly) {
+ const base::Value::List args;
+
+ EXPECT_FALSE(IsTimerStarted());
+
+ // We trigger the HaTS survey on the leave event but the user hasn't visited
+ // the page yet.
+ privacy_hub_handler_.HandlePrivacyPageClosed(args);
+ EXPECT_FALSE(IsTimerStarted());
+
+ // User goes to the page.
+ privacy_hub_handler_.HandlePrivacyPageOpened(args);
+ EXPECT_FALSE(IsTimerStarted());
+
+ // And leaves it again immediately, now the survey shouldn't be triggered.
+ privacy_hub_handler_.HandlePrivacyPageClosed(args);
+ EXPECT_FALSE(IsTimerStarted());
+}
+
#if DCHECK_IS_ON()
using PrivacyHubHandlerDeathTest = PrivacyHubHandlerTest;
@@ -224,7 +283,7 @@ TEST_F(PrivacyHubHandlerDeathTest, OnlyTriggerHatsIfFeatureIsEnabled) {
const base::Value::List args;
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
- ::features::kHappinessTrackingPrivacyHubBaseline);
+ ::features::kHappinessTrackingPrivacyHubPostLaunch);
// User goes to the page.
EXPECT_DEATH(privacy_hub_handler_.HandlePrivacyPageOpened(args),
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/privacy_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/privacy_section.cc
index 57aea46c694..b0d0be269c4 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/privacy_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/privacy_section.cc
@@ -13,14 +13,15 @@
#include "build/branding_buildflags.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
+#include "chrome/browser/ash/privacy_hub/privacy_hub_util.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/ash/auth/legacy_fingerprint_engine.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/metrics_consent_handler.h"
#include "chrome/browser/ui/webui/settings/ash/os_settings_features_util.h"
#include "chrome/browser/ui/webui/settings/ash/peripheral_data_access_handler.h"
#include "chrome/browser/ui/webui/settings/ash/privacy_hub_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/settings_secure_dns_handler.h"
#include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h"
#include "chrome/browser/ui/webui/webui_util.h"
@@ -41,6 +42,7 @@ namespace mojom {
using ::chromeos::settings::mojom::kFingerprintSubpagePathV2;
using ::chromeos::settings::mojom::kManageOtherPeopleSubpagePathV2;
using ::chromeos::settings::mojom::kPrivacyAndSecuritySectionPath;
+using ::chromeos::settings::mojom::kPrivacyHubMicrophoneSubpagePath;
using ::chromeos::settings::mojom::kPrivacyHubSubpagePath;
using ::chromeos::settings::mojom::kSecurityAndSignInSubpagePathV2;
using ::chromeos::settings::mojom::kSmartPrivacySubpagePath;
@@ -133,7 +135,13 @@ const std::vector<SearchConcept>& GetPrivacySearchConcepts() {
mojom::SearchResultIcon::kLock,
mojom::SearchResultDefaultRank::kMedium,
mojom::SearchResultType::kSubpage,
- {.subpage = mojom::Subpage::kSecurityAndSignInV2}}});
+ {.subpage = mojom::Subpage::kSecurityAndSignInV2}},
+ {IDS_OS_SETTINGS_TAG_LOCAL_DATA_RECOVERY,
+ mojom::kSecurityAndSignInSubpagePathV2,
+ mojom::SearchResultIcon::kLock,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::SearchResultType::kSetting,
+ {.setting = mojom::Setting::kDataRecovery}}});
}
return all_tags;
@@ -394,6 +402,8 @@ void PrivacySection::AddLoadTimeData(content::WebUIDataSource* html_source) {
{"cameraToggleTitle", IDS_OS_SETTINGS_PRIVACY_HUB_CAMERA_TOGGLE_TITLE},
{"cameraToggleSubtext",
IDS_OS_SETTINGS_PRIVACY_HUB_CAMERA_TOGGLE_SUBTEXT},
+ {"cameraToggleFallbackSubtext",
+ IDS_OS_SETTINGS_PRIVACY_HUB_FALLBACK_CAMERA_TOGGLE_SUBTEXT},
{"noCameraConnectedText",
IDS_OS_SETTINGS_PRIVACY_HUB_NO_CAMERA_CONNECTED_TEXT},
{"microphoneToggleTitle",
@@ -423,7 +433,10 @@ void PrivacySection::AddLoadTimeData(content::WebUIDataSource* html_source) {
html_source->AddBoolean(
"isPrivacyHubHatsEnabled",
base::FeatureList::IsEnabled(
- ::features::kHappinessTrackingPrivacyHubBaseline));
+ ::features::kHappinessTrackingPrivacyHubPostLaunch));
+ html_source->AddBoolean(
+ "showAppPermissionsInsidePrivacyHub",
+ ash::features::IsCrosPrivacyHubAppPermissionsEnabled());
html_source->AddBoolean("showPrivacyHubPage",
ash::features::IsCrosPrivacyHubEnabled());
html_source->AddBoolean("showPrivacyHubMVPPage",
@@ -488,7 +501,7 @@ mojom::SearchResultIcon PrivacySection::GetSectionIcon() const {
return mojom::SearchResultIcon::kShield;
}
-std::string PrivacySection::GetSectionPath() const {
+const char* PrivacySection::GetSectionPath() const {
return mojom::kPrivacyAndSecuritySectionPath;
}
@@ -525,6 +538,7 @@ void PrivacySection::RegisterHierarchy(HierarchyGenerator* generator) const {
mojom::Setting::kChangeAuthPinV2,
mojom::Setting::kPeripheralDataAccessProtection,
mojom::Setting::kLockScreenNotification,
+ mojom::Setting::kDataRecovery,
};
RegisterNestedSettingBulk(mojom::Subpage::kSecurityAndSignInV2,
kSecurityAndSignInSettings, generator);
@@ -580,6 +594,13 @@ void PrivacySection::RegisterHierarchy(HierarchyGenerator* generator) const {
mojom::Setting::kGeolocationOnOff,
mojom::Setting::kSpeakOnMuteDetectionOnOff}},
generator);
+
+ // Privacy hub microphone.
+ generator->RegisterTopLevelSubpage(
+ IDS_OS_SETTINGS_PRIVACY_HUB_MICROPHONE_TOGGLE_TITLE,
+ mojom::Subpage::kPrivacyHubMicrophone, mojom::SearchResultIcon::kShield,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::kPrivacyHubMicrophoneSubpagePath);
}
bool PrivacySection::AreFingerprintSettingsAllowed() {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/privacy_section.h b/chromium/chrome/browser/ui/webui/settings/ash/privacy_section.h
index b9147dc21ee..04846842d03 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/privacy_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/privacy_section.h
@@ -31,18 +31,18 @@ class PrivacySection : public OsSettingsSection {
PrefService* pref_service);
~PrivacySection() override;
- private:
// OsSettingsSection:
void AddHandlers(content::WebUI* web_ui) override;
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
bool AreFingerprintSettingsAllowed();
void UpdateRemoveFingerprintSearchTags();
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/reset_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/reset_section.cc
index b4a77124b99..fdde95e91e0 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/reset_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/reset_section.cc
@@ -4,10 +4,11 @@
#include "chrome/browser/ui/webui/settings/ash/reset_section.h"
+#include "ash/constants/ash_features.h"
#include "base/no_destructor.h"
#include "chrome/browser/browser_process.h"
-#include "chrome/browser/policy/management_utils.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/settings/ash/os_settings_features_util.h"
#include "chrome/browser/ui/webui/settings/reset_settings_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/grit/chromium_strings.h"
@@ -22,53 +23,32 @@ namespace ash::settings {
namespace mojom {
using ::chromeos::settings::mojom::kResetSectionPath;
+using ::chromeos::settings::mojom::kSystemPreferencesSectionPath;
using ::chromeos::settings::mojom::Section;
using ::chromeos::settings::mojom::Setting;
using ::chromeos::settings::mojom::Subpage;
} // namespace mojom
-namespace {
-
-const std::vector<SearchConcept>& GetResetSearchConcepts() {
- static const base::NoDestructor<std::vector<SearchConcept>> tags({
- {IDS_OS_SETTINGS_TAG_RESET,
- mojom::kResetSectionPath,
- mojom::SearchResultIcon::kReset,
- mojom::SearchResultDefaultRank::kMedium,
- mojom::SearchResultType::kSection,
- {.section = mojom::Section::kReset}},
- {IDS_OS_SETTINGS_TAG_RESET_POWERWASH,
- mojom::kResetSectionPath,
- mojom::SearchResultIcon::kReset,
- mojom::SearchResultDefaultRank::kMedium,
- mojom::SearchResultType::kSetting,
- {.setting = mojom::Setting::kPowerwash},
- {IDS_OS_SETTINGS_TAG_RESET_POWERWASH_ALT1, SearchConcept::kAltTagEnd}},
- });
- return *tags;
-}
-
-bool IsPowerwashAllowed() {
- return !policy::IsDeviceEnterpriseManaged() &&
- !user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
- !user_manager::UserManager::Get()->IsLoggedInAsChildUser();
-}
-
-} // namespace
-
ResetSection::ResetSection(Profile* profile,
SearchTagRegistry* search_tag_registry)
- : OsSettingsSection(profile, search_tag_registry) {
+ : OsSettingsSection(profile, search_tag_registry),
+ isRevampWayfindingEnabled_(
+ ash::features::IsOsSettingsRevampWayfindingEnabled()) {
SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
- if (IsPowerwashAllowed())
- updater.AddSearchTags(GetResetSearchConcepts());
+ if (IsPowerwashAllowed()) {
+ updater.AddSearchTags(GetSearchConcepts());
+ }
}
ResetSection::~ResetSection() = default;
void ResetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
- {"resetPageTitle", IDS_SETTINGS_RESET_TITLE},
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
+ {"resetPageTitle", kIsRevampEnabled ? IDS_OS_SETTINGS_REVAMP_RESET_TITLE
+ : IDS_SETTINGS_RESET_TITLE},
{"powerwashTitle", IDS_SETTINGS_FACTORY_RESET},
{"powerwashDialogTitle", IDS_SETTINGS_FACTORY_RESET_HEADING},
{"powerwashDialogButton", IDS_SETTINGS_RESTART},
@@ -99,8 +79,11 @@ void ResetSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
html_source->AddString(
"powerwashDescription",
- l10n_util::GetStringFUTF16(IDS_SETTINGS_FACTORY_RESET_DESCRIPTION,
- l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
+ kIsRevampEnabled ? l10n_util::GetStringUTF16(
+ IDS_OS_SETTINGS_REVAMP_FACTORY_RESET_DESCRIPTION)
+ : l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_FACTORY_RESET_DESCRIPTION,
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
}
void ResetSection::AddHandlers(content::WebUI* web_ui) {
@@ -113,15 +96,17 @@ int ResetSection::GetSectionNameMessageId() const {
}
mojom::Section ResetSection::GetSection() const {
- return mojom::Section::kReset;
+ return isRevampWayfindingEnabled_ ? mojom::Section::kSystemPreferences
+ : mojom::Section::kReset;
}
mojom::SearchResultIcon ResetSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kReset;
}
-std::string ResetSection::GetSectionPath() const {
- return mojom::kResetSectionPath;
+const char* ResetSection::GetSectionPath() const {
+ return isRevampWayfindingEnabled_ ? mojom::kSystemPreferencesSectionPath
+ : mojom::kResetSectionPath;
}
bool ResetSection::LogMetric(mojom::Setting setting, base::Value& value) const {
@@ -133,4 +118,27 @@ void ResetSection::RegisterHierarchy(HierarchyGenerator* generator) const {
generator->RegisterTopLevelSetting(mojom::Setting::kPowerwash);
}
+const std::vector<SearchConcept>& ResetSection::GetSearchConcepts() {
+ const mojom::Section section = GetSection();
+ const char* section_path = GetSectionPath();
+
+ static const base::NoDestructor<std::vector<SearchConcept>> tags({
+ {IDS_OS_SETTINGS_TAG_RESET,
+ section_path,
+ mojom::SearchResultIcon::kReset,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::SearchResultType::kSection,
+ {.section = section}},
+ {IDS_OS_SETTINGS_TAG_RESET_POWERWASH,
+ section_path,
+ mojom::SearchResultIcon::kReset,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::SearchResultType::kSetting,
+ {.setting = mojom::Setting::kPowerwash},
+ {IDS_OS_SETTINGS_TAG_RESET_POWERWASH_ALT1, SearchConcept::kAltTagEnd}},
+ });
+
+ return *tags;
+}
+
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/reset_section.h b/chromium/chrome/browser/ui/webui/settings/ash/reset_section.h
index 6fbc04194cd..5041af79031 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/reset_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/reset_section.h
@@ -14,6 +14,7 @@ class WebUIDataSource;
namespace ash::settings {
+struct SearchConcept;
class SearchTagRegistry;
// Provides UI strings and search tags for Reset settings. Note that search tags
@@ -24,17 +25,20 @@ class ResetSection : public OsSettingsSection {
ResetSection(Profile* profile, SearchTagRegistry* search_tag_registry);
~ResetSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+
+ private:
+ const bool isRevampWayfindingEnabled_;
+ const std::vector<SearchConcept>& GetSearchConcepts();
};
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search_engines_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/search_engines_handler_unittest.cc
index 17610dab3c7..ab390760303 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search_engines_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/search_engines_handler_unittest.cc
@@ -68,11 +68,13 @@ class SearchEnginesHandlerTest : public testing::Test {
protected:
std::unique_ptr<content::TestWebUI> web_ui_;
- raw_ptr<MockNewWindowDelegate, ExperimentalAsh> new_window_delegate_primary_;
+ raw_ptr<MockNewWindowDelegate, DanglingUntriaged | ExperimentalAsh>
+ new_window_delegate_primary_;
private:
content::BrowserTaskEnvironment task_environment_;
- raw_ptr<::settings::SearchEnginesHandler, ExperimentalAsh> handler_;
+ raw_ptr<::settings::SearchEnginesHandler, DanglingUntriaged | ExperimentalAsh>
+ handler_;
std::unique_ptr<TestingProfileManager> profile_manager_;
std::unique_ptr<TestNewWindowDelegateProvider> new_window_provider_;
};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/search_section.cc
index 497c428403d..674f5870907 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search_section.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/search_section.cc
@@ -15,8 +15,8 @@
#include "chrome/browser/ash/assistant/assistant_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_utils.h"
+#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/ash/google_assistant_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
#include "chrome/browser/ui/webui/settings/search_engines_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
@@ -38,6 +38,7 @@ namespace mojom {
using ::chromeos::settings::mojom::kAssistantSubpagePath;
using ::chromeos::settings::mojom::kSearchAndAssistantSectionPath;
using ::chromeos::settings::mojom::kSearchSubpagePath;
+using ::chromeos::settings::mojom::kSystemPreferencesSectionPath;
using ::chromeos::settings::mojom::Section;
using ::chromeos::settings::mojom::Setting;
using ::chromeos::settings::mojom::Subpage;
@@ -49,7 +50,8 @@ bool ShouldShowQuickAnswersSettings() {
return QuickAnswersState::Get() && QuickAnswersState::Get()->is_eligible();
}
-const std::vector<SearchConcept>& GetSearchPageSearchConcepts() {
+const std::vector<SearchConcept>& GetSearchPageSearchConcepts(
+ const char* section_path) {
if (ShouldShowQuickAnswersSettings()) {
static const base::NoDestructor<std::vector<SearchConcept>> tags({
{IDS_OS_SETTINGS_TAG_PREFERRED_SEARCH_ENGINE,
@@ -60,17 +62,17 @@ const std::vector<SearchConcept>& GetSearchPageSearchConcepts() {
{.setting = mojom::Setting::kPreferredSearchEngine}},
});
return *tags;
- } else {
- static const base::NoDestructor<std::vector<SearchConcept>> tags({
- {IDS_OS_SETTINGS_TAG_PREFERRED_SEARCH_ENGINE,
- mojom::kSearchAndAssistantSectionPath,
- mojom::SearchResultIcon::kMagnifyingGlass,
- mojom::SearchResultDefaultRank::kMedium,
- mojom::SearchResultType::kSetting,
- {.setting = mojom::Setting::kPreferredSearchEngine}},
- });
- return *tags;
}
+
+ static const base::NoDestructor<std::vector<SearchConcept>> tags({
+ {IDS_OS_SETTINGS_TAG_PREFERRED_SEARCH_ENGINE,
+ section_path,
+ mojom::SearchResultIcon::kMagnifyingGlass,
+ mojom::SearchResultDefaultRank::kMedium,
+ mojom::SearchResultType::kSetting,
+ {.setting = mojom::Setting::kPreferredSearchEngine}},
+ });
+ return *tags;
}
const std::vector<SearchConcept>& GetQuickAnswersSearchConcepts() {
@@ -268,7 +270,8 @@ SearchSection::SearchSection(Profile* profile,
SearchTagRegistry* search_tag_registry)
: OsSettingsSection(profile, search_tag_registry) {
SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
- updater.AddSearchTags(GetSearchPageSearchConcepts());
+
+ updater.AddSearchTags(GetSearchPageSearchConcepts(GetSectionPath()));
AssistantState* assistant_state = AssistantState::Get();
if (IsAssistantAllowed() && assistant_state) {
@@ -291,16 +294,23 @@ SearchSection::~SearchSection() {
}
void SearchSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
- static constexpr webui::LocalizedString kLocalizedStrings[] = {
- {"osSearchEngineLabel", IDS_OS_SETTINGS_SEARCH_ENGINE_LABEL},
+ const bool kIsRevampEnabled =
+ ash::features::IsOsSettingsRevampWayfindingEnabled();
+
+ webui::LocalizedString kLocalizedStrings[] = {
+ {"osSearchEngineLabel", kIsRevampEnabled
+ ? IDS_OS_SETTINGS_REVAMP_SEARCH_ENGINE_LABEL
+ : IDS_OS_SETTINGS_SEARCH_ENGINE_LABEL},
{"searchSubpageTitle", IDS_SETTINGS_SEARCH_SUBPAGE_TITLE},
{"searchGoogleAssistant", IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT},
{"searchGoogleAssistantEnabled",
- IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ENABLED},
+ kIsRevampEnabled ? IDS_OS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ON
+ : IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ENABLED},
{"searchGoogleAssistantDisabled",
- IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_DISABLED},
- {"searchGoogleAssistantOn", IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ON},
- {"searchGoogleAssistantOff", IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_OFF},
+ kIsRevampEnabled ? IDS_OS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_OFF
+ : IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_DISABLED},
+ {"searchGoogleAssistantOn", IDS_OS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ON},
+ {"searchGoogleAssistantOff", IDS_OS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_OFF},
};
html_source->AddLocalizedStrings(kLocalizedStrings);
@@ -332,15 +342,19 @@ int SearchSection::GetSectionNameMessageId() const {
}
mojom::Section SearchSection::GetSection() const {
- return mojom::Section::kSearchAndAssistant;
+ return ash::features::IsOsSettingsRevampWayfindingEnabled()
+ ? mojom::Section::kSystemPreferences
+ : mojom::Section::kSearchAndAssistant;
}
mojom::SearchResultIcon SearchSection::GetSectionIcon() const {
return mojom::SearchResultIcon::kMagnifyingGlass;
}
-std::string SearchSection::GetSectionPath() const {
- return mojom::kSearchAndAssistantSectionPath;
+const char* SearchSection::GetSectionPath() const {
+ return ash::features::IsOsSettingsRevampWayfindingEnabled()
+ ? mojom::kSystemPreferencesSectionPath
+ : mojom::kSearchAndAssistantSectionPath;
}
bool SearchSection::LogMetric(mojom::Setting setting,
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/search_section.h b/chromium/chrome/browser/ui/webui/settings/ash/search_section.h
index 660202001b6..a33bfc43ce8 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/search_section.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/search_section.h
@@ -28,18 +28,18 @@ class SearchSection : public OsSettingsSection,
SearchSection(Profile* profile, SearchTagRegistry* search_tag_registry);
~SearchSection() override;
- private:
// OsSettingsSection:
void AddLoadTimeData(content::WebUIDataSource* html_source) override;
void AddHandlers(content::WebUI* web_ui) override;
int GetSectionNameMessageId() const override;
chromeos::settings::mojom::Section GetSection() const override;
mojom::SearchResultIcon GetSectionIcon() const override;
- std::string GetSectionPath() const override;
+ const char* GetSectionPath() const override;
bool LogMetric(chromeos::settings::mojom::Setting setting,
base::Value& value) const override;
void RegisterHierarchy(HierarchyGenerator* generator) const override;
+ private:
// AssistantStateObserver:
void OnAssistantConsentStatusChanged(int consent_status) override;
void OnAssistantContextEnabled(bool enabled) override;
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/select_to_speak_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/select_to_speak_handler.cc
index f3eddb365d1..73861e387d6 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/select_to_speak_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/select_to_speak_handler.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/ui/webui/settings/ash/select_to_speak_handler.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/values.h"
@@ -13,7 +14,6 @@
#include "chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.h"
#include "chrome/browser/speech/extension_api/tts_extension_api.h"
#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/tts_controller.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.cc b/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.cc
index e8cc3546686..169865e98d2 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.cc
@@ -16,8 +16,10 @@ namespace ash::settings {
SettingsUserActionTracker::SettingsUserActionTracker(
Hierarchy* hierarchy,
OsSettingsSections* sections,
- PrefService* pref_service)
- : hierarchy_(hierarchy), sections_(sections), pref_service_(pref_service) {}
+ PrefService* profile_pref_service)
+ : hierarchy_(hierarchy),
+ sections_(sections),
+ profile_pref_service_(profile_pref_service) {}
SettingsUserActionTracker::~SettingsUserActionTracker() = default;
@@ -31,8 +33,8 @@ void SettingsUserActionTracker::BindInterface(
base::Unretained(this)));
// New session started, so create a new per session tracker.
- per_session_tracker_ =
- std::make_unique<PerSessionSettingsUserActionTracker>(pref_service_);
+ per_session_tracker_ = std::make_unique<PerSessionSettingsUserActionTracker>(
+ profile_pref_service_);
}
void SettingsUserActionTracker::EndCurrentSession() {
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.h b/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.h
index 127689400e5..3998b56f197 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.h
@@ -9,8 +9,8 @@
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
-#include "chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h"
-#include "chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/user_action_recorder.mojom.h"
+#include "chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.h"
#include "components/prefs/pref_service.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
@@ -27,7 +27,7 @@ class SettingsUserActionTracker : public mojom::UserActionRecorder {
public:
SettingsUserActionTracker(Hierarchy* hierarchy,
OsSettingsSections* sections,
- PrefService* pref_service);
+ PrefService* profile_pref_service);
SettingsUserActionTracker(const SettingsUserActionTracker& other) = delete;
SettingsUserActionTracker& operator=(const SettingsUserActionTracker& other) =
delete;
@@ -69,7 +69,7 @@ class SettingsUserActionTracker : public mojom::UserActionRecorder {
raw_ptr<Hierarchy, ExperimentalAsh> hierarchy_;
raw_ptr<OsSettingsSections, ExperimentalAsh> sections_;
- raw_ptr<PrefService> pref_service_;
+ raw_ptr<PrefService> profile_pref_service_;
std::unique_ptr<PerSessionSettingsUserActionTracker> per_session_tracker_;
mojo::Receiver<mojom::UserActionRecorder> receiver_{this};
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker_unittest.cc b/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker_unittest.cc
index 574a9cb166f..7e18fe66369 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/settings_user_action_tracker_unittest.cc
@@ -4,20 +4,43 @@
#include "chrome/browser/ui/webui/settings/ash/settings_user_action_tracker.h"
+#include "ash/constants/ash_features.h"
+#include "ash/webui/settings/public/constants/setting.mojom.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/values.h"
+#include "chrome/browser/metrics/chrome_metrics_service_client.h"
+#include "chrome/browser/ui/webui/ash/settings/search/mojom/user_action_recorder.mojom.h"
#include "chrome/browser/ui/webui/settings/ash/fake_hierarchy.h"
#include "chrome/browser/ui/webui/settings/ash/fake_os_settings_section.h"
#include "chrome/browser/ui/webui/settings/ash/fake_os_settings_sections.h"
-#include "chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h"
-#include "chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
+#include "chrome/browser/ui/webui/settings/ash/per_session_settings_user_action_tracker.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/test/test_enabled_state_provider.h"
+#include "components/metrics/test/test_metrics_service_client.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace {
+class TestUserMetricsServiceClient
+ : public ::metrics::TestMetricsServiceClient {
+ public:
+ absl::optional<bool> GetCurrentUserMetricsConsent() const override {
+ return current_user_metrics_consent_;
+ }
+
+ void UpdateCurrentUserMetricsConsent(bool metrics_consent) override {
+ current_user_metrics_consent_ = metrics_consent;
+ }
+
+ private:
+ bool current_user_metrics_consent_ = true;
+};
+} // namespace
+
namespace ash::settings {
constexpr char kProfileName[] = "user@gmail.com";
@@ -29,15 +52,10 @@ using ::chromeos::settings::mojom::Setting;
class SettingsUserActionTrackerTest : public testing::Test {
protected:
- SettingsUserActionTrackerTest()
- : fake_hierarchy_(&fake_sections_),
- tracker_(&fake_hierarchy_, &fake_sections_, GetTestProfilePref()) {
- // Initialize per_session_tracker_ manually since BindInterface is never
- // called on tracker_.
- tracker_.per_session_tracker_ =
- std::make_unique<PerSessionSettingsUserActionTracker>(
- testing_profile_->GetPrefs());
+ SettingsUserActionTrackerTest() {
+ feature_list_.InitAndEnableFeature(::ash::features::kPerUserMetrics);
}
+
~SettingsUserActionTrackerTest() override = default;
void SetUpTestingProfile() {
@@ -47,45 +65,91 @@ class SettingsUserActionTrackerTest : public testing::Test {
testing_profile_ = profile_manager_->CreateTestingProfile(kProfileName);
}
- PrefService* GetTestProfilePref() {
- SetUpTestingProfile();
- test_pref_service_ = testing_profile_->GetPrefs();
- return test_pref_service_;
+ // metrics setup
+ void SetupTestMetricsClient() {
+ local_state_ = std::make_unique<TestingPrefServiceSimple>();
+ metrics::MetricsService::RegisterPrefs(local_state_->registry());
+
+ test_enabled_state_provider_ =
+ std::make_unique<metrics::TestEnabledStateProvider>(true, true);
+ test_metrics_state_manager_ = metrics::MetricsStateManager::Create(
+ local_state_.get(), test_enabled_state_provider_.get(), std::wstring(),
+ base::FilePath());
+ test_metrics_service_client_ =
+ std::make_unique<TestUserMetricsServiceClient>();
+ test_metrics_service_ = std::make_unique<metrics::MetricsService>(
+ test_metrics_state_manager_.get(), test_metrics_service_client_.get(),
+ local_state_.get());
+ TestingBrowserProcess::GetGlobal()->SetMetricsService(
+ test_metrics_service_.get());
+
+ // Needs to be set for metrics service.
+ base::SetRecordActionTaskRunner(
+ task_environment_.GetMainThreadTaskRunner());
}
// testing::Test:
void SetUp() override {
- fake_hierarchy_.AddSettingMetadata(mojom::Section::kBluetooth,
- mojom::Setting::kBluetoothOnOff);
- fake_hierarchy_.AddSettingMetadata(mojom::Section::kDevice,
- mojom::Setting::kKeyboardFunctionKeys);
- fake_hierarchy_.AddSettingMetadata(mojom::Section::kDevice,
- mojom::Setting::kTouchpadSpeed);
- fake_hierarchy_.AddSettingMetadata(mojom::Section::kPeople,
- mojom::Setting::kAddAccount);
- fake_hierarchy_.AddSettingMetadata(mojom::Section::kPrinting,
- mojom::Setting::kScanningApp);
- fake_hierarchy_.AddSettingMetadata(mojom::Section::kNetwork,
- mojom::Setting::kWifiAddNetwork);
+ fake_sections_ = std::make_unique<FakeOsSettingsSections>();
+ fake_hierarchy_ = std::make_unique<FakeHierarchy>(fake_sections_.get());
+ fake_hierarchy_->AddSettingMetadata(mojom::Section::kBluetooth,
+ mojom::Setting::kBluetoothOnOff);
+ fake_hierarchy_->AddSettingMetadata(mojom::Section::kDevice,
+ mojom::Setting::kKeyboardFunctionKeys);
+ fake_hierarchy_->AddSettingMetadata(mojom::Section::kDevice,
+ mojom::Setting::kTouchpadSpeed);
+ fake_hierarchy_->AddSettingMetadata(mojom::Section::kPeople,
+ mojom::Setting::kAddAccount);
+ fake_hierarchy_->AddSettingMetadata(mojom::Section::kPrinting,
+ mojom::Setting::kScanningApp);
+ fake_hierarchy_->AddSettingMetadata(mojom::Section::kNetwork,
+ mojom::Setting::kWifiAddNetwork);
+
+ SetUpTestingProfile();
+ test_pref_service_ = testing_profile_->GetPrefs();
+ SetupTestMetricsClient();
+
+ tracker_ = std::make_unique<SettingsUserActionTracker>(
+ fake_hierarchy_.get(), fake_sections_.get(), test_pref_service_);
+ // Initialize per_session_tracker_ manually since BindInterface is never
+ // called on tracker_.
+ tracker_->per_session_tracker_ =
+ std::make_unique<PerSessionSettingsUserActionTracker>(
+ testing_profile_->GetPrefs());
}
- void TearDown() override { tracker_.per_session_tracker_.reset(); }
+ void TearDown() override {
+ TestingBrowserProcess::GetGlobal()->SetMetricsService(nullptr);
+ }
// TestingProfile is bound to the IO thread:
// CurrentlyOn(content::BrowserThread::UI).
content::BrowserTaskEnvironment task_environment_;
base::HistogramTester histogram_tester_;
- FakeOsSettingsSections fake_sections_;
- raw_ptr<PrefService> test_pref_service_;
- FakeHierarchy fake_hierarchy_;
+ std::unique_ptr<FakeOsSettingsSections> fake_sections_;
+ std::unique_ptr<FakeHierarchy> fake_hierarchy_;
std::unique_ptr<TestingProfileManager> profile_manager_;
- SettingsUserActionTracker tracker_;
- TestingProfile* testing_profile_;
+ raw_ptr<TestingProfile> testing_profile_;
+ raw_ptr<PrefService> test_pref_service_;
+ std::unique_ptr<SettingsUserActionTracker> tracker_;
+
+ // This needs to be initialized before any tasks running on other threads
+ // access the feature list, and destroyed after |task_environment_|, to avoid
+ // data races.
+ base::test::ScopedFeatureList feature_list_;
+
+ // MetricsService.
+ std::unique_ptr<TestingPrefServiceSimple> local_state_;
+ std::unique_ptr<metrics::TestEnabledStateProvider>
+ test_enabled_state_provider_;
+ std::unique_ptr<metrics::MetricsStateManager> test_metrics_state_manager_;
+ std::unique_ptr<TestUserMetricsServiceClient> test_metrics_service_client_;
+ std::unique_ptr<metrics::MetricsService> test_metrics_service_;
};
TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedBool) {
// Record that the bluetooth enabled setting was toggled off.
- tracker_.RecordSettingChangeWithDetails(
+ tracker_->RecordSettingChangeWithDetails(
mojom::Setting::kBluetoothOnOff,
mojom::SettingChangeValue::NewBoolValue(false));
@@ -100,14 +164,14 @@ TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedBool) {
// The LogMetric fn in the Blutooth section should have been called.
const FakeOsSettingsSection* bluetooth_section =
static_cast<const FakeOsSettingsSection*>(
- fake_sections_.GetSection(mojom::Section::kBluetooth));
+ fake_sections_->GetSection(mojom::Section::kBluetooth));
EXPECT_TRUE(bluetooth_section->logged_metrics().back() ==
mojom::Setting::kBluetoothOnOff);
}
TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedString) {
// Record that the user tried to add a 3rd account.
- tracker_.RecordSettingChangeWithDetails(
+ tracker_->RecordSettingChangeWithDetails(
mojom::Setting::kWifiAddNetwork,
mojom::SettingChangeValue::NewStringValue("guid"));
@@ -122,14 +186,14 @@ TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedString) {
// The LogMetric fn in the People section should have been called.
const FakeOsSettingsSection* network_section =
static_cast<const FakeOsSettingsSection*>(
- fake_sections_.GetSection(mojom::Section::kNetwork));
+ fake_sections_->GetSection(mojom::Section::kNetwork));
EXPECT_TRUE(network_section->logged_metrics().back() ==
mojom::Setting::kWifiAddNetwork);
}
TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedInt) {
// Record that the user tried to add a 3rd account.
- tracker_.RecordSettingChangeWithDetails(
+ tracker_->RecordSettingChangeWithDetails(
mojom::Setting::kAddAccount, mojom::SettingChangeValue::NewIntValue(3));
// The umbrella metric for which setting was changed should be updated. Note
@@ -143,7 +207,7 @@ TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedInt) {
// The LogMetric fn in the People section should have been called.
const FakeOsSettingsSection* people_section =
static_cast<const FakeOsSettingsSection*>(
- fake_sections_.GetSection(mojom::Section::kPeople));
+ fake_sections_->GetSection(mojom::Section::kPeople));
EXPECT_TRUE(people_section->logged_metrics().back() ==
mojom::Setting::kAddAccount);
}
@@ -152,7 +216,7 @@ TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedBoolPref) {
// Record that the keyboard function keys setting was toggled off. This
// setting is controlled by a pref and uses the pref-to-setting-metric
// converter flow.
- tracker_.RecordSettingChangeWithDetails(
+ tracker_->RecordSettingChangeWithDetails(
mojom::Setting::kKeyboardFunctionKeys,
mojom::SettingChangeValue::NewBoolValue(false));
@@ -167,7 +231,7 @@ TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedBoolPref) {
// The LogMetric fn in the Device section should have been called.
const FakeOsSettingsSection* device_section =
static_cast<const FakeOsSettingsSection*>(
- fake_sections_.GetSection(mojom::Section::kDevice));
+ fake_sections_->GetSection(mojom::Section::kDevice));
EXPECT_TRUE(device_section->logged_metrics().back() ==
mojom::Setting::kKeyboardFunctionKeys);
}
@@ -176,7 +240,7 @@ TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedIntPref) {
// Record that the touchpad speed slider was changed by the user. This
// setting is controlled by a pref and uses the pref-to-setting-metric
// converter flow.
- tracker_.RecordSettingChangeWithDetails(
+ tracker_->RecordSettingChangeWithDetails(
mojom::Setting::kTouchpadSpeed,
mojom::SettingChangeValue::NewIntValue(4));
@@ -191,15 +255,15 @@ TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedIntPref) {
// The LogMetric fn in the Device section should have been called.
const FakeOsSettingsSection* device_section =
static_cast<const FakeOsSettingsSection*>(
- fake_sections_.GetSection(mojom::Section::kDevice));
+ fake_sections_->GetSection(mojom::Section::kDevice));
EXPECT_TRUE(device_section->logged_metrics().back() ==
mojom::Setting::kTouchpadSpeed);
}
TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedNullValue) {
// Record that the Scan app is opened.
- tracker_.RecordSettingChangeWithDetails(mojom::Setting::kScanningApp,
- nullptr);
+ tracker_->RecordSettingChangeWithDetails(mojom::Setting::kScanningApp,
+ nullptr);
// The umbrella metric for which setting was changed should be updated. Note
// that kScanningApp has enum value of 1403.
@@ -212,7 +276,7 @@ TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedNullValue) {
// The LogMetric fn in the Printing section should have been called.
const FakeOsSettingsSection* printing_section =
static_cast<const FakeOsSettingsSection*>(
- fake_sections_.GetSection(mojom::Section::kPrinting));
+ fake_sections_->GetSection(mojom::Section::kPrinting));
EXPECT_TRUE(printing_section->logged_metrics().back() ==
mojom::Setting::kScanningApp);
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.cc
index 55bc5f232da..e69d3f8eec0 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.cc
@@ -105,6 +105,14 @@ void SwitchAccessHandler::RegisterMessages() {
}
void SwitchAccessHandler::OnJavascriptAllowed() {
+ if (action_assignment_pane_active_) {
+ gfx::NativeView native_view = web_ui()->GetWebContents()->GetNativeView();
+
+ // Ensure we don't add the handler twice.
+ native_view->RemovePreTargetHandler(this);
+ native_view->AddPreTargetHandler(this);
+ }
+
pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
pref_change_registrar_->Init(prefs_);
pref_change_registrar_->Add(
@@ -125,10 +133,12 @@ void SwitchAccessHandler::OnJavascriptAllowed() {
}
void SwitchAccessHandler::OnJavascriptDisallowed() {
+ web_ui()->GetWebContents()->GetNativeView()->RemovePreTargetHandler(this);
pref_change_registrar_.reset();
}
void SwitchAccessHandler::OnKeyEvent(ui::KeyEvent* event) {
+ CHECK(action_assignment_pane_active_);
event->StopPropagation();
event->SetHandled();
@@ -161,14 +171,15 @@ void SwitchAccessHandler::HandleRefreshAssignmentsFromPrefs(
void SwitchAccessHandler::HandleNotifySwitchAccessActionAssignmentPaneActive(
const base::Value::List& args) {
+ action_assignment_pane_active_ = true;
AllowJavascript();
OnSwitchAccessAssignmentsUpdated();
- web_ui()->GetWebContents()->GetNativeView()->AddPreTargetHandler(this);
AccessibilityController::Get()->SuspendSwitchAccessKeyHandling(true);
}
void SwitchAccessHandler::HandleNotifySwitchAccessActionAssignmentPaneInactive(
const base::Value::List& args) {
+ action_assignment_pane_active_ = false;
web_ui()->GetWebContents()->GetNativeView()->RemovePreTargetHandler(this);
AccessibilityController::Get()->SuspendSwitchAccessKeyHandling(false);
}
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.h b/chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.h
index d4cd904dc7e..c3126f6d997 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/ash/switch_access_handler.h
@@ -41,6 +41,8 @@ class SwitchAccessHandler : public ::settings::SettingsPageUIHandler,
std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
raw_ptr<PrefService, ExperimentalAsh> prefs_;
+
+ bool action_assignment_pane_active_ = false;
};
} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/system_preferences_section.cc b/chromium/chrome/browser/ui/webui/settings/ash/system_preferences_section.cc
new file mode 100644
index 00000000000..56fe2fac2c6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/ash/system_preferences_section.cc
@@ -0,0 +1,90 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/settings/ash/system_preferences_section.h"
+
+#include "chrome/browser/ui/webui/settings/ash/date_time_section.h"
+#include "chrome/browser/ui/webui/settings/ash/languages_section.h"
+#include "chrome/browser/ui/webui/settings/ash/reset_section.h"
+#include "chrome/browser/ui/webui/settings/ash/search_section.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace ash::settings {
+
+namespace mojom {
+using ::chromeos::settings::mojom::kSystemPreferencesSectionPath;
+using ::chromeos::settings::mojom::Section;
+using ::chromeos::settings::mojom::Setting;
+using ::chromeos::settings::mojom::Subpage;
+} // namespace mojom
+
+SystemPreferencesSection::SystemPreferencesSection(
+ Profile* profile,
+ SearchTagRegistry* search_tag_registry,
+ PrefService* pref_service)
+ : OsSettingsSection(profile, search_tag_registry),
+ date_time_subsection_(profile, search_tag_registry),
+ languages_subsection_(profile, search_tag_registry, pref_service),
+ reset_subsection_(profile, search_tag_registry),
+ search_subsection_(profile, search_tag_registry) {}
+
+SystemPreferencesSection::~SystemPreferencesSection() = default;
+
+void SystemPreferencesSection::AddLoadTimeData(
+ content::WebUIDataSource* html_source) {
+ date_time_subsection_.AddLoadTimeData(html_source);
+ languages_subsection_.AddLoadTimeData(html_source);
+ reset_subsection_.AddLoadTimeData(html_source);
+ search_subsection_.AddLoadTimeData(html_source);
+
+ webui::LocalizedString kLocalizedStrings[] = {
+ {"storageAndPowerTitle",
+ IDS_OS_SETTINGS_SYSTEM_PREFERENCES_STORAGE_AND_POWER_TITLE},
+ {"systemPreferencesTitle", IDS_OS_SETTINGS_SYSTEM_PREFERENCES_TITLE},
+ };
+ html_source->AddLocalizedStrings(kLocalizedStrings);
+}
+
+void SystemPreferencesSection::AddHandlers(content::WebUI* web_ui) {
+ date_time_subsection_.AddHandlers(web_ui);
+ languages_subsection_.AddHandlers(web_ui);
+ reset_subsection_.AddHandlers(web_ui);
+ search_subsection_.AddHandlers(web_ui);
+}
+
+int SystemPreferencesSection::GetSectionNameMessageId() const {
+ return IDS_OS_SETTINGS_SYSTEM_PREFERENCES_TITLE;
+}
+
+mojom::Section SystemPreferencesSection::GetSection() const {
+ return mojom::Section::kSystemPreferences;
+}
+
+mojom::SearchResultIcon SystemPreferencesSection::GetSectionIcon() const {
+ return mojom::SearchResultIcon::kSystemPreferences;
+}
+
+const char* SystemPreferencesSection::GetSectionPath() const {
+ return mojom::kSystemPreferencesSectionPath;
+}
+
+bool SystemPreferencesSection::LogMetric(mojom::Setting setting,
+ base::Value& value) const {
+ return date_time_subsection_.LogMetric(setting, value) ||
+ languages_subsection_.LogMetric(setting, value) ||
+ reset_subsection_.LogMetric(setting, value) ||
+ search_subsection_.LogMetric(setting, value);
+}
+
+void SystemPreferencesSection::RegisterHierarchy(
+ HierarchyGenerator* generator) const {
+ date_time_subsection_.RegisterHierarchy(generator);
+ languages_subsection_.RegisterHierarchy(generator);
+ reset_subsection_.RegisterHierarchy(generator);
+ search_subsection_.RegisterHierarchy(generator);
+}
+
+} // namespace ash::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/system_preferences_section.h b/chromium/chrome/browser/ui/webui/settings/ash/system_preferences_section.h
new file mode 100644
index 00000000000..ef910cfc6c2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/ash/system_preferences_section.h
@@ -0,0 +1,52 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SYSTEM_PREFERENCES_SECTION_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SYSTEM_PREFERENCES_SECTION_H_
+
+#include "base/values.h"
+#include "chrome/browser/ui/webui/settings/ash/date_time_section.h"
+#include "chrome/browser/ui/webui/settings/ash/languages_section.h"
+#include "chrome/browser/ui/webui/settings/ash/os_settings_section.h"
+#include "chrome/browser/ui/webui/settings/ash/reset_section.h"
+#include "chrome/browser/ui/webui/settings/ash/search_section.h"
+
+namespace content {
+class WebUIDataSource;
+} // namespace content
+
+namespace ash::settings {
+
+class SearchTagRegistry;
+
+// Provides UI strings and search tags for System Preferences settings.
+// Includes the Date & Time, Languages, Reset, and Search sections.
+class SystemPreferencesSection : public OsSettingsSection {
+ public:
+ SystemPreferencesSection(Profile* profile,
+ SearchTagRegistry* search_tag_registry,
+ PrefService* pref_service);
+ ~SystemPreferencesSection() override;
+
+ // OsSettingsSection:
+ void AddLoadTimeData(content::WebUIDataSource* html_source) override;
+ void AddHandlers(content::WebUI* web_ui) override;
+ int GetSectionNameMessageId() const override;
+ chromeos::settings::mojom::Section GetSection() const override;
+ mojom::SearchResultIcon GetSectionIcon() const override;
+ const char* GetSectionPath() const override;
+ bool LogMetric(chromeos::settings::mojom::Setting setting,
+ base::Value& value) const override;
+ void RegisterHierarchy(HierarchyGenerator* generator) const override;
+
+ private:
+ DateTimeSection date_time_subsection_;
+ LanguagesSection languages_subsection_;
+ ResetSection reset_subsection_;
+ SearchSection search_subsection_;
+};
+
+} // namespace ash::settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SYSTEM_PREFERENCES_SECTION_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/ash/tts_handler.cc b/chromium/chrome/browser/ui/webui/settings/ash/tts_handler.cc
index 987de613a82..bc454c26bb7 100644
--- a/chromium/chrome/browser/ui/webui/settings/ash/tts_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/ash/tts_handler.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/ui/webui/settings/ash/tts_handler.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "base/functional/bind.h"
#include "base/i18n/rtl.h"
#include "base/json/json_reader.h"
@@ -13,7 +14,6 @@
#include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
#include "chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.h"
#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/tts_controller.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc b/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
index f630f060e77..051465b6080 100644
--- a/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
@@ -8,8 +8,9 @@
#include "base/functional/callback_helpers.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/lifetime/application_lifetime.h"
-#include "chrome/browser/policy/management_utils.h"
+#include "components/policy/core/common/management/management_service.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/tpm_firmware_update.h"
@@ -121,7 +122,7 @@ void BrowserLifetimeHandler::HandleFactoryReset(const base::Value::List& args) {
// TODO(crbug.com/891905): Centralize powerwash restriction checks.
bool allow_powerwash =
- !policy::IsDeviceEnterpriseManaged() &&
+ !policy::ManagementServiceFactory::GetForPlatform()->IsManaged() &&
!user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
!user_manager::UserManager::Get()->IsLoggedInAsChildUser();
diff --git a/chromium/chrome/browser/ui/webui/settings/captions_handler.cc b/chromium/chrome/browser/ui/webui/settings/captions_handler.cc
index c3a94da8ae1..50328b0e809 100644
--- a/chromium/chrome/browser/ui/webui/settings/captions_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/captions_handler.cc
@@ -44,6 +44,22 @@ std::vector<std::string> GetEnabledLanguages() {
",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
}
+base::Value::List SortByDisplayName(
+ std::vector<base::Value::Dict> language_packs) {
+ std::sort(language_packs.begin(), language_packs.end(),
+ [](const base::Value::Dict& a, const base::Value::Dict& b) {
+ return *(a.Find(kDisplayNameKey)->GetIfString()) <
+ *(b.Find(kDisplayNameKey)->GetIfString());
+ });
+
+ base::Value::List sorted_language_packs;
+ for (base::Value::Dict& language_pack : language_packs) {
+ sorted_language_packs.Append(std::move(language_pack));
+ }
+
+ return sorted_language_packs;
+}
+
} // namespace
namespace settings {
@@ -150,7 +166,7 @@ void CaptionsHandler::HandleInstallLanguagePacks(
base::Value::List CaptionsHandler::GetAvailableLanguagePacks() {
auto enabled_languages = GetEnabledLanguages();
- base::Value::List available_language_packs;
+ std::vector<base::Value::Dict> available_language_packs;
for (const auto& config : speech::kLanguageComponentConfigs) {
if (config.language_code != speech::LanguageCode::kNone &&
base::Contains(enabled_languages, config.language_name)) {
@@ -164,15 +180,15 @@ base::Value::List CaptionsHandler::GetAvailableLanguagePacks() {
kNativeDisplayNameKey,
speech::GetLanguageDisplayName(config.language_name,
config.language_name));
- available_language_packs.Append(std::move(available_language_pack));
+ available_language_packs.push_back(std::move(available_language_pack));
}
}
- return available_language_packs;
+ return SortByDisplayName(std::move(available_language_packs));
}
base::Value::List CaptionsHandler::GetInstalledLanguagePacks() {
- base::Value::List installed_language_packs;
+ std::vector<base::Value::Dict> installed_language_packs;
for (const auto& language : g_browser_process->local_state()->GetList(
prefs::kSodaRegisteredLanguagePacks)) {
base::Value::Dict installed_language_pack;
@@ -188,11 +204,11 @@ base::Value::List CaptionsHandler::GetInstalledLanguagePacks() {
kNativeDisplayNameKey,
speech::GetLanguageDisplayName(config->language_name,
config->language_name));
- installed_language_packs.Append(std::move(installed_language_pack));
+ installed_language_packs.push_back(std::move(installed_language_pack));
}
}
- return installed_language_packs;
+ return SortByDisplayName(std::move(installed_language_packs));
}
void CaptionsHandler::OnSodaInstalled(speech::LanguageCode language_code) {
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/DIR_METADATA b/chromium/chrome/browser/ui/webui/settings/chromeos/DIR_METADATA
deleted file mode 100644
index e84643a0ace..00000000000
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/DIR_METADATA
+++ /dev/null
@@ -1 +0,0 @@
-mixins: "//chrome/browser/resources/settings/chromeos/COMMON_METADATA"
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/OWNERS b/chromium/chrome/browser/ui/webui/settings/chromeos/OWNERS
deleted file mode 100644
index 7942dfa92ff..00000000000
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://chrome/browser/resources/ash/settings/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/BUILD.gn b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/BUILD.gn
deleted file mode 100644
index 2442c0221cd..00000000000
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/BUILD.gn
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright 2020 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/chromeos/ui_mode.gni")
-import("//mojo/public/tools/bindings/mojom.gni")
-
-assert(is_chromeos)
-
-mojom("mojom") {
- sources = [
- "routes.mojom",
- "setting.mojom",
- ]
- webui_module_path = "/"
- use_typescript_sources = true
-}
-
-action("gen_routes") {
- script = "gen_routes.py"
- outputs = [ "$target_gen_dir/routes.h" ]
- deps = [ ":mojom" ]
- args = [
- "--gen_dir",
- rebase_path(target_gen_dir, root_build_dir),
- ]
-}
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/gen_routes.py b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/gen_routes.py
deleted file mode 100755
index d3428d1b22d..00000000000
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/gen_routes.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2022 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-""" Helper tool to generate cpp header file with all routes paths.
-
-All route paths are defined in `routes.mojom` as string constants.
-Unfortunately there is no collection in the generated cpp binding that contains
-all these strings. Instead of manually write all the routes in a cpp file, this
-tool automatically generates it to avoid human errors.
-
-"""
-
-import argparse
-import os
-import pathlib
-import sys
-
-# Path of the <chromium>/src directory.
-CHROMIUM_SRC_PATH = pathlib.Path(__file__).parents[7].resolve(strict=True)
-if CHROMIUM_SRC_PATH.name != 'src':
- raise AssertionError(
- f'CHROMIUM_SRC_PATH "{CHROMIUM_SRC_PATH}" should end in "src".')
-
-# Insert chromium mojom path to beginning to make sure we prefer this one.
-# https://crbug.com/1422422
-sys.path.insert(0, os.path.join(CHROMIUM_SRC_PATH, "mojo/public/tools/mojom"))
-from mojom.generate.module import Constant
-from mojom.generate.module import Module
-
-
-def parse_arguments(arguments):
- parser = argparse.ArgumentParser()
- parser.add_argument('--gen_dir',
- help='The generated files path for routes.mojom',
- required=True)
- return parser.parse_args(arguments)
-
-
-def main(args):
- parsed_args = parse_arguments(args)
- module_path = os.path.join(parsed_args.gen_dir, 'routes.mojom-module')
-
- with open(module_path, 'rb') as f:
- module = Module.Load(f)
-
- this_file_path = os.path.relpath(pathlib.Path(__file__), CHROMIUM_SRC_PATH)
- result = f'''// This file is autogenerated by {this_file_path}
-
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CONSTANTS_ROUTES_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CONSTANTS_ROUTES_H_
-
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
-
-namespace chromeos::settings {{
-
-inline const char* const kPaths[] = {{
-'''
- for c in module.constants:
- result += f' mojom::{c.mojom_name},\n'
- result += '''
-};
-
-} // namespace chromeos::settings
-
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CONSTANTS_ROUTES_H_
-'''
- output_path = os.path.join(parsed_args.gen_dir, 'routes.h')
- with open(output_path, 'w') as f:
- f.write(result)
-
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
deleted file mode 100644
index fb28c8abb20..00000000000
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module chromeos.settings.mojom;
-
-// ChromeOS Settings sections (i.e., top-level pages & their navigation items).
-// Each section has a corresponding path string listed below. Numerical values
-// are used for metrics.
-// DO NOT change or reuse values.
-enum Section {
- kNetwork = 0,
- kBluetooth = 1,
- kMultiDevice = 2,
- kPeople = 3,
- kDevice = 4,
- kPersonalization = 5,
- kSearchAndAssistant = 6,
- kApps = 7,
- kCrostini = 8,
- // Note: Value 9 was for deprecated Plugin VM section - see
- // https://crbug.com/1074101. Do not reuse.
- kDateAndTime = 10,
- kPrivacyAndSecurity = 11,
- kLanguagesAndInput = 12,
- kFiles = 13,
- kPrinting = 14,
- kAccessibility = 15,
- kReset = 16,
- kAboutChromeOs = 17,
- kKerberos = 18,
-};
-
-// Chrome OS Settings subpages (i.e., nested pages within a section). Each
-// subpage has a corresponding path string listed below. Numerical values are
-// used for metrics; do not change or reuse values.
-enum Subpage {
- // Network section.
- kEthernetDetails = 0,
- kWifiNetworks = 1,
- kWifiDetails = 2,
- kKnownNetworks = 3,
- kMobileDataNetworks = 4,
- kCellularDetails = 5,
- kTetherDetails = 6,
- kVpnDetails = 7,
- kApn = 8,
- kHotspotDetails = 9,
- kPasspointDetails = 10,
-
- // Bluetooth section.
- kBluetoothDevices = 100,
- kBluetoothDeviceDetail = 101,
- kBluetoothSavedDevices = 102,
-
- // MultiDevice section.
- kMultiDeviceFeatures = 200,
- // Note: Value 201 was for deprecated SmartLock subpage - see b/227674947.
- // Do not reuse.
- kNearbyShare = 202,
-
- // People section.
- kMyAccounts = 300,
- kSync = 301,
- kSyncSetup = 302,
- kSyncDeprecatedAdvanced = 303, // Used if split-sync flag is off.
- // Note: Value 304 was for kSecurityAndSignIn. Do not reuse.
- // Note: Value 305 was for kFingerprint. Do not reuse.
- // Note: Value 306 was for kManageOtherPeople. Do not reuse.
-
- // Note: Value 307 was for Kerberos Accounts Subpage, which has been migrated
- // to a separate section - see https://crbug.com/1186190. Do not reuse.
-
- // Device section.
- kPointers = 400,
- kKeyboard = 401,
- kStylus = 402,
- kDisplay = 403,
- kStorage = 404,
- kExternalStorage = 405,
- // Note: Value 406 was for deprecated DLC Subpage - see
- // https://crbug.com/1108093. Do not reuse.
- kPower = 407,
- kAudio = 408,
- kPerDeviceKeyboard = 409,
- kPerDeviceMouse = 410,
- kPerDeviceTouchpad = 411,
- kPerDevicePointingStick = 412,
- kPerDeviceKeyboardRemapKeys = 413,
- kGraphicsTablet = 414,
-
- // Personalization section.
- // 500 was used for kChangePicture. Do not reuse.
- // 501 was used for kAmbientMode. Do not reuse.
- // Note: Value 502 was for deprecated kAmbientModePhotos. Do not reuse.
- // 503 was used for kAmbientModeGooglePhotosAlbum. Do not reuse.
- // 504 was used for kAmbientModeArtGalleryAlbum. Do not reuse.
- // 505 was used for kWallpaper. Do not reuse.
- // 506 was used for kWallpaperImages. Do not reuse.
- // 507 was used for kDarkMode. Do not reuse.
-
- // Search and Assistant section.
- kAssistant = 600,
- kSearch = 601,
-
- // Apps section.
- kAppManagement = 700,
- kAppDetails = 701,
- kGooglePlayStore = 702,
- kPluginVmSharedPaths = 703,
- kPluginVmUsbPreferences = 704,
- // Note: Value 705 was for deprecated kOnStartup. Do not reuse.
- kAppNotifications = 706,
- kArcVmUsbPreferences = 707,
-
- // Crostini section.
- kCrostiniDetails = 800,
- kCrostiniManageSharedFolders = 801,
- kCrostiniUsbPreferences = 802,
- kCrostiniBackupAndRestore = 803,
- kCrostiniDevelopAndroidApps = 804,
- kCrostiniPortForwarding = 805,
- kCrostiniExtraContainers = 806,
- kBruschettaDetails = 850,
- kBruschettaUsbPreferences = 851,
- kBruschettaManageSharedFolders = 852,
-
- // Note: Deprecated Plugin VM section has no subpages.
-
- // Date and Time section.
- kTimeZone = 1000,
-
- // Privacy section.
- kManageOtherPeopleV2 = 1100,
- kSecurityAndSignInV2 = 1101,
- kFingerprintV2 = 1102,
- kSmartPrivacy = 1103,
- kPrivacyHub = 1104,
-
- // Languages and Input section.
- // 1200 was used for kLanguagesAndInputDetails. Do not reuse.
- // 1201 was used for kManageInputMethods. Do not reuse.
- kSmartInputs = 1202,
- kInputMethodOptions = 1203,
- kLanguages = 1204,
- kInput = 1205,
- kEditDictionary = 1206,
- kJapaneseManageUserDictionary = 1207,
-
- // Files section.
- kNetworkFileShares = 1300,
- kOfficeFiles = 1301,
- kGoogleDrive = 1302,
- kOneDrive = 1303,
-
- // Printing section.
- kPrintingDetails = 1400,
-
- // Accessibility section.
- kManageAccessibility = 1500,
- kTextToSpeech = 1502,
- kSwitchAccessOptions = 1503,
- // 1504 was used for kCaptions. Do not reuse.
- kTextToSpeechPage = 1505,
- kDisplayAndMagnification = 1506,
- kKeyboardAndTextInput = 1507,
- kCursorAndTouchpad = 1508,
- kAudioAndCaptions = 1509,
- kSelectToSpeak = 1510,
- kChromeVox = 1511,
-
- // Note: Reset section has no subpages.
-
- // About Chrome OS section.
- kAboutChromeOsDetails = 1700,
- kDetailedBuildInfo = 1701,
-
- // Kerberos section.
- kKerberosAccountsV2 = 1800,
-};
-
-// Network section.
-const string kNetworkSectionPath = "internet";
-const string kEthernetDetailsSubpagePath = "networkDetail";
-const string kNetworksSubpageBasePath = "networks";
-const string kWifiNetworksSubpagePath = "networks?type=WiFi";
-const string kCellularNetworksSubpagePath = "networks?type=Cellular";
-const string kMobileDataNetworksSubpagePath = "networks?type=Tether";
-const string kKnownNetworksSubpagePath = "knownNetworks";
-const string kWifiDetailsSubpagePath = "networkDetail";
-const string kCellularDetailsSubpagePath = "networkDetail";
-const string kTetherDetailsSubpagePath = "networkDetail";
-const string kVpnDetailsSubpagePath = "networkDetail";
-const string kApnSubpagePath = "apn";
-const string kHotspotSubpagePath = "hotspotDetail";
-const string kPasspointDetailSubpagePath = "passpointDetail";
-
-// Bluetooth section.
-const string kBluetoothSectionPath = "bluetooth";
-const string kBluetoothDevicesSubpagePath = "bluetoothDevices";
-const string kBluetoothDeviceDetailSubpagePath = "bluetoothDeviceDetail";
-const string kBluetoothSavedDevicesSubpagePath = "bluetoothSavedDevices";
-
-// MultiDevice section.
-const string kMultiDeviceSectionPath = "multidevice";
-const string kMultiDeviceFeaturesSubpagePath = "multidevice/features";
-const string kNearbyShareSubpagePath = "multidevice/nearbyshare";
-
-// People section.
-const string kPeopleSectionPath = "osPeople";
-const string kMyAccountsSubpagePath = "accountManager";
-const string kSyncSubpagePath = "osSync";
-const string kSyncSetupSubpagePath = "osSyncSetup";
-const string kSyncDeprecatedAdvancedSubpagePath = "osSyncSetup/advanced";
-
-// Device section.
-const string kDeviceSectionPath = "device";
-const string kPointersSubpagePath = "pointer-overlay";
-const string kPerDeviceKeyboardSubpagePath = "per-device-keyboard";
-const string kPerDeviceKeyboardRemapKeysSubpagePath =
- "per-device-keyboard/remap-keys";
-const string kPerDeviceMouseSubpagePath = "per-device-mouse";
-const string kPerDevicePointingStickSubpagePath = "per-device-pointing-stick";
-const string kPerDeviceTouchpadSubpagePath = "per-device-touchpad";
-const string kKeyboardSubpagePath = "keyboard-overlay";
-const string kStylusSubpagePath = "stylus";
-const string kDisplaySubpagePath = "display";
-const string kAudioSubpagePath = "audio";
-const string kStorageSubpagePath = "storage";
-const string kExternalStorageSubpagePath = "storage/externalStoragePreferences";
-const string kPowerSubpagePath = "power";
-const string kGraphicsTabletSubpagePath = "graphics-tablet";
-
-// Personalization section.
-const string kPersonalizationSectionPath = "personalization";
-
-// Search and Assistant section.
-const string kSearchAndAssistantSectionPath = "osSearch";
-const string kAssistantSubpagePath = "googleAssistant";
-const string kSearchSubpagePath = "osSearch/search";
-
-// Apps section.
-const string kAppsSectionPath = "apps";
-const string kAppManagementSubpagePath = "app-management";
-const string kAppNotificationsSubpagePath = "app-notifications";
-const string kAppDetailsSubpagePath = "app-management/detail";
-const string kGooglePlayStoreSubpagePath = "androidAppsDetails";
-const string kPluginVmSharedPathsSubpagePath =
- "app-management/pluginVm/sharedPaths";
-const string kPluginVmUsbPreferencesSubpagePath =
- "app-management/pluginVm/sharedUsbDevices";
-const string kArcVmUsbPreferencesSubpagePath =
- "androidAppsDetails/sharedUsbDevices";
-
-// Crostini section.
-const string kCrostiniSectionPath = "crostini";
-const string kCrostiniDetailsSubpagePath = "crostini/details";
-const string kCrostiniManageSharedFoldersSubpagePath = "crostini/sharedPaths";
-const string kCrostiniUsbPreferencesSubpagePath = "crostini/sharedUsbDevices";
-const string kCrostiniBackupAndRestoreSubpagePath = "crostini/exportImport";
-const string kCrostiniDevelopAndroidAppsSubpagePath = "crostini/androidAdb";
-const string kCrostiniPortForwardingSubpagePath = "crostini/portForwarding";
-const string kCrostiniExtraContainersSubpagePath = "crostini/extraContainers";
-const string kBruschettaDetailsSubpagePath = "bruschetta/details";
-const string kBruschettaUsbPreferencesSubpagePath =
- "bruschetta/sharedUsbDevices";
-const string kBruschettaManageSharedFoldersSubpagePath =
- "bruschetta/sharedPaths";
-
-// Date and Time section.
-const string kDateAndTimeSectionPath = "dateTime";
-const string kTimeZoneSubpagePath = "dateTime/timeZone";
-
-// Privacy and Security section.
-const string kPrivacyAndSecuritySectionPath = "osPrivacy";
-const string kManageOtherPeopleSubpagePathV2 = "osPrivacy/accounts";
-const string kSecurityAndSignInSubpagePathV2 = "osPrivacy/lockScreen";
-const string kFingerprintSubpagePathV2 = "osPrivacy/lockScreen/fingerprint";
-const string kSmartPrivacySubpagePath = "osPrivacy/smartPrivacy";
-const string kPrivacyHubSubpagePath = "osPrivacy/privacyHub";
-
-// Languages and Input section.
-const string kLanguagesAndInputSectionPath = "osLanguages";
-const string kSmartInputsSubpagePath = "osLanguages/smartInputs";
-const string kInputMethodOptionsSubpagePath = "osLanguages/inputMethodOptions";
-const string kLanguagesSubpagePath = "osLanguages/languages";
-const string kInputSubpagePath = "osLanguages/input";
-const string kEditDictionarySubpagePath = "osLanguages/editDictionary";
-const string kJapaneseManageUserDictionarySubpagePath =
- "osLanguages/japaneseManageUserDictionary";
-
-// Files section.
-const string kFilesSectionPath = "files";
-const string kGoogleDriveSubpagePath = "googleDrive";
-const string kOneDriveSubpagePath = "oneDrive";
-const string kOfficeFilesSubpagePath = "officeFiles";
-const string kNetworkFileSharesSubpagePath = "smbShares";
-
-// Printing section.
-const string kPrintingSectionPath = "osPrinting";
-const string kPrintingDetailsSubpagePath = "cupsPrinters";
-
-// Accessibility section.
-const string kAccessibilitySectionPath = "osAccessibility";
-const string kManageAccessibilitySubpagePath = "manageAccessibility";
-const string kTextToSpeechPagePath = "textToSpeech";
-const string kDisplayAndMagnificationSubpagePath = "displayAndMagnification";
-const string kKeyboardAndTextInputSubpagePath = "keyboardAndTextInput";
-const string kCursorAndTouchpadSubpagePath = "cursorAndTouchpad";
-const string kSelectToSpeakSubpagePath = "textToSpeech/selectToSpeak";
-const string kChromeVoxSubpagePath = "textToSpeech/chromeVox";
-const string kAudioAndCaptionsSubpagePath = "audioAndCaptions";
-const string kTextToSpeechSubpagePath = "manageAccessibility/tts";
-const string kSwitchAccessOptionsSubpagePath =
- "manageAccessibility/switchAccess";
-
-// Reset section.
-const string kResetSectionPath = "osReset";
-
-// About Chrome OS section.
-const string kAboutChromeOsSectionPath = "help";
-const string kAboutChromeOsDetailsSubpagePath = "help/about";
-const string kDetailedBuildInfoSubpagePath = "help/details";
-
-// Kerberos section.
-const string kKerberosSectionPath = "kerberos";
-const string kKerberosAccountsV2SubpagePath = "kerberos/kerberosAccounts";
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
deleted file mode 100644
index 5a23446a653..00000000000
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CONSTANTS_ROUTES_UTIL_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CONSTANTS_ROUTES_UTIL_H_
-
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes_util.h"
-
-#include <string>
-
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.h"
-
-namespace chromeos::settings {
-
-namespace {
-
-std::string RemoveQuery(std::string path) {
- std::string::size_type input_index = path.find('?');
- if (input_index != std::string::npos)
- path.resize(input_index);
- return path;
-}
-
-} // namespace
-
-bool IsOSSettingsSubPage(const std::string& sub_page) {
- // Sub-pages may have query parameters, e.g. networkDetail?guid=123456.
- std::string sub_page_without_query = RemoveQuery(sub_page);
-
- for (const char* p : kPaths) {
- std::string path_without_query = RemoveQuery(p);
- if (sub_page_without_query == path_without_query)
- return true;
- }
-
- return false;
-}
-
-} // namespace chromeos::settings
-
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CONSTANTS_ROUTES_UTIL_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.h b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.h
deleted file mode 100644
index 677d78e1cdb..00000000000
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CONSTANTS_ROUTES_UTIL_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CONSTANTS_ROUTES_UTIL_H_
-
-#include <string>
-
-namespace chromeos::settings {
-
-// Returns true if the sub-page is one of the above.
-bool IsOSSettingsSubPage(const std::string& sub_page);
-
-} // namespace chromeos::settings
-
-#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CONSTANTS_ROUTES_UTIL_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util_unittest.cc
deleted file mode 100644
index 0b7167e55b2..00000000000
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util_unittest.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes_util.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chromeos::settings {
-
-namespace {
-
-class RoutesUtilTest : public testing::Test {};
-
-TEST_F(RoutesUtilTest, WellKnownRoutesAreValid) {
- ASSERT_TRUE(IsOSSettingsSubPage("help"));
- ASSERT_TRUE(IsOSSettingsSubPage("internet"));
- ASSERT_TRUE(IsOSSettingsSubPage("power"));
- ASSERT_TRUE(IsOSSettingsSubPage("networks"));
- ASSERT_TRUE(IsOSSettingsSubPage("networks?type=Tether"));
- ASSERT_TRUE(IsOSSettingsSubPage("networkDetail?guid=123456"));
-}
-
-TEST_F(RoutesUtilTest, BadRoutesAreValid) {
- ASSERT_FALSE(IsOSSettingsSubPage("a_wrong_url"));
- ASSERT_FALSE(IsOSSettingsSubPage("a_wrong_url?param=bad_param"));
-}
-
-} // namespace
-
-} // namespace chromeos::settings
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
deleted file mode 100644
index 02bfa2e447d..00000000000
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module chromeos.settings.mojom;
-
-// Enumeration of each setting in the Chrome OS Settings app. Numerical values
-// are used for metrics; do not change or reuse values. Add new values to the
-// OsSetting enum in tools/metrics/histograms/enums.xml.
-enum Setting {
- // Network section.
- kConfigureEthernet = 0,
- kEthernetAutoConfigureIp = 1,
- kEthernetDns = 2,
- kEthernetProxy = 3,
- kWifiOnOff = 4,
- kDisconnectWifiNetwork = 5,
- kPreferWifiNetwork = 6,
- kForgetWifiNetwork = 7,
- kWifiAddNetwork = 8,
- kWifiAutoConfigureIp = 9,
- kWifiDns = 10,
- kWifiProxy = 11,
- kWifiAutoConnectToNetwork = 12,
- kMobileOnOff = 13,
- kCellularSimLock = 14,
- kCellularRoaming = 15,
- kCellularApn = 16,
- kDisconnectCellularNetwork = 17,
- kCellularAutoConfigureIp = 18,
- kCellularDns = 19,
- kCellularProxy = 20,
- kCellularAutoConnectToNetwork = 21,
- kInstantTetheringOnOff = 22,
- kDisconnectTetherNetwork = 23,
- kWifiMetered = 24,
- kCellularMetered = 25,
- kAddESimNetwork = 26,
- kCellularRemoveESimNetwork = 27,
- kCellularRenameESimNetwork = 28,
- kWifiHidden = 29,
- kHotspotOnOff = 30,
- kHotspotAutoDisabled = 31,
-
- // Bluetooth section.
- kBluetoothOnOff = 100,
- // [Deprecated] kBluetoothConnectToDevice = 101,
- // [Deprecated] kBluetoothDisconnectFromDevice = 102,
- kBluetoothPairDevice = 103,
- kBluetoothUnpairDevice = 104,
- kFastPairOnOff = 105,
- kFastPairSavedDevices= 106,
-
- // MultiDevice section.
- kSetUpMultiDevice = 200,
- kVerifyMultiDeviceSetup = 201,
- kMultiDeviceOnOff = 202,
- kSmartLockOnOff = 203,
- // Note: Value 204 was for deprecated kSmartLockUnlockOrSignIn -
- // see b/227674947. Do not reuse.
- kMessagesSetUp = 205,
- kMessagesOnOff = 206,
- kForgetPhone = 207,
- kNearbyShareOnOff = 208,
- kPhoneHubOnOff = 209,
- kPhoneHubNotificationsOnOff = 210,
- // Note: Value 211 was for deprecated kPhoneHubNotificationBadgeOnOff.
- // Do not reuse.
- kPhoneHubTaskContinuationOnOff = 212,
- kWifiSyncOnOff = 213,
- kNearbyShareDeviceName = 214,
- kNearbyShareDeviceVisibility = 215,
- kNearbyShareContacts = 216,
- kNearbyShareDataUsage = 217,
- kPhoneHubAppsOnOff = 218,
- kPhoneHubCameraRollOnOff = 219,
- kDevicesNearbyAreSharingNotificationOnOff = 220,
-
- // People section.
- kAddAccount = 300,
- kRemoveAccount = 301,
- kSplitSyncOnOff = 302,
- // Note: Value 303 was for kLockScreen. Do not reuse.
- // Note: Value 304 was for kChangeAuthPin. Do not reuse.
- // Note: Value 305 was for kGuestBrowsing. Do not reuse.
- // Note: Value 306 was for kShowUsernamesAndPhotosAtSignIn. Do not reuse.
- // Note: Value 307 was for kRestrictSignIn. Do not reuse.
- // Note: Value 308 was for kAddToUserAllowlist. Do not reuse.
- // Note: Value 309 was for kRemoveFromUserAllowlist. Do not reuse.
- // Note: Values 310, 311 and 312 were for kAddKerberosTicket,
- // kRemoveKerberosTicket and kSetActiveKerberosTicket, respectively. These
- // settings have been moved to a separate Kerberos section - see
- // https://crbug.com/1186190. Do not reuse.
- // Note: Value 313 was for kAddFingerprint. Do not reuse.
- // Note: Value 314 was for kRemoveFingerprint. Do not reuse.
- kSetUpParentalControls = 315,
- kNonSplitSyncEncryptionOptions = 316,
- kImproveSearchSuggestions = 317,
- kMakeSearchesAndBrowsingBetter = 318,
- kGoogleDriveSearchSuggestions = 319,
-
- // Device section.
- kTouchpadTapToClick = 400,
- kTouchpadTapDragging = 401,
- kTouchpadReverseScrolling = 402,
- kTouchpadAcceleration = 403,
- kTouchpadScrollAcceleration = 404,
- kTouchpadSpeed = 405,
- kMouseSwapPrimaryButtons = 406,
- kMouseReverseScrolling = 407,
- kMouseAcceleration = 408,
- kMouseScrollAcceleration = 409,
- kMouseSpeed = 410,
- kKeyboardFunctionKeys = 411,
- kKeyboardAutoRepeat = 412,
- kKeyboardShortcuts = 413,
- kDisplaySize = 414,
- kNightLight = 415,
- kStylusToolsInShelf = 416,
- kStylusNoteTakingApp = 417,
- kStylusNoteTakingFromLockScreen = 418,
- kStylusLatestNoteOnLockScreen = 419,
- kDisplayOrientation = 420,
- kDisplayArrangement = 421,
- kPowerIdleBehaviorWhileCharging = 422,
- kPowerSource = 423,
- kSleepWhenLaptopLidClosed = 424,
- kDisplayResolution = 425,
- kDisplayRefreshRate = 426,
- // Note: Value 427 was for deprecated Remove DLC - see
- // https://crbug.com/1108093. Do not reuse.
- kDisplayMirroring = 428,
- kAllowWindowsToSpanDisplays = 429,
- kAmbientColors = 430,
- kTouchscreenCalibration = 431,
- kNightLightColorTemperature = 432,
- kPowerIdleBehaviorWhileOnBattery = 433,
- kDisplayOverscan = 434,
- kPointingStickSpeed = 435,
- kPointingStickAcceleration = 436,
- kPointingStickSwapPrimaryButtons = 437,
- kTouchpadHapticFeedback = 438,
- kTouchpadHapticClickSensitivity = 439,
- kAdaptiveCharging = 440,
- kKeyboardBlockMetaFkeyRewrites = 441,
- kKeyboardRemapKeys = 442,
- kChargingSounds = 443,
- kLowBatterySound = 444,
- kBatterySaver = 445,
-
- // Personalization section.
- kOpenWallpaper = 500,
- // 501 was used for kAmbientModeOnOff. Do not reuse.
- // 502 was used for kAmbientModeSource. Do not reuse.
- // 503 was used for kChangeDeviceAccountImage. Do not reuse.
- // Note: Values 504, 505, and 506 were for deprecated
- // kAmbientModeUpdatePhotosContainers, kDarkModeOnOff and
- // kDarkModeThemed respectively.
- // Do not reuse.
-
- // Search and Assistant section.
- kPreferredSearchEngine = 600,
- kAssistantOnOff = 601,
- kAssistantRelatedInfo = 602,
- // Note: Value 603 was for deprecated kAssistantQuickAnswers.
- // Do not reuse.
- kAssistantOkGoogle = 604,
- kAssistantNotifications = 605,
- kAssistantVoiceInput = 606,
- kTrainAssistantVoiceModel = 607,
- kQuickAnswersOnOff = 608,
- kQuickAnswersDefinition = 609,
- kQuickAnswersTranslation = 610,
- kQuickAnswersUnitConversion = 611,
-
- // Apps section.
- kManageAndroidPreferences = 700,
- kRemovePlayStore = 701,
- kTurnOnPlayStore = 702,
- kRestoreAppsAndPages = 703,
- kDoNotDisturbOnOff = 704,
- kAppBadgingOnOff = 705,
-
- // Crostini section.
- kSetUpCrostini= 800,
- kUninstallCrostini = 801,
- kBackupLinuxAppsAndFiles = 802,
- kRestoreLinuxAppsAndFiles = 803,
- kCrostiniAdbDebugging = 804,
- kCrostiniDiskResize = 805,
- kCrostiniMicAccess = 806,
- kCrostiniContainerUpgrade = 807,
-
- // Note: Plugin VM section is omitted (see https://crbug.com/1074101).
-
- // Date and Time section.
- k24HourClock = 1000,
- kChangeTimeZone = 1001,
-
- // Privacy section.
- kVerifiedAccess = 1101,
- // Note: Value 1102 was for deprecated "Keep wi-fi on during sleep" setting -
- // see https://bugs.chromium.org/p/chromium/issues/detail?id=1077126.
- // Do not reuse.
- kUsageStatsAndCrashReports = 1103,
- kGuestBrowsingV2 = 1104,
- kShowUsernamesAndPhotosAtSignInV2 = 1105,
- kRestrictSignInV2 = 1106,
- kAddToUserAllowlistV2 = 1107,
- kRemoveFromUserAllowlistV2 = 1108,
- kLockScreenV2 = 1109,
- kChangeAuthPinV2 = 1110,
- kAddFingerprintV2 = 1111,
- kRemoveFingerprintV2 = 1112,
- kPeripheralDataAccessProtection = 1113,
- kSnoopingProtection = 1114,
- kQuickDim = 1115,
- kCameraOnOff = 1116,
- kMicrophoneOnOff = 1117,
- kGeolocationOnOff = 1118,
- kLockScreenNotification = 1119,
- kSpeakOnMuteDetectionOnOff = 1120,
-
- // Languages and Input section.
- kAddLanguage = 1200,
- kShowInputOptionsInShelf = 1201,
- // Note: Value 1202 was deprecated for kShowPersonalInformationSuggestions.
- // Do not reuse.
- kShowEmojiSuggestions = 1203,
- kChangeDeviceLanguage = 1204,
- kOfferTranslation = 1205,
- kAddInputMethod = 1206,
- kSpellCheck = 1207,
- // Note: Value 1208 was for deprecated kShowPredictiveWriting.
- // Do not reuse.
- // TODO(crbug/1058552): Add [Deprecated] attribute once implemented.
- kShowPKAutoCorrection = 1209,
- kShowVKAutoCorrection = 1210,
- kShowDiacritic = 1211,
-
- // Files section.
- kGoogleDriveConnection = 1300,
-
- // Printing section.
- kAddPrinter = 1400,
- kSavedPrinters = 1401,
- kPrintJobs = 1402,
- kScanningApp = 1403,
-
- // Accessibility section.
- kA11yQuickSettings = 1500,
- kChromeVox = 1501,
- kSelectToSpeak = 1502,
- kTextToSpeechRate = 1503,
- kTextToSpeechPitch = 1504,
- kTextToSpeechVolume = 1505,
- kTextToSpeechVoice = 1506,
- kTextToSpeechEngines = 1507,
- kHighContrastMode = 1508,
- kFullscreenMagnifier = 1509,
- kDockedMagnifier = 1510,
- kStickyKeys = 1511,
- kOnScreenKeyboard = 1512,
- kDictation = 1513,
- kHighlightKeyboardFocus = 1514,
- kHighlightTextCaret = 1515,
- kAutoClickWhenCursorStops = 1516,
- kLargeCursor = 1517,
- kHighlightCursorWhileMoving = 1518,
- kTabletNavigationButtons = 1519,
- kMonoAudio = 1520,
- kStartupSound = 1521,
- kEnableSwitchAccess = 1522,
- kSwitchActionAssignment = 1523,
- kSwitchActionAutoScan = 1524,
- kSwitchActionAutoScanKeyboard = 1525,
- kGetImageDescriptionsFromGoogle = 1526,
- kLiveCaption = 1527,
- kEnableCursorColor = 1528,
- kFullscreenMagnifierFocusFollowing = 1529,
- kFullscreenMagnifierMouseFollowingMode = 1530,
- kSelectToSpeakWordHighlight = 1531,
- kSelectToSpeakBackgroundShading = 1532,
- kSelectToSpeakNavigationControls = 1533,
-
- // Reset section.
- kPowerwash = 1600,
-
- // About Chrome OS section.
- kChangeChromeChannel = 1700,
- kCopyDetailedBuildInfo = 1701,
- kCheckForOsUpdate = 1702,
- kSeeWhatsNew = 1703,
- kGetHelpWithChromeOs = 1704,
- kReportAnIssue = 1705,
- kTermsOfService = 1706,
- kDiagnostics = 1707,
- kChangeDeviceName = 1708,
- kFirmwareUpdates = 1709,
-
- // Kerberos section.
- kAddKerberosTicketV2 = 1800,
- kRemoveKerberosTicketV2 = 1801,
- kSetActiveKerberosTicketV2 = 1802,
-};
diff --git a/chromium/chrome/browser/ui/webui/settings/hats_handler.cc b/chromium/chrome/browser/ui/webui/settings/hats_handler.cc
index f57cb4f788f..36aaff36842 100644
--- a/chromium/chrome/browser/ui/webui/settings/hats_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/hats_handler.cc
@@ -38,6 +38,25 @@ SurveyBitsData GetPrivacySettingsProductSpecificBitsData(Profile* profile) {
{"Privacy Sandbox enabled", privacy_sandbox_enabled}};
}
+// Generate the Product Specific bits data which accompanies M1 Ad Privacy
+// survey responses from |profile|.
+SurveyBitsData GetAdPrivacyProductSpecificBitsData(Profile* profile) {
+ const bool third_party_cookies_blocked =
+ static_cast<content_settings::CookieControlsMode>(
+ profile->GetPrefs()->GetInteger(prefs::kCookieControlsMode)) ==
+ content_settings::CookieControlsMode::kBlockThirdParty;
+
+ return {
+ {"3P cookies blocked", third_party_cookies_blocked},
+ {"Topics enabled",
+ profile->GetPrefs()->GetBoolean(prefs::kPrivacySandboxM1TopicsEnabled)},
+ {"Fledge enabled",
+ profile->GetPrefs()->GetBoolean(prefs::kPrivacySandboxM1FledgeEnabled)},
+ {"Ad Measurement enabled",
+ profile->GetPrefs()->GetBoolean(
+ prefs::kPrivacySandboxM1AdMeasurementEnabled)},
+ };
+}
} // namespace
namespace settings {
@@ -79,50 +98,112 @@ void HatsHandler::RequestHatsSurvey(TrustSafetyInteraction interaction) {
if (!hats_service)
return;
- if (interaction == TrustSafetyInteraction::OPENED_PRIVACY_SANDBOX) {
- hats_service->LaunchDelayedSurveyForWebContents(
- kHatsSurveyTriggerPrivacySandbox, web_ui()->GetWebContents(),
- features::kHappinessTrackingSurveysForDesktopPrivacySandboxTime.Get()
- .InMilliseconds(),
- GetPrivacySettingsProductSpecificBitsData(profile),
- /*product_specific_string_data=*/{},
- /*require_same_origin=*/true);
- } else if (interaction == TrustSafetyInteraction::RAN_SAFETY_CHECK ||
- interaction == TrustSafetyInteraction::USED_PRIVACY_CARD) {
- // The control group for the Privacy guide HaTS experiment will need to see
- // either safety check or the privacy page to be eligible and have never
- // seen privacy guide.
- if (features::kHappinessTrackingSurveysForDesktopSettingsPrivacyNoGuide
- .Get() &&
- Profile::FromWebUI(web_ui())->GetPrefs()->GetBoolean(
- prefs::kPrivacyGuideViewed)) {
- return;
+ std::string trigger = "";
+ int timeout_ms = 0;
+ SurveyBitsData product_specific_bits_data = {};
+ bool require_same_origin = false;
+
+ switch (interaction) {
+ case TrustSafetyInteraction::OPENED_PRIVACY_SANDBOX: {
+ trigger = kHatsSurveyTriggerPrivacySandbox;
+ timeout_ms =
+ features::kHappinessTrackingSurveysForDesktopPrivacySandboxTime.Get()
+ .InMilliseconds();
+ product_specific_bits_data =
+ GetPrivacySettingsProductSpecificBitsData(profile);
+ require_same_origin = true;
+ break;
}
- // If the privacy settings survey is explicitly targeting users who have not
- // viewed the Privacy Sandbox page, and this user has viewed the page, do
- // not attempt to show the privacy settings survey.
- if (features::kHappinessTrackingSurveysForDesktopSettingsPrivacyNoSandbox
- .Get() &&
- Profile::FromWebUI(web_ui())->GetPrefs()->GetBoolean(
- prefs::kPrivacySandboxPageViewed)) {
+ case TrustSafetyInteraction::RAN_SAFETY_CHECK:
+ [[fallthrough]];
+ case TrustSafetyInteraction::USED_PRIVACY_CARD: {
+ // The control group for the Privacy guide HaTS experiment will need to
+ // see either safety check or the privacy page to be eligible and have
+ // never seen privacy guide.
+ if (features::kHappinessTrackingSurveysForDesktopSettingsPrivacyNoGuide
+ .Get() &&
+ Profile::FromWebUI(web_ui())->GetPrefs()->GetBoolean(
+ prefs::kPrivacyGuideViewed)) {
+ return;
+ }
+ // If the privacy settings survey is explicitly targeting users who have
+ // not viewed the Privacy Sandbox page, and this user has viewed the page,
+ // do not attempt to show the privacy settings survey.
+ if (features::kHappinessTrackingSurveysForDesktopSettingsPrivacyNoSandbox
+ .Get() &&
+ Profile::FromWebUI(web_ui())->GetPrefs()->GetBoolean(
+ prefs::kPrivacySandboxPageViewed)) {
+ return;
+ }
+ trigger = kHatsSurveyTriggerSettingsPrivacy;
+ timeout_ms =
+ features::kHappinessTrackingSurveysForDesktopSettingsPrivacyTime.Get()
+ .InMilliseconds();
+ product_specific_bits_data =
+ GetPrivacySettingsProductSpecificBitsData(profile);
+ require_same_origin = true;
+ break;
+ }
+ case TrustSafetyInteraction::COMPLETED_PRIVACY_GUIDE: {
+ trigger = kHatsSurveyTriggerPrivacyGuide;
+ timeout_ms =
+ features::kHappinessTrackingSurveysForDesktopPrivacyGuideTime.Get()
+ .InMilliseconds();
+ require_same_origin = true;
+ break;
+ }
+ case TrustSafetyInteraction::OPENED_AD_PRIVACY: {
+ trigger = kHatsSurveyTriggerM1AdPrivacyPage;
+ timeout_ms =
+ features::kHappinessTrackingSurveysForDesktopM1AdPrivacyPageTime.Get()
+ .InMilliseconds();
+ require_same_origin = true;
+ product_specific_bits_data = GetAdPrivacyProductSpecificBitsData(profile);
+ break;
+ }
+ case TrustSafetyInteraction::OPENED_TOPICS_SUBPAGE: {
+ trigger = kHatsSurveyTriggerM1TopicsSubpage;
+ timeout_ms =
+ features::kHappinessTrackingSurveysForDesktopM1TopicsSubpageTime.Get()
+ .InMilliseconds();
+ require_same_origin = true;
+ product_specific_bits_data = GetAdPrivacyProductSpecificBitsData(profile);
+ break;
+ }
+ case TrustSafetyInteraction::OPENED_FLEDGE_SUBPAGE: {
+ trigger = kHatsSurveyTriggerM1FledgeSubpage;
+ timeout_ms =
+ features::kHappinessTrackingSurveysForDesktopM1FledgeSubpageTime.Get()
+ .InMilliseconds();
+ require_same_origin = true;
+ product_specific_bits_data = GetAdPrivacyProductSpecificBitsData(profile);
+ break;
+ }
+ case TrustSafetyInteraction::OPENED_AD_MEASUREMENT_SUBPAGE: {
+ trigger = kHatsSurveyTriggerM1AdMeasurementSubpage;
+ timeout_ms =
+ features::
+ kHappinessTrackingSurveysForDesktopM1AdMeasurementSubpageTime
+ .Get()
+ .InMilliseconds();
+ require_same_origin = true;
+ product_specific_bits_data = GetAdPrivacyProductSpecificBitsData(profile);
+ break;
+ }
+ case TrustSafetyInteraction::OPENED_PASSWORD_MANAGER:
+ [[fallthrough]];
+ case TrustSafetyInteraction::RAN_PASSWORD_CHECK: {
+ // Only relevant for the sentiment service
return;
}
- hats_service->LaunchDelayedSurveyForWebContents(
- kHatsSurveyTriggerSettingsPrivacy, web_ui()->GetWebContents(),
- features::kHappinessTrackingSurveysForDesktopSettingsPrivacyTime.Get()
- .InMilliseconds(),
- GetPrivacySettingsProductSpecificBitsData(profile),
- /*product_specific_string_data=*/{},
- /*require_same_origin=*/true);
- } else if (interaction == TrustSafetyInteraction::COMPLETED_PRIVACY_GUIDE) {
- hats_service->LaunchDelayedSurveyForWebContents(
- kHatsSurveyTriggerPrivacyGuide, web_ui()->GetWebContents(),
- features::kHappinessTrackingSurveysForDesktopPrivacyGuideTime.Get()
- .InMilliseconds(),
- /*product_specific_bits_data=*/{},
- /*product_specific_string_data=*/{},
- /*require_same_origin=*/true);
}
+ // If we haven't returned, a trigger must have been set in the switch above.
+ CHECK_NE(trigger, "");
+
+ hats_service->LaunchDelayedSurveyForWebContents(
+ trigger, web_ui()->GetWebContents(), timeout_ms,
+ product_specific_bits_data,
+ /*product_specific_string_data=*/{}, require_same_origin);
}
void HatsHandler::InformSentimentService(TrustSafetyInteraction interaction) {
diff --git a/chromium/chrome/browser/ui/webui/settings/hats_handler.h b/chromium/chrome/browser/ui/webui/settings/hats_handler.h
index 82239e76fa4..45a404d73d0 100644
--- a/chromium/chrome/browser/ui/webui/settings/hats_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/hats_handler.h
@@ -28,6 +28,7 @@ class HatsHandler : public SettingsPageUIHandler {
private:
friend class HatsHandlerTest;
+ friend class HatsHandlerParamTest;
FRIEND_TEST_ALL_PREFIXES(HatsHandlerTest, PrivacySettingsHats);
FRIEND_TEST_ALL_PREFIXES(HatsHandlerTest, PrivacyGuideHats);
FRIEND_TEST_ALL_PREFIXES(HatsHandlerTest, PrivacySandboxHats);
@@ -35,6 +36,7 @@ class HatsHandler : public SettingsPageUIHandler {
FRIEND_TEST_ALL_PREFIXES(HatsHandlerNoSandboxTest, PrivacySettings);
FRIEND_TEST_ALL_PREFIXES(HatsHandlerNoSandboxTest,
TrustSafetySentimentInteractions);
+ FRIEND_TEST_ALL_PREFIXES(HatsHandlerParamTest, AdPrivacyHats);
// All Trust & Safety based interactions which may result in a HaTS survey.
// Must be kept in sync with the enum of the same name in
@@ -46,6 +48,10 @@ class HatsHandler : public SettingsPageUIHandler {
OPENED_PASSWORD_MANAGER = 3,
COMPLETED_PRIVACY_GUIDE = 4,
RAN_PASSWORD_CHECK = 5,
+ OPENED_AD_PRIVACY = 6,
+ OPENED_TOPICS_SUBPAGE = 7,
+ OPENED_FLEDGE_SUBPAGE = 8,
+ OPENED_AD_MEASUREMENT_SUBPAGE = 9,
};
// Requests the appropriate HaTS survey, which may be none, for |interaction|.
diff --git a/chromium/chrome/browser/ui/webui/settings/hats_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/hats_handler_unittest.cc
index 54c4e57cea4..f13660a0870 100644
--- a/chromium/chrome/browser/ui/webui/settings/hats_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/hats_handler_unittest.cc
@@ -226,4 +226,52 @@ TEST_F(HatsHandlerNoSandboxTest, TrustSafetySentimentInteractions) {
handler()->HandleTrustSafetyInteractionOccurred(args);
}
+class HatsHandlerParamTest : public HatsHandlerTest,
+ public testing::WithParamInterface<bool> {};
+
+TEST_P(HatsHandlerParamTest, AdPrivacyHats) {
+ auto cookie_setting =
+ GetParam() ? content_settings::CookieControlsMode::kBlockThirdParty
+ : content_settings::CookieControlsMode::kIncognitoOnly;
+ profile()->GetPrefs()->SetInteger(prefs::kCookieControlsMode,
+ static_cast<int>(cookie_setting));
+ profile()->GetPrefs()->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled,
+ GetParam());
+ profile()->GetPrefs()->SetBoolean(prefs::kPrivacySandboxM1FledgeEnabled,
+ GetParam());
+ profile()->GetPrefs()->SetBoolean(
+ prefs::kPrivacySandboxM1AdMeasurementEnabled, GetParam());
+ SurveyBitsData expected_product_specific_data = {
+ {"3P cookies blocked", GetParam()},
+ {"Topics enabled", GetParam()},
+ {"Fledge enabled", GetParam()},
+ {"Ad Measurement enabled", GetParam()}};
+
+ auto interaction_to_survey =
+ std::map<HatsHandler::TrustSafetyInteraction, std::string>{
+ {HatsHandler::TrustSafetyInteraction::OPENED_AD_PRIVACY,
+ kHatsSurveyTriggerM1AdPrivacyPage},
+ {HatsHandler::TrustSafetyInteraction::OPENED_TOPICS_SUBPAGE,
+ kHatsSurveyTriggerM1TopicsSubpage},
+ {HatsHandler::TrustSafetyInteraction::OPENED_FLEDGE_SUBPAGE,
+ kHatsSurveyTriggerM1FledgeSubpage},
+ {HatsHandler::TrustSafetyInteraction::OPENED_AD_MEASUREMENT_SUBPAGE,
+ kHatsSurveyTriggerM1AdMeasurementSubpage},
+ };
+
+ for (const auto& [interaction, survey] : interaction_to_survey) {
+ EXPECT_CALL(*mock_hats_service_,
+ LaunchDelayedSurveyForWebContents(
+ survey, web_contents(), 20000,
+ expected_product_specific_data, _, true));
+ base::Value::List args;
+ args.Append(static_cast<int>(interaction));
+ handler()->HandleTrustSafetyInteractionOccurred(args);
+ task_environment()->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_hats_service_);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(AdPrivacy, HatsHandlerParamTest, testing::Bool());
+
} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/mac_system_settings_handler.cc b/chromium/chrome/browser/ui/webui/settings/mac_system_settings_handler.cc
new file mode 100644
index 00000000000..c8accb75f05
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/mac_system_settings_handler.cc
@@ -0,0 +1,32 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/settings/mac_system_settings_handler.h"
+
+#include "base/mac/mac_util.h"
+
+using content::WebContents;
+
+namespace settings {
+
+MacSystemSettingsHandler::MacSystemSettingsHandler() = default;
+MacSystemSettingsHandler::~MacSystemSettingsHandler() = default;
+
+void MacSystemSettingsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "openTrackpadGesturesSettings",
+ base::BindRepeating(
+ &MacSystemSettingsHandler::HandleOpenTrackpadGesturesSettings,
+ base::Unretained(this)));
+}
+
+void MacSystemSettingsHandler::HandleOpenTrackpadGesturesSettings(
+ const base::Value::List& args) {
+ AllowJavascript();
+ // TODO(crbug.com/1473415): Figure out how to directly open the more gestures
+ // subpane. Currently this only opens the first subpane of trackpad settings.
+ base::mac::OpenSystemSettingsPane(base::mac::SystemSettingsPane::kTrackpad);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/mac_system_settings_handler.h b/chromium/chrome/browser/ui/webui/settings/mac_system_settings_handler.h
new file mode 100644
index 00000000000..814d2d4e081
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/mac_system_settings_handler.h
@@ -0,0 +1,32 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_MAC_SYSTEM_SETTINGS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_MAC_SYSTEM_SETTINGS_HANDLER_H_
+
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace settings {
+
+class MacSystemSettingsHandler : public SettingsPageUIHandler {
+ public:
+ MacSystemSettingsHandler();
+
+ MacSystemSettingsHandler(const MacSystemSettingsHandler&) = delete;
+ MacSystemSettingsHandler& operator=(const MacSystemSettingsHandler&) = delete;
+
+ ~MacSystemSettingsHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ private:
+ void HandleOpenTrackpadGesturesSettings(const base::Value::List& args);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_MAC_SYSTEM_SETTINGS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/people_handler.cc b/chromium/chrome/browser/ui/webui/settings/people_handler.cc
index 77ebeea4178..a134e692f13 100644
--- a/chromium/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/people_handler.cc
@@ -34,14 +34,13 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/signin_view_controller.h"
+#include "chrome/browser/ui/signin/signin_view_controller.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
-#include "components/autofill/core/common/autofill_prefs.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/signin_error_controller.h"
#include "components/signin/public/base/consent_level.h"
@@ -100,15 +99,13 @@ struct SyncConfigInfo {
bool sync_everything;
syncer::UserSelectableTypeSet selected_types;
- bool payments_integration_enabled;
};
bool IsSyncSubpage(const GURL& current_url) {
return current_url == chrome::GetSettingsUrl(chrome::kSyncSetupSubPage);
}
-SyncConfigInfo::SyncConfigInfo()
- : sync_everything(false), payments_integration_enabled(false) {}
+SyncConfigInfo::SyncConfigInfo() : sync_everything(false) {}
SyncConfigInfo::~SyncConfigInfo() {}
@@ -127,15 +124,6 @@ bool GetConfiguration(const std::string& json, SyncConfigInfo* config) {
}
config->sync_everything = *sync_everything;
- absl::optional<bool> payments_integration_enabled =
- root.FindBool("paymentsIntegrationEnabled");
- if (!payments_integration_enabled.has_value()) {
- DLOG(ERROR) << "GetConfiguration() not passed a paymentsIntegrationEnabled "
- << "value";
- return false;
- }
- config->payments_integration_enabled = *payments_integration_enabled;
-
for (syncer::UserSelectableType type : syncer::UserSelectableTypeSet::All()) {
std::string key_name =
syncer::GetUserSelectableTypeName(type) + std::string("Synced");
@@ -408,9 +396,6 @@ void PeopleHandler::HandleSetDatatypes(const base::Value::List& args) {
const base::Value* callback_id = nullptr;
ParseConfigurationArguments(args, &configuration, &callback_id);
- autofill::prefs::SetPaymentsIntegrationEnabled(
- profile_->GetPrefs(), configuration.payments_integration_enabled);
-
// Start configuring the SyncService using the configuration passed to us from
// the JS layer.
syncer::SyncService* service = GetSyncService();
@@ -985,7 +970,6 @@ void PeopleHandler::PushSyncPrefs() {
// syncAllDataTypes: true if the user wants to sync everything
// <data_type>Registered: true if the associated data type is supported
// <data_type>Synced: true if the user wants to sync that specific data type
- // paymentsIntegrationEnabled: true if the user wants Payments integration
// customPassphraseAllowed: true if sync allows setting a custom passphrase
// to encrypt data.
// encryptAllData: true if user wants to encrypt all data (not just
@@ -1010,10 +994,10 @@ void PeopleHandler::PushSyncPrefs() {
const std::string type_name = syncer::GetUserSelectableTypeName(type);
args.Set(type_name + "Registered", registered_types.Has(type));
args.Set(type_name + "Synced", selected_types.Has(type));
+ args.Set(type_name + "Managed",
+ sync_user_settings->IsTypeManagedByPolicy(type));
}
args.Set("syncAllDataTypes", sync_user_settings->IsSyncEverythingEnabled());
- args.Set("paymentsIntegrationEnabled",
- autofill::prefs::IsPaymentsIntegrationEnabled(profile_->GetPrefs()));
args.Set("encryptAllData", sync_user_settings->IsEncryptEverythingEnabled());
args.Set("customPassphraseAllowed",
sync_user_settings->IsCustomPassphraseAllowed());
@@ -1081,7 +1065,7 @@ void PeopleHandler::MarkFirstSetupComplete() {
}
unified_consent::metrics::RecordSyncSetupDataTypesHistrogam(
- service->GetUserSettings(), profile_->GetPrefs());
+ service->GetUserSettings());
// We're done configuring, so notify SyncService that it is OK to start
// syncing.
diff --git a/chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index 11918015874..df1d9a71ad2 100644
--- a/chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -102,6 +102,8 @@ std::string GetConfiguration(SyncAllDataConfig sync_all,
types.Has(syncer::UserSelectableType::kExtensions));
result.Set("passwordsSynced",
types.Has(syncer::UserSelectableType::kPasswords));
+ result.Set("paymentsSynced",
+ types.Has(syncer::UserSelectableType::kPayments));
result.Set("preferencesSynced",
types.Has(syncer::UserSelectableType::kPreferences));
result.Set("readingListSynced",
@@ -112,7 +114,6 @@ std::string GetConfiguration(SyncAllDataConfig sync_all,
result.Set("themesSynced", types.Has(syncer::UserSelectableType::kThemes));
result.Set("typedUrlsSynced",
types.Has(syncer::UserSelectableType::kHistory));
- result.Set("paymentsIntegrationEnabled", false);
// Reading list doesn't really have a UI and is supported on ios only.
result.Set("readingListSynced",
@@ -884,12 +885,12 @@ TEST_F(PeopleHandlerTest, ShowSetupSyncEverything) {
ExpectHasBoolKey(dictionary, "bookmarksRegistered", true);
ExpectHasBoolKey(dictionary, "extensionsRegistered", true);
ExpectHasBoolKey(dictionary, "passwordsRegistered", true);
+ ExpectHasBoolKey(dictionary, "paymentsRegistered", true);
ExpectHasBoolKey(dictionary, "preferencesRegistered", true);
ExpectHasBoolKey(dictionary, "readingListRegistered", true);
ExpectHasBoolKey(dictionary, "tabsRegistered", true);
ExpectHasBoolKey(dictionary, "themesRegistered", true);
ExpectHasBoolKey(dictionary, "typedUrlsRegistered", true);
- ExpectHasBoolKey(dictionary, "paymentsIntegrationEnabled", true);
ExpectHasBoolKey(dictionary, "passphraseRequired", false);
ExpectHasBoolKey(dictionary, "encryptAllData", false);
CheckConfigDataTypeArguments(dictionary, SYNC_ALL_DATA, GetAllTypes());
@@ -1283,8 +1284,7 @@ TEST(PeopleHandlerMainProfile, Signout) {
std::unique_ptr<TestingProfile> profile =
IdentityTestEnvironmentProfileAdaptor::
- CreateProfileForIdentityTestEnvironment(
- builder, signin::AccountConsistencyMethod::kMirror);
+ CreateProfileForIdentityTestEnvironment(builder);
auto identity_test_env_adaptor =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile.get());
@@ -1318,8 +1318,7 @@ TEST(PeopleHandlerMainProfile, DeleteProfileCrashes) {
std::unique_ptr<TestingProfile> profile =
IdentityTestEnvironmentProfileAdaptor::
- CreateProfileForIdentityTestEnvironment(
- builder, signin::AccountConsistencyMethod::kMirror);
+ CreateProfileForIdentityTestEnvironment(builder);
PeopleHandler handler(profile.get());
base::Value::List args;
@@ -1336,8 +1335,7 @@ TEST(PeopleHandlerSecondaryProfile, SignoutWhenSyncing) {
std::unique_ptr<TestingProfile> profile =
IdentityTestEnvironmentProfileAdaptor::
- CreateProfileForIdentityTestEnvironment(
- builder, signin::AccountConsistencyMethod::kMirror);
+ CreateProfileForIdentityTestEnvironment(builder);
auto identity_test_env_adaptor =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile.get());
@@ -1367,8 +1365,7 @@ TEST(PeopleHandlerMainProfile, GetStoredAccountsList) {
std::unique_ptr<TestingProfile> profile =
IdentityTestEnvironmentProfileAdaptor::
- CreateProfileForIdentityTestEnvironment(
- builder, signin::AccountConsistencyMethod::kMirror);
+ CreateProfileForIdentityTestEnvironment(builder);
auto identity_test_env_adaptor =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile.get());
@@ -1399,8 +1396,7 @@ TEST(PeopleHandlerSecondaryProfile, GetStoredAccountsList) {
std::unique_ptr<TestingProfile> profile =
IdentityTestEnvironmentProfileAdaptor::
- CreateProfileForIdentityTestEnvironment(
- builder, signin::AccountConsistencyMethod::kMirror);
+ CreateProfileForIdentityTestEnvironment(builder);
auto identity_test_env_adaptor =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile.get());
@@ -1509,8 +1505,7 @@ class PeopleHandlerSignoutTest : public BrowserWithTestWindowTest {
private:
TestingProfile::TestingFactories GetTestingFactories() override {
return IdentityTestEnvironmentProfileAdaptor::
- GetIdentityTestEnvironmentFactories(
- signin::AccountConsistencyMethod::kDice);
+ GetIdentityTestEnvironmentFactories();
}
void TearDown() override {
diff --git a/chromium/chrome/browser/ui/webui/settings/performance_handler.cc b/chromium/chrome/browser/ui/webui/settings/performance_handler.cc
index f3fed823b21..7c0a9d25e4a 100644
--- a/chromium/chrome/browser/ui/webui/settings/performance_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/performance_handler.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -40,6 +40,10 @@ void PerformanceHandler::RegisterMessages() {
&PerformanceHandler::HandleOpenHighEfficiencyFeedbackDialog,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "openSpeedFeedbackDialog",
+ base::BindRepeating(&PerformanceHandler::HandleOpenSpeedFeedbackDialog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"validateTabDiscardExceptionRule",
base::BindRepeating(
&PerformanceHandler::HandleValidateTabDiscardExceptionRule,
@@ -52,8 +56,7 @@ void PerformanceHandler::RegisterMessages() {
void PerformanceHandler::OnJavascriptAllowed() {
performance_handler_observer_.Observe(
- performance_manager::user_tuning::UserPerformanceTuningManager::
- GetInstance());
+ performance_manager::user_tuning::BatterySaverModeManager::GetInstance());
}
void PerformanceHandler::OnJavascriptDisallowed() {
@@ -115,7 +118,7 @@ void PerformanceHandler::HandleGetDeviceHasBattery(
AllowJavascript();
ResolveJavascriptCallback(
callback_id, base::Value(performance_manager::user_tuning::
- UserPerformanceTuningManager::GetInstance()
+ BatterySaverModeManager::GetInstance()
->DeviceHasBattery()));
}
@@ -129,6 +132,11 @@ void PerformanceHandler::HandleOpenHighEfficiencyFeedbackDialog(
HandleOpenFeedbackDialog("performance_tabs");
}
+void PerformanceHandler::HandleOpenSpeedFeedbackDialog(
+ const base::Value::List& args) {
+ HandleOpenFeedbackDialog("performance_speed");
+}
+
void PerformanceHandler::HandleOpenFeedbackDialog(
const std::string category_tag) {
Browser* browser =
diff --git a/chromium/chrome/browser/ui/webui/settings/performance_handler.h b/chromium/chrome/browser/ui/webui/settings/performance_handler.h
index 0c7fcc34329..1446a5713cc 100644
--- a/chromium/chrome/browser/ui/webui/settings/performance_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/performance_handler.h
@@ -1,18 +1,18 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_PERFORMANCE_HANDLER_H_
#define CHROME_BROWSER_UI_WEBUI_SETTINGS_PERFORMANCE_HANDLER_H_
-#include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
+#include "chrome/browser/performance_manager/public/user_tuning/battery_saver_mode_manager.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
namespace settings {
class PerformanceHandler : public SettingsPageUIHandler,
public performance_manager::user_tuning::
- UserPerformanceTuningManager::Observer {
+ BatterySaverModeManager::Observer {
public:
PerformanceHandler();
@@ -31,11 +31,11 @@ class PerformanceHandler : public SettingsPageUIHandler,
FRIEND_TEST_ALL_PREFIXES(PerformanceHandlerTest, GetCurrentOpenSites);
base::ScopedObservation<
- performance_manager::user_tuning::UserPerformanceTuningManager,
- performance_manager::user_tuning::UserPerformanceTuningManager::Observer>
+ performance_manager::user_tuning::BatterySaverModeManager,
+ performance_manager::user_tuning::BatterySaverModeManager::Observer>
performance_handler_observer_{this};
- // UserPerformanceTuningManager::Observer:
+ // BatterySaverModeManager::Observer:
void OnDeviceHasBatteryChanged(bool device_has_battery) override;
/**
@@ -52,6 +52,7 @@ class PerformanceHandler : public SettingsPageUIHandler,
void HandleGetDeviceHasBattery(const base::Value::List& args);
void HandleOpenBatterySaverFeedbackDialog(const base::Value::List& args);
void HandleOpenHighEfficiencyFeedbackDialog(const base::Value::List& args);
+ void HandleOpenSpeedFeedbackDialog(const base::Value::List& args);
void HandleOpenFeedbackDialog(const std::string category_tag);
void HandleValidateTabDiscardExceptionRule(const base::Value::List& args);
};
diff --git a/chromium/chrome/browser/ui/webui/settings/performance_settings_interactive_uitest.cc b/chromium/chrome/browser/ui/webui/settings/performance_settings_interactive_uitest.cc
index 65db12188ea..eaa03dd02b6 100644
--- a/chromium/chrome/browser/ui/webui/settings/performance_settings_interactive_uitest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/performance_settings_interactive_uitest.cc
@@ -3,12 +3,15 @@
// found in the LICENSE file.
#include "base/power_monitor/battery_state_sampler.h"
+#include "base/strings/string_util.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/power_monitor_test_utils.h"
#include "base/test/scoped_feature_list.h"
#include "build/branding_buildflags.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/performance_manager/public/user_tuning/battery_saver_mode_manager.h"
+#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/webui/feedback/feedback_dialog.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/ui_test_utils.h"
@@ -19,6 +22,11 @@
#include "content/public/test/browser_test.h"
#include "url/gurl.h"
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/constants/ash_features.h"
+#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
using performance_manager::user_tuning::prefs::BatterySaverModeState;
using performance_manager::user_tuning::prefs::HighEfficiencyModeState;
@@ -26,6 +34,7 @@ namespace {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPerformanceSettingsPage);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kButtonWasClicked);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kElementRenders);
+DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kIronCollapseContentShows);
constexpr char kCheckJsElementIsChecked[] = "(el) => { return el.checked; }";
constexpr char kCheckJsElementIsNotChecked[] =
@@ -39,11 +48,24 @@ const WebContentsInteractionTestUtil::DeepQuery kHighEfficiencyToggleQuery = {
"settings-toggle-button",
"cr-toggle#control"};
+const WebContentsInteractionTestUtil::DeepQuery kDiscardOnUsageQuery = {
+ "settings-ui", "settings-main", "settings-basic-page",
+ "settings-performance-page", "controlled-radio-button"};
+
+const WebContentsInteractionTestUtil::DeepQuery kDiscardOnTimerQuery = {
+ "settings-ui", "settings-main", "settings-basic-page",
+ "settings-performance-page",
+ "controlled-radio-button#enabledOnTimerButton"};
+
+} // namespace
+
class PerformanceSettingsInteractiveTest : public InteractiveBrowserTest {
public:
void SetUp() override {
- ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
SetUpFakeBatterySampler();
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ scoped_feature_list_.InitAndDisableFeature(ash::features::kBatterySaver);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
InteractiveBrowserTest::SetUp();
}
@@ -52,6 +74,7 @@ class PerformanceSettingsInteractiveTest : public InteractiveBrowserTest {
performance_manager::user_tuning::UserPerformanceTuningManager::
GetInstance()
->SetHighEfficiencyModeEnabled(true);
+ ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
embedded_test_server()->StartAcceptingConnections();
}
@@ -144,12 +167,26 @@ class PerformanceSettingsInteractiveTest : public InteractiveBrowserTest {
return WaitForStateChange(contents_id, element_renders);
}
+ auto WaitForIronListCollapseStateChange(ui::ElementIdentifier webcontents_id,
+ DeepQuery query) {
+ StateChange iron_collapse_finish_animating;
+ iron_collapse_finish_animating.event = kIronCollapseContentShows;
+ iron_collapse_finish_animating.where = query;
+ iron_collapse_finish_animating.type =
+ StateChange::Type::kExistsAndConditionTrue;
+ iron_collapse_finish_animating.test_function =
+ "(el) => { return !el.transitioning; }";
+
+ return WaitForStateChange(webcontents_id, iron_collapse_finish_animating);
+ }
+
private:
raw_ptr<base::test::TestSamplingEventSource, DanglingUntriaged>
sampling_source_;
raw_ptr<base::test::TestBatteryLevelProvider, DanglingUntriaged>
battery_level_provider_;
std::unique_ptr<base::BatteryStateSampler> battery_state_sampler_;
+ base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(PerformanceSettingsInteractiveTest,
@@ -246,8 +283,6 @@ IN_PROC_BROWSER_TEST_F(PerformanceSettingsInteractiveTest,
IN_PROC_BROWSER_TEST_F(PerformanceSettingsInteractiveTest,
BatterySaverMetricsShouldLogOnToggle) {
- DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kIronCollapseContentShows);
-
const DeepQuery battery_saver_toggle = {
"settings-ui", "settings-main", "settings-basic-page",
"settings-battery-page", "settings-toggle-button", "cr-toggle#control"};
@@ -265,14 +300,6 @@ IN_PROC_BROWSER_TEST_F(PerformanceSettingsInteractiveTest,
"settings-battery-page",
"controlled-radio-button#enabledOnBatteryButton"};
- StateChange iron_collapse_finish_animating;
- iron_collapse_finish_animating.event = kIronCollapseContentShows;
- iron_collapse_finish_animating.where = iron_collapse;
- iron_collapse_finish_animating.type =
- StateChange::Type::kExistsAndConditionTrue;
- iron_collapse_finish_animating.test_function =
- "(el) => { return !el.transitioning; }";
-
base::HistogramTester histogram_tester;
RunTestSequence(
InstrumentTab(kPerformanceSettingsPage),
@@ -298,8 +325,8 @@ IN_PROC_BROWSER_TEST_F(PerformanceSettingsInteractiveTest,
// Wait for the iron-collapse animation to finish so that the battery
// saver radio buttons will show on screen
- WaitForStateChange(kPerformanceSettingsPage,
- iron_collapse_finish_animating),
+ WaitForIronListCollapseStateChange(kPerformanceSettingsPage,
+ iron_collapse),
// Change Battery Saver Setting to turn on when unplugged
ClickElement(kPerformanceSettingsPage, turn_on_when_unplugged_button),
@@ -348,4 +375,251 @@ IN_PROC_BROWSER_TEST_F(PerformanceSettingsInteractiveTest,
}
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
-} // namespace
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+class BatterySettingsInteractiveTest : public InteractiveAshTest {
+ public:
+ BatterySettingsInteractiveTest()
+ : scoped_feature_list_(ash::features::kBatterySaver) {}
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(
+ performance_manager::user_tuning::BatterySaverModeManager::
+ kForceDeviceHasBatterySwitch);
+ }
+
+ auto WaitForElementToRender(const ui::ElementIdentifier& contents_id,
+ const DeepQuery& element) {
+ StateChange element_renders;
+ element_renders.event = kElementRenders;
+ element_renders.where = element;
+ element_renders.type = StateChange::Type::kExistsAndConditionTrue;
+ element_renders.test_function =
+ "(el) => { return el !== null && el.clientWidth > 0 && el.clientHeight "
+ "> 0; }";
+
+ return WaitForStateChange(contents_id, element_renders);
+ }
+
+ auto ClickElement(const ui::ElementIdentifier& contents_id,
+ const DeepQuery& element) {
+ return Steps(WaitForElementToRender(contents_id, element),
+ MoveMouseTo(contents_id, element), ClickMouse());
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(BatterySettingsInteractiveTest,
+ BatterySaverSettingsLinksToOSSettings) {
+ SetupContextWidget();
+ InstallSystemApps();
+
+ DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOsSettingsElementId);
+
+ const DeepQuery battery_saver_link_row = {
+ "settings-ui", "settings-main", "settings-basic-page",
+ "settings-battery-page", "cr-link-row#batterySaverOSSettingsLinkRow"};
+
+ CreateBrowserWindow(GURL(chrome::kChromeUIPerformanceSettingsURL));
+ Browser* const browser = chrome::FindLastActive();
+ ASSERT_NE(browser, nullptr);
+
+ RunTestSequence(
+ InContext(browser->window()->GetElementContext(),
+ InstrumentTab(kPerformanceSettingsPage)),
+ WaitForElementToRender(kPerformanceSettingsPage, battery_saver_link_row),
+ InstrumentNextTab(kOsSettingsElementId, AnyBrowser()),
+ ClickElement(kPerformanceSettingsPage, battery_saver_link_row),
+ WaitForShow(kOsSettingsElementId),
+ WaitForWebContentsReady(kOsSettingsElementId,
+ GURL("chrome://os-settings/power")));
+}
+
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+class PerformanceSettingsMultiStateModeInteractiveTest
+ : public PerformanceSettingsInteractiveTest {
+ public:
+ void SetUp() override {
+ scoped_feature_list_.InitAndEnableFeature(
+ performance_manager::features::kHighEfficiencyMultistateMode);
+
+ InteractiveBrowserTest::SetUp();
+ }
+
+ auto WaitForDisabledStateChange(const ui::ElementIdentifier& contents_id,
+ DeepQuery element,
+ bool is_disabled) {
+ StateChange toggle_selection_change;
+ toggle_selection_change.event = kButtonWasClicked;
+ toggle_selection_change.where = element;
+ toggle_selection_change.type = StateChange::Type::kExistsAndConditionTrue;
+ toggle_selection_change.test_function =
+ is_disabled ? "(el) => el.disabled === true"
+ : "(el) => el.disabled === false";
+
+ return WaitForStateChange(contents_id, toggle_selection_change);
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PerformanceSettingsMultiStateModeInteractiveTest,
+ HighEfficiencyPrefChanged) {
+ RunTestSequence(
+ InstrumentTab(kPerformanceSettingsPage),
+ NavigateWebContents(kPerformanceSettingsPage,
+ GURL(chrome::kChromeUIPerformanceSettingsURL)),
+ WaitForElementToRender(kPerformanceSettingsPage,
+ kHighEfficiencyToggleQuery),
+ CheckJsResultAt(kPerformanceSettingsPage, kHighEfficiencyToggleQuery,
+ kCheckJsElementIsChecked),
+
+ // Enable high efficiency mode to discard tabs based on a timer
+ ClickElement(kPerformanceSettingsPage, kDiscardOnTimerQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage, kDiscardOnTimerQuery,
+ true),
+ CheckHighEfficiencyModePrefState(
+ HighEfficiencyModeState::kEnabledOnTimer),
+
+ // Turn off high efficiency mode
+ ClickElement(kPerformanceSettingsPage, kHighEfficiencyToggleQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage,
+ kHighEfficiencyToggleQuery, false),
+ CheckHighEfficiencyModePrefState(HighEfficiencyModeState::kDisabled),
+
+ // Turn high efficiency mode back on
+ ClickElement(kPerformanceSettingsPage, kHighEfficiencyToggleQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage,
+ kHighEfficiencyToggleQuery, true),
+ CheckHighEfficiencyModePrefState(HighEfficiencyModeState::kEnabled));
+}
+
+IN_PROC_BROWSER_TEST_F(PerformanceSettingsMultiStateModeInteractiveTest,
+ HighEfficiencyMetricsShouldLogOnToggle) {
+ base::HistogramTester histogram_tester;
+
+ const DeepQuery iron_collapse = {
+ "settings-ui", "settings-main", "settings-basic-page",
+ "settings-performance-page", "iron-collapse#radioGroupCollapse"};
+
+ RunTestSequence(
+ InstrumentTab(kPerformanceSettingsPage),
+ NavigateWebContents(kPerformanceSettingsPage,
+ GURL(chrome::kChromeUIPerformanceSettingsURL)),
+ WaitForElementToRender(kPerformanceSettingsPage,
+ kHighEfficiencyToggleQuery),
+ CheckJsResultAt(kPerformanceSettingsPage, kHighEfficiencyToggleQuery,
+ kCheckJsElementIsChecked),
+
+ // Turn Off High Efficiency Mode
+ ClickElement(kPerformanceSettingsPage, kHighEfficiencyToggleQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage,
+ kHighEfficiencyToggleQuery, false),
+ CheckHighEfficiencyModeLogged(HighEfficiencyModeState::kDisabled, 1,
+ histogram_tester),
+
+ // Turn High Efficiency Mode back on
+ ClickElement(kPerformanceSettingsPage, kHighEfficiencyToggleQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage,
+ kHighEfficiencyToggleQuery, true),
+ CheckHighEfficiencyModeLogged(HighEfficiencyModeState::kEnabled, 1,
+ histogram_tester),
+
+ // Wait for the iron-collapse animation to finish so that the performance
+ // radio buttons will show on screen
+ WaitForIronListCollapseStateChange(kPerformanceSettingsPage,
+ iron_collapse),
+
+ // Change high efficiency setting to discard tabs based on timer
+ ClickElement(kPerformanceSettingsPage, kDiscardOnTimerQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage, kDiscardOnTimerQuery,
+ true),
+ CheckHighEfficiencyModeLogged(HighEfficiencyModeState::kEnabledOnTimer, 1,
+ histogram_tester),
+
+ // Change high efficiency setting to discard tabs based on usage
+ ClickElement(kPerformanceSettingsPage, kDiscardOnUsageQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage, kDiscardOnUsageQuery,
+ true),
+ CheckHighEfficiencyModeLogged(HighEfficiencyModeState::kEnabled, 2,
+ histogram_tester));
+}
+
+// Checks that the selected discard timer value is preserved as the high
+// efficiency mode gets toggled
+IN_PROC_BROWSER_TEST_F(PerformanceSettingsMultiStateModeInteractiveTest,
+ DiscardTimerStateIsPreserved) {
+ const DeepQuery discard_time_menu = {
+ "settings-ui", "settings-main", "settings-basic-page",
+ "settings-performance-page",
+ "settings-dropdown-menu#discardTimeDropdown"};
+
+ const DeepQuery discard_time_drop_down = {
+ "settings-ui",
+ "settings-main",
+ "settings-basic-page",
+ "settings-performance-page",
+ "settings-dropdown-menu#discardTimeDropdown",
+ "select#dropdownMenu"};
+
+ const DeepQuery iron_collapse = {
+ "settings-ui", "settings-main", "settings-basic-page",
+ "settings-performance-page", "iron-collapse#radioGroupCollapse"};
+
+ const std::string discard_timer_value = "5";
+
+ RunTestSequence(
+ InstrumentTab(kPerformanceSettingsPage),
+ NavigateWebContents(kPerformanceSettingsPage,
+ GURL(chrome::kChromeUIPerformanceSettingsURL)),
+ WaitForElementToRender(kPerformanceSettingsPage,
+ kHighEfficiencyToggleQuery),
+ CheckJsResultAt(kPerformanceSettingsPage, kHighEfficiencyToggleQuery,
+ kCheckJsElementIsChecked),
+
+ // Select discard on timer option
+ ClickElement(kPerformanceSettingsPage, kDiscardOnTimerQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage, kDiscardOnTimerQuery,
+ true),
+ WaitForDisabledStateChange(kPerformanceSettingsPage,
+ discard_time_drop_down, false),
+
+ // Change the selected timer value
+ ExecuteJsAt(
+ kPerformanceSettingsPage, discard_time_drop_down,
+ base::ReplaceStringPlaceholders("(el) => { el.value = $1}",
+ {discard_timer_value}, nullptr)),
+
+ // Turn off high efficiency mode
+ ClickElement(kPerformanceSettingsPage, kHighEfficiencyToggleQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage,
+ kHighEfficiencyToggleQuery, false),
+ // Turn high efficiency mode back on
+ ClickElement(kPerformanceSettingsPage, kHighEfficiencyToggleQuery),
+ WaitForIronListCollapseStateChange(kPerformanceSettingsPage,
+ iron_collapse),
+ CheckJsResultAt(kPerformanceSettingsPage, discard_time_drop_down,
+ "(el) => el.value", discard_timer_value),
+ WaitForDisabledStateChange(kPerformanceSettingsPage, discard_time_menu,
+ true),
+ WaitForButtonStateChange(kPerformanceSettingsPage, kDiscardOnUsageQuery,
+ true),
+
+ // Change discard settings to discard tabs based on timer
+ ClickElement(kPerformanceSettingsPage, kDiscardOnTimerQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage, kDiscardOnTimerQuery,
+ true),
+ CheckJsResultAt(kPerformanceSettingsPage, discard_time_drop_down,
+ "(el) => el.value", discard_timer_value),
+
+ // Change discard settings to discard tabs based on usage
+ ClickElement(kPerformanceSettingsPage, kDiscardOnUsageQuery),
+ WaitForButtonStateChange(kPerformanceSettingsPage, kDiscardOnUsageQuery,
+ true),
+ CheckJsResultAt(kPerformanceSettingsPage, discard_time_drop_down,
+ "(el) => el.value", discard_timer_value));
+}
diff --git a/chromium/chrome/browser/ui/webui/settings/privacy_sandbox_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/privacy_sandbox_handler_unittest.cc
index 42f836a240a..b4fc217d451 100644
--- a/chromium/chrome/browser/ui/webui/settings/privacy_sandbox_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/privacy_sandbox_handler_unittest.cc
@@ -5,7 +5,7 @@
#include "chrome/browser/ui/webui/settings/privacy_sandbox_handler.h"
#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/privacy_sandbox/privacy_sandbox_service.h"
+#include "chrome/browser/privacy_sandbox/mock_privacy_sandbox_service.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service_factory.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
#include "chrome/test/base/testing_profile.h"
@@ -28,40 +28,6 @@ constexpr char kCallbackId2[] = "test-callback-id-2";
constexpr int kTestTaxonomyVersion = 1;
-class MockPrivacySandboxService : public PrivacySandboxService {
- public:
- MOCK_METHOD(void,
- GetFledgeJoiningEtldPlusOneForDisplay,
- (base::OnceCallback<void(std::vector<std::string>)>),
- (override));
- MOCK_METHOD(std::vector<std::string>,
- GetBlockedFledgeJoiningTopFramesForDisplay,
- (),
- (const override));
- MOCK_METHOD(void,
- SetFledgeJoiningAllowed,
- ((const std::string&), bool),
- (const override));
- MOCK_METHOD(std::vector<privacy_sandbox::CanonicalTopic>,
- GetCurrentTopTopics,
- (),
- (const override));
- MOCK_METHOD(std::vector<privacy_sandbox::CanonicalTopic>,
- GetBlockedTopics,
- (),
- (const override));
- MOCK_METHOD(void,
- SetTopicAllowed,
- (privacy_sandbox::CanonicalTopic, bool),
- (override));
- MOCK_METHOD(void, TopicsToggleChanged, (bool), (const override));
-};
-
-std::unique_ptr<KeyedService> BuildMockPrivacySandboxService(
- content::BrowserContext* context) {
- return std::make_unique<::testing::StrictMock<MockPrivacySandboxService>>();
-}
-
void ValidateFledgeInfo(content::TestWebUI* web_ui,
std::string expected_callback_id,
std::vector<std::string> expected_joining_sites,
diff --git a/chromium/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
index f38ecf0cc1b..77d7a6fa603 100644
--- a/chromium/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
@@ -32,7 +32,7 @@
#include "chrome/common/extensions/api/passwords_private.h"
#include "chrome/test/base/testing_profile.h"
#include "components/crx_file/id_util.h"
-#include "components/password_manager/core/browser/affiliation/mock_affiliation_service.h"
+#include "components/password_manager/core/browser/affiliation/fake_affiliation_service.h"
#include "components/password_manager/core/browser/bulk_leak_check_service.h"
#include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
#include "components/password_manager/core/browser/password_form.h"
@@ -139,14 +139,9 @@ class TestPasswordsDelegate : public extensions::TestPasswordsPrivateDelegate {
TestPasswordsDelegate() {
store_->Init(/*prefs=*/nullptr, /*affiliated_match_helper=*/nullptr);
presenter_.Init();
- base::RunLoop().RunUntilIdle();
}
- void TearDown() {
- store_->ShutdownOnUIThread();
- // Needs to be invoked in the test's TearDown() - before the destructor.
- base::RunLoop().RunUntilIdle();
- }
+ void TearDown() { store_->ShutdownOnUIThread(); }
void SetBulkLeakCheckService(
password_manager::BulkLeakCheckService* leak_service) {
@@ -182,7 +177,7 @@ class TestPasswordsDelegate : public extensions::TestPasswordsPrivateDelegate {
total_ = total;
}
- void InvokeOnCompromisedCredentialsChanged() {
+ void StoreCompromisedPassword() {
// Compromised credentials can be added only after password form to which
// they corresponds exists.
password_manager::PasswordForm form;
@@ -193,13 +188,12 @@ class TestPasswordsDelegate : public extensions::TestPasswordsPrivateDelegate {
"test" + base::NumberToString(test_credential_counter_++));
form.password_value = u"password";
form.username_element = u"username_element";
- store_->AddLogin(form);
form.password_issues = {
{password_manager::InsecureType::kLeaked,
password_manager::InsecurityMetadata(
base::Time(), password_manager::IsMuted(false),
password_manager::TriggerBackendNotification(false))}};
- base::RunLoop().RunUntilIdle();
+ store_->AddLogin(form);
}
std::vector<extensions::api::passwords_private::PasswordUiEntry>
@@ -262,7 +256,7 @@ class TestPasswordsDelegate : public extensions::TestPasswordsPrivateDelegate {
extensions::api::passwords_private::PASSWORD_CHECK_STATE_IDLE;
scoped_refptr<password_manager::TestPasswordStore> store_ =
base::MakeRefCounted<password_manager::TestPasswordStore>();
- password_manager::MockAffiliationService affiliation_service_;
+ password_manager::FakeAffiliationService affiliation_service_;
password_manager::SavedPasswordsPresenter presenter_{
&affiliation_service_, store_, /*account_store=*/nullptr};
password_manager::InsecureCredentialsManager credentials_manager_{
@@ -382,6 +376,7 @@ void SafetyCheckHandlerTest::SetUp() {
void SafetyCheckHandlerTest::TearDown() {
test_passwords_delegate_->TearDown();
+ browser_task_environment_.RunUntilIdle();
}
const base::Value::Dict*
@@ -915,11 +910,12 @@ TEST_F(SafetyCheckHandlerTest, CheckPasswords_StaleSafeThenCompromised) {
// An InsecureCredentialsManager callback fires once the compromised passwords
// get written to disk.
test_passwords_delegate_->SetNumLeakedCredentials(kCompromised);
- test_passwords_delegate_->InvokeOnCompromisedCredentialsChanged();
+ test_passwords_delegate_->StoreCompromisedPassword();
+ browser_task_environment_.RunUntilIdle();
const base::Value::Dict* event2 = GetSafetyCheckStatusChangedWithDataIfExists(
kPasswords,
static_cast<int>(SafetyCheckHandler::PasswordsStatus::kCompromisedExist));
- EXPECT_TRUE(event2);
+ ASSERT_TRUE(event2);
VerifyDisplayString(
event2, base::NumberToString(kCompromised) + " compromised passwords");
}
@@ -937,7 +933,8 @@ TEST_F(SafetyCheckHandlerTest, CheckPasswords_SafeStateThenMoreEvents) {
// Previous safe state got loaded.
test_passwords_delegate_->SetNumLeakedCredentials(0);
- test_passwords_delegate_->InvokeOnCompromisedCredentialsChanged();
+ test_passwords_delegate_->StoreCompromisedPassword();
+ browser_task_environment_.RunUntilIdle();
// The event should get ignored, since the state is still running.
const base::Value::Dict* event = GetSafetyCheckStatusChangedWithDataIfExists(
kPasswords, static_cast<int>(SafetyCheckHandler::PasswordsStatus::kSafe));
@@ -956,7 +953,8 @@ TEST_F(SafetyCheckHandlerTest, CheckPasswords_SafeStateThenMoreEvents) {
// After some time, some compromises were discovered (unrelated to SC).
constexpr int kCompromised = 7;
test_passwords_delegate_->SetNumLeakedCredentials(kCompromised);
- test_passwords_delegate_->InvokeOnCompromisedCredentialsChanged();
+ test_passwords_delegate_->StoreCompromisedPassword();
+ browser_task_environment_.RunUntilIdle();
// The new event should get ignored, since the safe state was final.
const base::Value::Dict* event2 = GetSafetyCheckStatusChangedWithDataIfExists(
kPasswords,
@@ -1293,7 +1291,8 @@ TEST_F(SafetyCheckHandlerTest, CheckPasswords_Error_FutureEventsIgnored) {
// An InsecureCredentialsManager callback fires once the compromised passwords
// get written to disk.
test_passwords_delegate_->SetNumLeakedCredentials(kCompromised);
- test_passwords_delegate_->InvokeOnCompromisedCredentialsChanged();
+ test_passwords_delegate_->StoreCompromisedPassword();
+ browser_task_environment_.RunUntilIdle();
const base::Value::Dict* event2 = GetSafetyCheckStatusChangedWithDataIfExists(
kPasswords,
static_cast<int>(SafetyCheckHandler::PasswordsStatus::kCompromisedExist));
diff --git a/chromium/chrome/browser/ui/webui/settings/safety_hub_handler.cc b/chromium/chrome/browser/ui/webui/settings/safety_hub_handler.cc
index b29fba3d6b1..475e660cea6 100644
--- a/chromium/chrome/browser/ui/webui/settings/safety_hub_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/safety_hub_handler.cc
@@ -14,9 +14,13 @@
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/permissions/notification_permission_review_service_factory.h"
-#include "chrome/browser/permissions/unused_site_permissions_service_factory.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/safety_hub/notification_permission_review_service.h"
+#include "chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h"
+#include "chrome/browser/ui/safety_hub/password_status_check_service.h"
+#include "chrome/browser/ui/safety_hub/password_status_check_service_factory.h"
+#include "chrome/browser/ui/safety_hub/unused_site_permissions_service.h"
+#include "chrome/browser/ui/safety_hub/unused_site_permissions_service_factory.h"
#include "chrome/browser/ui/webui/settings/site_settings_helper.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h"
@@ -24,10 +28,12 @@
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/features.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/permissions/constants.h"
-#include "components/permissions/unused_site_permissions_service.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/time_format.h"
#include "url/gurl.h"
namespace {
@@ -37,6 +43,12 @@ namespace {
constexpr char kExpirationKey[] = "expiration";
// Key of the lifetime in the |UnusedSitePermissions| object.
constexpr char kLifetimeKey[] = "lifetime";
+// Key of the header in |CardInfo| object.
+constexpr char kHeader[] = "header";
+// Key of the subheader in |CardInfo| object.
+constexpr char kSubheader[] = "subheader";
+// Key of the state in |CardInfo| object.
+constexpr char kState[] = "state";
// Get values from |UnusedSitePermission| object in
// safety_hub_browser_proxy.ts.
@@ -72,14 +84,11 @@ GetUnusedSitePermissionsFromDict(
base::Time expiration = base::ValueToTime(js_expiration).value();
const base::Value* js_lifetime = unused_site_permissions.Find(kLifetimeKey);
- // TODO(https://crbug.com/1455435): The use of ComputeLifetime here should be
- // temporary. Once all persisted RuleMetaData instances include lifetimes, we
- // can remove this, and just use the stored lifetime directly. We can do this
- // after all lifetime-less settings have expired. Realistically this will take
- // only one or two milestones, so this can safely be removed in M118 or M119.
+ // Users may edit the stored fields directly, so we cannot assume their
+ // presence and validity.
base::TimeDelta lifetime = content_settings::RuleMetaData::ComputeLifetime(
- /*lifetime=*/js_lifetime ? base::ValueToTimeDelta(js_lifetime).value()
- : base::TimeDelta(),
+ /*lifetime=*/
+ base::ValueToTimeDelta(js_lifetime).value_or(base::TimeDelta()),
/*expiration=*/expiration);
content_settings::ContentSettingConstraints constraints =
@@ -88,6 +97,19 @@ GetUnusedSitePermissionsFromDict(
return std::make_tuple(origin, permission_types, constraints);
}
+
+// Returns the state of Safe Browsing setting.
+SafeBrowsingState GetSafeBrowsingState(PrefService* pref_service) {
+ if (safe_browsing::IsEnhancedProtectionEnabled(*pref_service))
+ return SafeBrowsingState::kEnabledEnhanced;
+ if (safe_browsing::IsSafeBrowsingEnabled(*pref_service))
+ return SafeBrowsingState::kEnabledStandard;
+ if (safe_browsing::IsSafeBrowsingPolicyManaged(*pref_service))
+ return SafeBrowsingState::kDisabledByAdmin;
+ if (safe_browsing::IsSafeBrowsingExtensionControlled(*pref_service))
+ return SafeBrowsingState::kDisabledByExtension;
+ return SafeBrowsingState::kDisabledByUser;
+}
} // namespace
SafetyHubHandler::SafetyHubHandler(Profile* profile)
@@ -118,8 +140,9 @@ void SafetyHubHandler::HandleAllowPermissionsAgainForUnusedSite(
CHECK(args[0].is_string());
const std::string& origin_str = args[0].GetString();
- permissions::UnusedSitePermissionsService* service =
+ UnusedSitePermissionsService* service =
UnusedSitePermissionsServiceFactory::GetForProfile(profile_);
+ CHECK(service);
url::Origin origin = url::Origin::Create(GURL(origin_str));
@@ -134,8 +157,9 @@ void SafetyHubHandler::HandleUndoAllowPermissionsAgainForUnusedSite(
auto [origin, permissions, constraints] =
GetUnusedSitePermissionsFromDict(args[0].GetDict());
- permissions::UnusedSitePermissionsService* service =
+ UnusedSitePermissionsService* service =
UnusedSitePermissionsServiceFactory::GetForProfile(profile_);
+ CHECK(service);
service->UndoRegrantPermissionsForOrigin(permissions, constraints, origin);
@@ -144,8 +168,9 @@ void SafetyHubHandler::HandleUndoAllowPermissionsAgainForUnusedSite(
void SafetyHubHandler::HandleAcknowledgeRevokedUnusedSitePermissionsList(
const base::Value::List& args) {
- permissions::UnusedSitePermissionsService* service =
+ UnusedSitePermissionsService* service =
UnusedSitePermissionsServiceFactory::GetForProfile(profile_);
+ CHECK(service);
service->ClearRevokedPermissionsList();
SendUnusedSitePermissionsReviewList();
@@ -157,8 +182,9 @@ void SafetyHubHandler::HandleUndoAcknowledgeRevokedUnusedSitePermissionsList(
CHECK(args[0].is_list());
const base::Value::List& unused_site_permissions_list = args[0].GetList();
- permissions::UnusedSitePermissionsService* service =
+ UnusedSitePermissionsService* service =
UnusedSitePermissionsServiceFactory::GetForProfile(profile_);
+ CHECK(service);
for (const auto& unused_site_permissions_js : unused_site_permissions_list) {
CHECK(unused_site_permissions_js.is_dict());
@@ -182,11 +208,8 @@ base::Value::List SafetyHubHandler::PopulateUnusedSitePermissionsData() {
HostContentSettingsMap* hcsm =
HostContentSettingsMapFactory::GetForProfile(profile_);
- ContentSettingsForOneType settings;
- hcsm->GetSettingsForOneType(
- ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, &settings);
-
- for (const auto& revoked_permissions : settings) {
+ for (const auto& revoked_permissions : hcsm->GetSettingsForOneType(
+ ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS)) {
base::Value::Dict revoked_permission_value;
revoked_permission_value.Set(
site_settings::kOrigin, revoked_permissions.primary_pattern.ToString());
@@ -240,8 +263,16 @@ void SafetyHubHandler::HandleGetNotificationPermissionReviewList(
const base::Value& callback_id = args[0];
+ NotificationPermissionsReviewService* service =
+ NotificationPermissionsReviewServiceFactory::GetForProfile(profile_);
+ DCHECK(service);
+
+ if (!service) {
+ RejectJavascriptCallback(callback_id, base::Value());
+ }
+
base::Value::List result =
- site_settings::PopulateNotificationPermissionReviewData(profile_);
+ service->PopulateNotificationPermissionReviewData(profile_);
ResolveJavascriptCallback(callback_id, base::Value(std::move(result)));
}
@@ -251,9 +282,9 @@ void SafetyHubHandler::HandleIgnoreOriginsForNotificationPermissionReview(
CHECK_EQ(1U, args.size());
const base::Value::List& origins = args[0].GetList();
- auto* service =
+ NotificationPermissionsReviewService* service =
NotificationPermissionsReviewServiceFactory::GetForProfile(profile_);
- DCHECK(service);
+ CHECK(service);
for (const auto& origin : origins) {
const ContentSettingsPattern primary_pattern =
@@ -323,9 +354,9 @@ void SafetyHubHandler::HandleUndoIgnoreOriginsForNotificationPermissionReview(
const base::Value::List& args) {
CHECK_EQ(1U, args.size());
const base::Value::List& origins = args[0].GetList();
- auto* service =
+ NotificationPermissionsReviewService* service =
NotificationPermissionsReviewServiceFactory::GetForProfile(profile_);
- DCHECK(service);
+ CHECK(service);
for (const auto& origin : origins) {
const ContentSettingsPattern& primary_pattern =
@@ -336,6 +367,42 @@ void SafetyHubHandler::HandleUndoIgnoreOriginsForNotificationPermissionReview(
SendNotificationPermissionReviewList();
}
+void SafetyHubHandler::HandleGetSafeBrowsingState(
+ const base::Value::List& args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args.size());
+ const base::Value& callback_id = args[0];
+
+ SafeBrowsingState result = GetSafeBrowsingState(profile_->GetPrefs());
+
+ ResolveJavascriptCallback(callback_id, (int)result);
+}
+
+void SafetyHubHandler::HandleGetPasswordCardData(
+ const base::Value::List& args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args.size());
+ const base::Value& callback_id = args[0];
+
+ PasswordStatusCheckService* service =
+ PasswordStatusCheckServiceFactory::GetForProfile(profile_);
+ base::Time last_check_completed =
+ base::Time::FromTimeT(profile_->GetPrefs()->GetDouble(
+ password_manager::prefs::kLastTimePasswordCheckCompleted));
+
+ // TODO(crbug.com/1443466): The UI should be able to observe when password
+ // issues change.
+ base::Value::Dict result = GetPasswordCardData(
+ /*compromised_count=*/service->compromised_credential_count(),
+ /*weak_count=*/service->weak_credential_count(),
+ /*reused_count=*/service->reused_credential_count(),
+ /*last_check=*/last_check_completed);
+
+ ResolveJavascriptCallback(callback_id, base::Value(std::move(result)));
+}
+
void SafetyHubHandler::RegisterMessages() {
// Usage of base::Unretained(this) is safe, because web_ui() owns `this` and
// won't release ownership until destruction.
@@ -396,6 +463,14 @@ void SafetyHubHandler::RegisterMessages() {
&SafetyHubHandler::
HandleUndoIgnoreOriginsForNotificationPermissionReview,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getSafeBrowsingState",
+ base::BindRepeating(&SafetyHubHandler::HandleGetSafeBrowsingState,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getPasswordCardData",
+ base::BindRepeating(&SafetyHubHandler::HandleGetPasswordCardData,
+ base::Unretained(this)));
}
void SafetyHubHandler::SendUnusedSitePermissionsReviewList() {
@@ -408,11 +483,84 @@ void SafetyHubHandler::SendUnusedSitePermissionsReviewList() {
}
void SafetyHubHandler::SendNotificationPermissionReviewList() {
+ NotificationPermissionsReviewService* service =
+ NotificationPermissionsReviewServiceFactory::GetForProfile(profile_);
+ if (!service) {
+ return;
+ }
+
+ base::Value::List result =
+ service->PopulateNotificationPermissionReviewData(profile_);
// Notify observers that the permission review list could have changed. Note
// that the list is not guaranteed to have changed.
FireWebUIListener(
site_settings::kNotificationPermissionsReviewListMaybeChangedEvent,
- site_settings::PopulateNotificationPermissionReviewData(profile_));
+ service->PopulateNotificationPermissionReviewData(profile_));
+}
+
+base::Value::Dict SafetyHubHandler::GetPasswordCardData(int compromised_count,
+ int weak_count,
+ int reused_count,
+ base::Time last_check) {
+ base::Value::Dict result;
+
+ // TODO(crbug.com/1443466): Handle edge cases: User is signed out, passwords
+ // are disabled due to enterprise policy, or no check has yet taken place.
+ if (compromised_count > 0) {
+ result.Set(kHeader, l10n_util::GetPluralStringFUTF16(
+ IDS_PASSWORD_MANAGER_UI_COMPROMISED_PASSWORDS_COUNT,
+ compromised_count));
+ result.Set(kSubheader,
+ l10n_util::GetStringUTF16(
+ IDS_PASSWORD_MANAGER_UI_HAS_COMPROMISED_PASSWORDS));
+ result.Set(kState, static_cast<int>(SafetyHubCardState::kWarning));
+ return result;
+ }
+
+ if (reused_count > 0) {
+ result.Set(kHeader, l10n_util::GetPluralStringFUTF16(
+ IDS_PASSWORD_MANAGER_UI_REUSED_PASSWORDS_COUNT,
+ reused_count));
+ result.Set(kSubheader, l10n_util::GetStringUTF16(
+ IDS_PASSWORD_MANAGER_UI_HAS_REUSED_PASSWORDS));
+ result.Set(kState, static_cast<int>(SafetyHubCardState::kWeak));
+ return result;
+ }
+
+ if (weak_count > 0) {
+ result.Set(kHeader,
+ l10n_util::GetPluralStringFUTF16(
+ IDS_PASSWORD_MANAGER_UI_WEAK_PASSWORDS_COUNT, weak_count));
+ result.Set(kSubheader, l10n_util::GetStringUTF16(
+ IDS_PASSWORD_MANAGER_UI_HAS_WEAK_PASSWORDS));
+ result.Set(kState, static_cast<int>(SafetyHubCardState::kWeak));
+ return result;
+ }
+
+ // No issues, the card is in the safe state.
+ result.Set(kHeader,
+ l10n_util::GetPluralStringFUTF16(
+ IDS_PASSWORD_MANAGER_UI_COMPROMISED_PASSWORDS_COUNT, 0));
+ // The subheader string depends on how much time has passed since the last
+ // check.
+ base::TimeDelta time_delta = base::Time::Now() - last_check;
+ if (time_delta < base::Minutes(1)) {
+ result.Set(kSubheader,
+ l10n_util::GetStringUTF16(
+ IDS_SETTINGS_SAFETY_HUB_PASSWORD_CHECK_SUBHEADER_RECENTLY));
+ } else {
+ std::u16string last_check_string =
+ ui::TimeFormat::Simple(ui::TimeFormat::Format::FORMAT_DURATION,
+ ui::TimeFormat::Length::LENGTH_LONG, time_delta);
+ result.Set(
+ kSubheader,
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_SAFETY_HUB_PASSWORD_CHECK_SUBHEADER_SOME_TIME_AGO,
+ last_check_string));
+ }
+ result.Set(kState, static_cast<int>(SafetyHubCardState::kSafe));
+
+ return result;
}
void SafetyHubHandler::SetClockForTesting(base::Clock* clock) {
diff --git a/chromium/chrome/browser/ui/webui/settings/safety_hub_handler.h b/chromium/chrome/browser/ui/webui/settings/safety_hub_handler.h
index c073b42d778..971bcafeb4e 100644
--- a/chromium/chrome/browser/ui/webui/settings/safety_hub_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/safety_hub_handler.h
@@ -14,6 +14,28 @@
#include "components/content_settings/core/common/content_settings_types.h"
#include "url/origin.h"
+// The state of Safe Browsing settings.
+enum class SafeBrowsingState {
+ kEnabledEnhanced = 0,
+ kEnabledStandard = 1,
+ kDisabledByAdmin = 2,
+ kDisabledByExtension = 3,
+ kDisabledByUser = 4,
+ // New enum values must go above here.
+ kMaxValue = kDisabledByUser,
+};
+
+// State that a top card in the SafetyHub page can be in.
+// Should be kept in sync with the corresponding enum in
+// chrome/browser/resources/settings/safety_hub/safety_hub_browser_proxy.ts
+enum class SafetyHubCardState {
+ kWarning = 0,
+ kWeak = 1,
+ kInfo = 2,
+ kSafe = 3,
+ kMaxValue = kSafe,
+};
+
/**
* This handler deals with the permission-related operations on the site
* settings page.
@@ -54,6 +76,9 @@ class SafetyHubHandler : public settings::SettingsPageUIHandler {
SafetyHubHandlerTest,
SendNotificationPermissionReviewList_FeatureDisabled);
FRIEND_TEST_ALL_PREFIXES(SafetyHubHandlerTest, RevokeAllContentSettingTypes);
+ FRIEND_TEST_ALL_PREFIXES(SafetyHubHandlerParameterizedTest,
+ PasswordCardState);
+ FRIEND_TEST_ALL_PREFIXES(SafetyHubHandlerTest, PasswordCardCheckTime);
// SettingsPageUIHandler implementation.
void OnJavascriptAllowed() override;
@@ -119,6 +144,18 @@ class SafetyHubHandler : public settings::SettingsPageUIHandler {
void HandleUndoIgnoreOriginsForNotificationPermissionReview(
const base::Value::List& args);
+ // Returns the Safe Browsing state.
+ void HandleGetSafeBrowsingState(const base::Value::List& args);
+
+ // Returns the data for the password card.
+ void HandleGetPasswordCardData(const base::Value::List& args);
+
+ // Helper function for determining password card strings and state.
+ base::Value::Dict GetPasswordCardData(int compromised_count,
+ int weak_count,
+ int reused_count,
+ base::Time last_check);
+
// Sends the list of notification permissions to review to the WebUI.
void SendNotificationPermissionReviewList();
diff --git a/chromium/chrome/browser/ui/webui/settings/safety_hub_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/safety_hub_handler_unittest.cc
index eff41a3c8b1..d2efceac22f 100644
--- a/chromium/chrome/browser/ui/webui/settings/safety_hub_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/safety_hub_handler_unittest.cc
@@ -6,14 +6,18 @@
#include <memory>
#include "base/memory/scoped_refptr.h"
+#include "base/test/gtest_util.h"
#include "base/test/simple_test_clock.h"
#include "base/time/clock.h"
+#include "base/time/time.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/permissions/notification_permission_review_service_factory.h"
+#include "chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h"
+#include "chrome/browser/ui/safety_hub/unused_site_permissions_service.h"
#include "chrome/browser/ui/webui/settings/safety_hub_handler.h"
#include "chrome/browser/ui/webui/settings/site_settings_helper.h"
#include "chrome/common/chrome_features.h"
+#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -22,14 +26,17 @@
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/features.h"
#include "components/permissions/constants.h"
-#include "components/permissions/unused_site_permissions_service.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_web_ui.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
+enum SettingManager { USER, ADMIN, EXTENSION };
constexpr char kUnusedTestSite[] = "https://example1.com";
constexpr char kUsedTestSite[] = "https://example2.com";
constexpr ContentSettingsType kUnusedPermission =
@@ -63,12 +70,10 @@ class SafetyHubHandlerTest : public testing::Test {
handler()->AllowJavascript();
// Create a revoked permission.
- base::Value::Dict dict = base::Value::Dict();
- base::Value::List permission_type_list = base::Value::List();
- permission_type_list.Append(
- static_cast<int32_t>(ContentSettingsType::GEOLOCATION));
- dict.Set(permissions::kRevokedKey,
- base::Value::List(std::move(permission_type_list)));
+ auto dict = base::Value::Dict().Set(
+ permissions::kRevokedKey,
+ base::Value::List().Append(
+ static_cast<int32_t>(ContentSettingsType::GEOLOCATION)));
hcsm()->SetWebsiteSettingDefaultScope(
GURL(kUnusedTestSite), GURL(kUnusedTestSite),
@@ -94,10 +99,9 @@ class SafetyHubHandlerTest : public testing::Test {
}
void ExpectRevokedPermission() {
- ContentSettingsForOneType revoked_permissions_list;
- hcsm()->GetSettingsForOneType(
- ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
- &revoked_permissions_list);
+ ContentSettingsForOneType revoked_permissions_list =
+ hcsm()->GetSettingsForOneType(
+ ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS);
EXPECT_EQ(1U, revoked_permissions_list.size());
EXPECT_EQ(
ContentSetting::CONTENT_SETTING_ASK,
@@ -116,6 +120,53 @@ class SafetyHubHandlerTest : public testing::Test {
ASSERT_TRUE(data.arg2()->is_list());
}
+ void SetPrefsForSafeBrowsing(bool is_enabled,
+ bool is_enhanced,
+ SettingManager managed_by) {
+ auto* prefs = profile()->GetTestingPrefService();
+
+ switch (managed_by) {
+ case USER:
+ prefs->SetUserPref(prefs::kSafeBrowsingEnabled,
+ std::make_unique<base::Value>(is_enabled));
+ prefs->SetUserPref(prefs::kSafeBrowsingEnhanced,
+ std::make_unique<base::Value>(is_enhanced));
+ break;
+ case ADMIN:
+ prefs->SetManagedPref(prefs::kSafeBrowsingEnabled,
+ std::make_unique<base::Value>(is_enabled));
+ prefs->SetManagedPref(prefs::kSafeBrowsingEnhanced,
+ std::make_unique<base::Value>(is_enhanced));
+ break;
+ case EXTENSION:
+ prefs->SetExtensionPref(prefs::kSafeBrowsingEnabled,
+ std::make_unique<base::Value>(is_enabled));
+ prefs->SetExtensionPref(prefs::kSafeBrowsingEnhanced,
+ std::make_unique<base::Value>(is_enhanced));
+ break;
+ default:
+ NOTREACHED() << "Unexpected value for managed_by argument. \n";
+ }
+ }
+
+ void ValidateHandleSafeBrowsingState(SafeBrowsingState state) {
+ base::Value::List args;
+ args.Append("getSafeBrowsingState");
+
+ handler()->HandleGetSafeBrowsingState(args);
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+
+ EXPECT_EQ("cr.webUIResponse", data.function_name());
+ ASSERT_TRUE(data.arg1()->is_string());
+ EXPECT_EQ("getSafeBrowsingState", data.arg1()->GetString());
+ // arg2 is a boolean that is true if the callback is successful.
+ ASSERT_TRUE(data.arg2()->is_bool());
+ ASSERT_TRUE(data.arg2());
+ ASSERT_TRUE(data.arg3()->is_int());
+ EXPECT_EQ((std::int32_t)state, data.arg3()->GetInt());
+ }
+
base::Value::List GetOriginList(int size) {
base::Value::List origins;
for (int i = 0; i < size; i++) {
@@ -140,6 +191,18 @@ class SafetyHubHandlerTest : public testing::Test {
base::SimpleTestClock clock_;
};
+class SafetyHubHandlerParameterizedTest
+ : public SafetyHubHandlerTest,
+ public testing::WithParamInterface<
+ testing::tuple</*compromised_issues*/ int,
+ /*weak_issues*/ int,
+ /*reused_issues*/ int>> {
+ public:
+ int compromised_issues() const { return std::get<0>(GetParam()); }
+ int weak_issues() const { return std::get<1>(GetParam()); }
+ int reused_issues() const { return std::get<2>(GetParam()); }
+};
+
TEST_F(SafetyHubHandlerTest, PopulateUnusedSitePermissionsData) {
// Add GEOLOCATION setting for url but do not add to revoked list.
content_settings::ContentSettingConstraints constraint;
@@ -169,10 +232,9 @@ TEST_F(SafetyHubHandlerTest, HandleAllowPermissionsAgainForUnusedSite) {
handler()->HandleAllowPermissionsAgainForUnusedSite(args);
// Check there is no origin in revoked permissions list.
- ContentSettingsForOneType revoked_permissions_list;
- hcsm()->GetSettingsForOneType(
- ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
- &revoked_permissions_list);
+ ContentSettingsForOneType revoked_permissions_list =
+ hcsm()->GetSettingsForOneType(
+ ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS);
EXPECT_EQ(0U, revoked_permissions_list.size());
// Check if the permissions of url is regranted.
EXPECT_EQ(
@@ -214,9 +276,9 @@ TEST_F(SafetyHubHandlerTest,
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile());
- ContentSettingsForOneType ignored_patterns;
- content_settings->GetSettingsForOneType(
- ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW, &ignored_patterns);
+ ContentSettingsForOneType ignored_patterns =
+ content_settings->GetSettingsForOneType(
+ ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW);
ASSERT_EQ(0U, ignored_patterns.size());
base::Value::List args;
@@ -224,8 +286,8 @@ TEST_F(SafetyHubHandlerTest,
handler()->HandleIgnoreOriginsForNotificationPermissionReview(args);
// Check there is 1 origin in ignore list.
- content_settings->GetSettingsForOneType(
- ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW, &ignored_patterns);
+ ignored_patterns = content_settings->GetSettingsForOneType(
+ ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW);
ASSERT_EQ(1U, ignored_patterns.size());
ValidateNotificationPermissionUpdate();
@@ -240,16 +302,15 @@ TEST_F(SafetyHubHandlerTest,
// Check there is 1 origin in ignore list.
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile());
- ContentSettingsForOneType ignored_patterns;
- ASSERT_EQ(0U, ignored_patterns.size());
- content_settings->GetSettingsForOneType(
- ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW, &ignored_patterns);
+ ContentSettingsForOneType ignored_patterns =
+ content_settings->GetSettingsForOneType(
+ ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW);
ASSERT_EQ(1U, ignored_patterns.size());
// Check there are no origins in ignore list.
handler()->HandleUndoIgnoreOriginsForNotificationPermissionReview(args);
- content_settings->GetSettingsForOneType(
- ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW, &ignored_patterns);
+ ignored_patterns = content_settings->GetSettingsForOneType(
+ ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW);
ASSERT_EQ(0U, ignored_patterns.size());
}
@@ -266,9 +327,9 @@ TEST_F(SafetyHubHandlerTest, HandleAllowNotificationPermissionForOrigins) {
// Check the permission for the two origins is allow.
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile());
- ContentSettingsForOneType notification_permissions;
- content_settings->GetSettingsForOneType(ContentSettingsType::NOTIFICATIONS,
- &notification_permissions);
+ ContentSettingsForOneType notification_permissions =
+ content_settings->GetSettingsForOneType(
+ ContentSettingsType::NOTIFICATIONS);
auto type = content_settings->GetContentSetting(
GURL(origins[0].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS);
ASSERT_EQ(CONTENT_SETTING_ALLOW, type);
@@ -294,9 +355,9 @@ TEST_F(SafetyHubHandlerTest, HandleBlockNotificationPermissionForOrigins) {
// Check the permission for the two origins is block.
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile());
- ContentSettingsForOneType notification_permissions;
- content_settings->GetSettingsForOneType(ContentSettingsType::NOTIFICATIONS,
- &notification_permissions);
+ ContentSettingsForOneType notification_permissions =
+ content_settings->GetSettingsForOneType(
+ ContentSettingsType::NOTIFICATIONS);
auto type = content_settings->GetContentSetting(
GURL(origins[0].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS);
ASSERT_EQ(CONTENT_SETTING_BLOCK, type);
@@ -334,6 +395,52 @@ TEST_F(SafetyHubHandlerTest, HandleResetNotificationPermissionForOrigins) {
ValidateNotificationPermissionUpdate();
}
+TEST_F(SafetyHubHandlerTest, HandleGetSafeBrowsingState_EnabledEnhanced) {
+ SetPrefsForSafeBrowsing(true, true, SettingManager::USER);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kEnabledEnhanced);
+
+ SetPrefsForSafeBrowsing(true, true, SettingManager::EXTENSION);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kEnabledEnhanced);
+
+ SetPrefsForSafeBrowsing(true, true, SettingManager::ADMIN);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kEnabledEnhanced);
+}
+
+TEST_F(SafetyHubHandlerTest, HandleGetSafeBrowsingState_EnabledStandard) {
+ SetPrefsForSafeBrowsing(true, false, SettingManager::USER);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kEnabledStandard);
+
+ SetPrefsForSafeBrowsing(true, false, SettingManager::EXTENSION);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kEnabledStandard);
+
+ SetPrefsForSafeBrowsing(true, false, SettingManager::ADMIN);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kEnabledStandard);
+}
+
+TEST_F(SafetyHubHandlerTest, HandleGetSafeBrowsingState_DisabledByAdmin) {
+ SetPrefsForSafeBrowsing(false, false, SettingManager::ADMIN);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kDisabledByAdmin);
+
+ SetPrefsForSafeBrowsing(false, true, SettingManager::ADMIN);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kDisabledByAdmin);
+}
+
+TEST_F(SafetyHubHandlerTest, HandleGetSafeBrowsingState_DisabledByExtension) {
+ SetPrefsForSafeBrowsing(false, false, SettingManager::EXTENSION);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kDisabledByExtension);
+
+ SetPrefsForSafeBrowsing(false, true, SettingManager::EXTENSION);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kDisabledByExtension);
+}
+
+TEST_F(SafetyHubHandlerTest, HandleGetSafeBrowsingState_DisabledByUser) {
+ SetPrefsForSafeBrowsing(false, false, SettingManager::USER);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kDisabledByUser);
+
+ SetPrefsForSafeBrowsing(false, true, SettingManager::USER);
+ ValidateHandleSafeBrowsingState(SafeBrowsingState::kDisabledByUser);
+}
+
// Test that revocation is happen correctly for all content setting types.
TEST_F(SafetyHubHandlerTest, RevokeAllContentSettingTypes) {
// TODO(crbug.com/1459305): Remove this after adding names for those
@@ -388,3 +495,102 @@ TEST_F(SafetyHubHandlerTest, RevokeAllContentSettingTypes) {
}
}
}
+
+TEST_P(SafetyHubHandlerParameterizedTest, PasswordCardState) {
+ base::Value::Dict card = handler()->GetPasswordCardData(
+ /*compromised_count=*/compromised_issues(),
+ /*weak_count=*/weak_issues(),
+ /*reused_count=*/reused_issues(), base::Time::Now());
+
+ std::u16string header = base::UTF8ToUTF16(*card.FindString("header"));
+ std::u16string subheader = base::UTF8ToUTF16(*card.FindString("subheader"));
+ int state = card.FindInt("state").value();
+
+ if (compromised_issues() > 0) {
+ EXPECT_EQ(header, l10n_util::GetPluralStringFUTF16(
+ IDS_PASSWORD_MANAGER_UI_COMPROMISED_PASSWORDS_COUNT,
+ compromised_issues()));
+ EXPECT_EQ(subheader,
+ l10n_util::GetStringUTF16(
+ IDS_PASSWORD_MANAGER_UI_HAS_COMPROMISED_PASSWORDS));
+ EXPECT_EQ(state, static_cast<int>(SafetyHubCardState::kWarning));
+ return;
+ }
+
+ if (reused_issues() > 0) {
+ EXPECT_EQ(header, l10n_util::GetPluralStringFUTF16(
+ IDS_PASSWORD_MANAGER_UI_REUSED_PASSWORDS_COUNT,
+ reused_issues()));
+ EXPECT_EQ(subheader, l10n_util::GetStringUTF16(
+ IDS_PASSWORD_MANAGER_UI_HAS_REUSED_PASSWORDS));
+ EXPECT_EQ(state, static_cast<int>(SafetyHubCardState::kWeak));
+ return;
+ }
+
+ if (weak_issues() > 0) {
+ EXPECT_EQ(header,
+ l10n_util::GetPluralStringFUTF16(
+ IDS_PASSWORD_MANAGER_UI_WEAK_PASSWORDS_COUNT, weak_issues()));
+ EXPECT_EQ(subheader, l10n_util::GetStringUTF16(
+ IDS_PASSWORD_MANAGER_UI_HAS_WEAK_PASSWORDS));
+ EXPECT_EQ(state, static_cast<int>(SafetyHubCardState::kWeak));
+ return;
+ }
+
+ // When there are no issues, state is safe.
+ EXPECT_EQ(compromised_issues(), 0);
+ EXPECT_EQ(weak_issues(), 0);
+ EXPECT_EQ(reused_issues(), 0);
+ EXPECT_EQ(header,
+ l10n_util::GetPluralStringFUTF16(
+ IDS_PASSWORD_MANAGER_UI_COMPROMISED_PASSWORDS_COUNT, 0));
+ EXPECT_EQ(subheader,
+ l10n_util::GetStringUTF16(
+ IDS_SETTINGS_SAFETY_HUB_PASSWORD_CHECK_SUBHEADER_RECENTLY));
+ EXPECT_EQ(state, static_cast<int>(SafetyHubCardState::kSafe));
+}
+
+TEST_F(SafetyHubHandlerTest, PasswordCardCheckTime) {
+ const std::string kSubheader = "subheader";
+ const base::Value::Dict password_card_now =
+ handler()->GetPasswordCardData(0, 0, 0, base::Time::Now());
+ EXPECT_EQ(*password_card_now.FindString(kSubheader),
+ std::string("Checked just now"));
+
+ const base::Value::Dict password_card_3_sec_ago =
+ handler()->GetPasswordCardData(
+ 0, 0, 0, base::Time::Now() - base::TimeDelta(base::Seconds(3)));
+ EXPECT_EQ(*password_card_3_sec_ago.FindString(kSubheader),
+ std::string("Checked just now"));
+
+ const base::Value::Dict password_card_3_min_ago =
+ handler()->GetPasswordCardData(
+ 0, 0, 0, base::Time::Now() - base::TimeDelta(base::Minutes(3)));
+ EXPECT_EQ(*password_card_3_min_ago.FindString(kSubheader),
+ std::string("Checked 3 minutes ago"));
+
+ const base::Value::Dict password_card_3_hours_ago =
+ handler()->GetPasswordCardData(
+ 0, 0, 0, base::Time::Now() - base::TimeDelta(base::Hours(3)));
+ EXPECT_EQ(*password_card_3_hours_ago.FindString(kSubheader),
+ std::string("Checked 3 hours ago"));
+
+ const base::Value::Dict password_card_3_days_ago =
+ handler()->GetPasswordCardData(
+ 0, 0, 0, base::Time::Now() - base::TimeDelta(base::Days(3)));
+ EXPECT_EQ(*password_card_3_days_ago.FindString(kSubheader),
+ std::string("Checked 3 days ago"));
+
+ const base::Value::Dict password_card_300_days_ago =
+ handler()->GetPasswordCardData(
+ 0, 0, 0, base::Time::Now() - base::TimeDelta(base::Days(300)));
+ EXPECT_EQ(*password_card_300_days_ago.FindString(kSubheader),
+ std::string("Checked 300 days ago"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ SafetyHubHandlerParameterizedTest,
+ ::testing::Combine(/*compromised_issues*/ ::testing::Values(0, 1, 2),
+ /*weak_issues*/ ::testing::Values(0, 1, 2),
+ /*reused_issues*/ ::testing::Values(0, 1, 2)));
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index ba144779fd6..899ae48f57d 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chromium/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -24,8 +24,8 @@
#include "chrome/browser/file_system_access/chrome_file_system_access_permission_context.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/obsolete_system/obsolete_system.h"
+#include "chrome/browser/performance_manager/public/user_tuning/battery_saver_mode_manager.h"
#include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
-#include "chrome/browser/preloading/preloading_features.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service_factory.h"
#include "chrome/browser/profiles/profile.h"
@@ -62,7 +62,6 @@
#include "components/autofill/core/browser/payments/payments_service_url.h"
#include "components/autofill/core/browser/payments/payments_util.h"
#include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/core/browser/sync_utils.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_payments_features.h"
@@ -79,6 +78,8 @@
#include "components/performance_manager/public/features.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_utils.h"
+#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/services/screen_ai/buildflags/buildflags.h"
#include "components/signin/public/base/signin_buildflags.h"
@@ -131,7 +132,7 @@
#endif
#if BUILDFLAG(IS_CHROMEOS)
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#endif
#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -171,6 +172,14 @@
namespace settings {
namespace {
+#if BUILDFLAG(IS_CHROMEOS)
+std::string BuildOSSettingsUrl(const std::string& sub_page) {
+ std::string os_settings_url = chrome::kChromeUIOSSettingsURL;
+ os_settings_url.append(sub_page);
+ return os_settings_url;
+}
+#endif
+
void AddCommonStrings(content::WebUIDataSource* html_source, Profile* profile) {
static constexpr webui::LocalizedString kLocalizedStrings[] = {
{"add", IDS_ADD},
@@ -238,7 +247,7 @@ void AddCommonStrings(content::WebUIDataSource* html_source, Profile* profile) {
"isGuest",
#if BUILDFLAG(IS_CHROMEOS_ASH)
user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
- user_manager::UserManager::Get()->IsLoggedInAsPublicAccount());
+ user_manager::UserManager::Get()->IsLoggedInAsManagedGuestSession());
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
chromeos::BrowserParamsProxy::Get()->SessionType() ==
crosapi::mojom::SessionType::kPublicSession ||
@@ -285,6 +294,12 @@ void AddA11yStrings(content::WebUIDataSource* html_source) {
{"focusHighlightLabel",
IDS_SETTINGS_ACCESSIBILITY_FOCUS_HIGHLIGHT_DESCRIPTION},
#endif
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
+ {"overscrollHistoryNavigationTitle",
+ IDS_SETTINGS_OVERSCROLL_HISTORY_NAVIGATION_TITLE},
+ {"overscrollHistoryNavigationSubtitle",
+ IDS_SETTINGS_OVERSCROLL_HISTORY_NAVIGATION_SUBTITLE},
+#endif
};
html_source->AddLocalizedStrings(kLocalizedStrings);
@@ -301,6 +316,9 @@ void AddA11yStrings(content::WebUIDataSource* html_source) {
html_source->AddBoolean("pdfOcrEnabled",
base::FeatureList::IsEnabled(features::kPdfOcr));
#endif
+ html_source->AddBoolean("overscrollHistoryNavigationSettingEnabled",
+ base::FeatureList::IsEnabled(
+ features::kOverscrollHistoryNavigationSetting));
AddCaptionSubpageStrings(html_source);
}
@@ -311,6 +329,7 @@ void AddAboutStrings(content::WebUIDataSource* html_source, Profile* profile) {
{"aboutProductLogoAlt", IDS_SHORT_PRODUCT_LOGO_ALT_TEXT},
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
{"aboutReportAnIssue", IDS_SETTINGS_ABOUT_PAGE_REPORT_AN_ISSUE},
+ {"aboutPrivacyPolicy", IDS_SETTINGS_ABOUT_PAGE_PRIVACY_POLICY},
#endif
{"aboutRelaunch", IDS_SETTINGS_ABOUT_PAGE_RELAUNCH},
{"aboutUpgradeCheckStarted", IDS_SETTINGS_ABOUT_UPGRADE_CHECK_STARTED},
@@ -372,7 +391,8 @@ void AddAboutStrings(content::WebUIDataSource* html_source, Profile* profile) {
html_source->AddString("aboutObsoleteSystemURL",
ObsoleteSystem::GetLinkURL());
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING) || \
+ BUILDFLAG(GOOGLE_CHROME_FOR_TESTING_BRANDING)
html_source->AddString("aboutTermsURL", chrome::kChromeUITermsURL);
html_source->AddLocalizedString("aboutProductTos",
IDS_ABOUT_TERMS_OF_SERVICE);
@@ -388,6 +408,10 @@ void AddAppearanceStrings(content::WebUIDataSource* html_source,
{"homeButtonDisabled", IDS_SETTINGS_HOME_BUTTON_DISABLED},
{"themes", IDS_SETTINGS_THEMES},
{"chromeColors", IDS_SETTINGS_CHROME_COLORS},
+ {"colorSchemeMode", IDS_SETTINGS_COLOR_SCHEME_MODE},
+ {"lightMode", IDS_NTP_CUSTOMIZE_CHROME_COLOR_SCHEME_MODE_LIGHT_LABEL},
+ {"darkMode", IDS_NTP_CUSTOMIZE_CHROME_COLOR_SCHEME_MODE_DARK_LABEL},
+ {"systemMode", IDS_NTP_CUSTOMIZE_CHROME_COLOR_SCHEME_MODE_SYSTEM_LABEL},
{"showHomeButton", IDS_SETTINGS_SHOW_HOME_BUTTON},
{"showBookmarksBar", IDS_SETTINGS_SHOW_BOOKMARKS_BAR},
{"showHoverCardImages", IDS_SETTINGS_SHOW_HOVER_CARD_IMAGES},
@@ -673,6 +697,8 @@ void AddImportDataStrings(content::WebUIDataSource* html_source) {
void AddPerformanceStrings(content::WebUIDataSource* html_source) {
static constexpr webui::LocalizedString kLocalizedStrings[] = {
{"performancePageTitle", IDS_SETTINGS_PERFORMANCE_PAGE_TITLE},
+ {"memoryPageTitle", IDS_SETTINGS_MEMORY_PAGE_TITLE},
+ {"speedPageTitle", IDS_SETTINGS_SPEED_PAGE_TITLE},
{"highEfficiencyModeLabel",
IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_SETTING},
{"highEfficiencyModeDescription",
@@ -692,6 +718,8 @@ void AddPerformanceStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_SETTING},
{"batterySaverModeDescription",
IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_SETTING_DESCRIPTION},
+ {"batterySaverModeLinkOsDescription",
+ IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_LINK_OS_SETTING_DESCRIPTION},
{"batterySaverModeEnabledOnBatteryLabel",
IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_ON_BATTERY_LABEL},
{"batterySaverModeRadioGroupAriaLabel",
@@ -702,10 +730,14 @@ void AddPerformanceStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_SAVE_BUTTON_ARIA_LABEL},
{"tabDiscardingExceptionsHeader",
IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_HEADER},
+ {"tabDiscardingExceptionsDescription",
+ IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_DESCRIPTION},
{"tabDiscardingExceptionsAdditionalSites",
IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADDITIONAL_SITES},
{"tabDiscardingExceptionsAddDialogCurrentTabs",
IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_CURRENT_TABS},
+ {"tabDiscardingExceptionsAddDialogCurrentTabsEmpty",
+ IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_CURRENT_TABS_EMPTY},
{"tabDiscardingExceptionsAddDialogManual",
IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_MANUAL},
{"tabDiscardingExceptionsActiveSiteAriaDescription",
@@ -714,8 +746,8 @@ void AddPerformanceStrings(content::WebUIDataSource* html_source) {
html_source->AddLocalizedStrings(kLocalizedStrings);
html_source->AddBoolean(
- "highEfficiencyDefaultHeuristicMode",
- performance_manager::features::kHighEfficiencyDefaultHeuristicMode.Get());
+ "highEfficiencyShowRecommendedBadge",
+ performance_manager::features::kHighEfficiencyShowRecommendedBadge.Get());
html_source->AddString(
"tabDiscardTimerFiveMinutes",
@@ -759,7 +791,7 @@ void AddPerformanceStrings(content::WebUIDataSource* html_source) {
l10n_util::GetStringFUTF16(
IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_BELOW_THRESHOLD_LABEL,
base::NumberToString16(
- performance_manager::user_tuning::UserPerformanceTuningManager::
+ performance_manager::user_tuning::BatterySaverModeManager::
kLowBatteryThresholdPercent)));
html_source->AddString(
"tabDiscardingExceptionsAddDialogHelp",
@@ -771,6 +803,12 @@ void AddPerformanceStrings(content::WebUIDataSource* html_source) {
chrome::kHighEfficiencyModeLearnMoreUrl);
html_source->AddString("batterySaverLearnMoreUrl",
chrome::kBatterySaverModeLearnMoreUrl);
+
+#if BUILDFLAG(IS_CHROMEOS)
+ html_source->AddString(
+ "osPowerSettingsUrl",
+ BuildOSSettingsUrl(chromeos::settings::mojom::kPowerSubpagePath));
+#endif
}
void AddLanguagesStrings(content::WebUIDataSource* html_source,
@@ -794,8 +832,6 @@ void AddLanguagesStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_LANGUAGES_IS_DISPLAYED_IN_THIS_LANGUAGE},
{"displayInThisLanguage", IDS_SETTINGS_LANGUAGES_DISPLAY_IN_THIS_LANGUAGE},
#endif
- {"offerToTranslateInThisLanguage",
- IDS_SETTINGS_LANGUAGES_OFFER_TO_TRANSLATE_IN_THIS_LANGUAGE},
{"offerToEnableTranslate",
IDS_SETTINGS_LANGUAGES_OFFER_TO_ENABLE_TRANSLATE},
{"offerToEnableTranslateSublabel",
@@ -887,11 +923,7 @@ void AddOnStartupStrings(content::WebUIDataSource* html_source) {
bool IsFidoAuthenticationAvailable(autofill::PersonalDataManager* personal_data,
content::WebContents* web_contents) {
// Don't show toggle switch if user is unable to downstream cards.
- if (personal_data->GetSyncSigninState() !=
- autofill::AutofillSyncSigninState::
- kSignedInAndWalletSyncTransportEnabled &&
- personal_data->GetSyncSigninState() !=
- autofill::AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled) {
+ if (!personal_data->IsPaymentsDownloadActive()) {
return false;
}
@@ -905,8 +937,6 @@ bool IsFidoAuthenticationAvailable(autofill::PersonalDataManager* personal_data,
web_contents->GetPrimaryMainFrame());
if (!autofill_driver)
return false;
- if (!autofill_driver->autofill_manager())
- return false;
// Show the toggle switch only if FIDO authentication is available. Once
// returned, this decision may be overridden (from true to false) by the
@@ -925,57 +955,17 @@ bool CheckDeviceAuthAvailability(content::WebContents* web_contents) {
return autofill::IsDeviceAuthAvailable(client->GetDeviceAuthenticator());
}
+bool CheckCvcStorageAvailability() {
+ return base::FeatureList::IsEnabled(
+ autofill::features::kAutofillEnableCvcStorageAndFilling);
+}
+
void AddAutofillStrings(content::WebUIDataSource* html_source,
Profile* profile,
content::WebContents* web_contents) {
static constexpr webui::LocalizedString kLocalizedStrings[] = {
{"autofillPageTitle", IDS_SETTINGS_AUTOFILL_AND_PASSWORDS},
{"passwordsDescription", IDS_SETTINGS_PASSWORD_MANAGER_DESCRIPTION},
- {"passwordsDevice", IDS_SETTINGS_DEVICE_PASSWORDS},
- {"checkPasswords", IDS_SETTINGS_CHECK_PASSWORDS},
- {"checkPasswordsCanceled", IDS_SETTINGS_CHECK_PASSWORDS_CANCELED},
- {"checkedPasswords", IDS_SETTINGS_CHECKED_PASSWORDS},
- {"checkPasswordsDescription", IDS_SETTINGS_CHECK_PASSWORDS_DESCRIPTION},
- {"checkPasswordsErrorOffline", IDS_SETTINGS_CHECK_PASSWORDS_ERROR_OFFLINE},
- {"checkPasswordsErrorSignedOut",
- IDS_SETTINGS_CHECK_PASSWORDS_ERROR_SIGNED_OUT},
- {"checkPasswordsErrorNoPasswords",
- IDS_SETTINGS_CHECK_PASSWORDS_ERROR_NO_PASSWORDS},
- {"checkPasswordsErrorQuota",
- IDS_SETTINGS_CHECK_PASSWORDS_ERROR_QUOTA_LIMIT},
- {"checkPasswordsErrorGeneric", IDS_SETTINGS_CHECK_PASSWORDS_ERROR_GENERIC},
- {"noCompromisedCredentials", IDS_SETTINGS_NO_COMPROMISED_CREDENTIALS_LABEL},
- {"checkPasswordsAgain", IDS_SETTINGS_CHECK_PASSWORDS_AGAIN},
- {"checkPasswordsAgainAfterError",
- IDS_SETTINGS_CHECK_PASSWORDS_AGAIN_AFTER_ERROR},
- {"checkPasswordsProgress", IDS_SETTINGS_CHECK_PASSWORDS_PROGRESS},
- {"checkPasswordsStop", IDS_SETTINGS_CHECK_PASSWORDS_STOP},
- {"compromisedPasswords", IDS_SETTINGS_COMPROMISED_PASSWORDS},
- {"compromisedPasswordsDescription",
- IDS_SETTINGS_COMPROMISED_PASSWORDS_ADVICE},
- {"mutedPasswords", IDS_SETTINGS_MUTED_PASSWORDS},
- {"weakPasswords", IDS_SETTINGS_WEAK_PASSWORDS},
- {"changePasswordButton", IDS_SETTINGS_CHANGE_PASSWORD_BUTTON},
- {"changePasswordInApp", IDS_SETTINGS_CHANGE_PASSWORD_IN_APP_LABEL},
- {"leakedPassword", IDS_SETTINGS_COMPROMISED_PASSWORD_REASON_LEAKED},
- {"phishedPassword", IDS_SETTINGS_COMPROMISED_PASSWORD_REASON_PHISHED},
- {"phishedAndLeakedPassword",
- IDS_SETTINGS_COMPROMISED_PASSWORD_REASON_PHISHED_AND_LEAKED},
- {"showCompromisedPassword", IDS_SETTINGS_COMPROMISED_PASSWORD_SHOW},
- {"hideCompromisedPassword", IDS_SETTINGS_COMPROMISED_PASSWORD_HIDE},
- {"removeCompromisedPassword", IDS_SETTINGS_COMPROMISED_PASSWORD_REMOVE},
- {"muteCompromisedPassword", IDS_SETTINGS_COMPROMISED_PASSWORD_MUTE},
- {"unmuteMutedCompromisedPassword",
- IDS_SETTINGS_COMPROMISED_PASSWORD_UNMUTE},
- {"removeCompromisedPasswordConfirmationTitle",
- IDS_SETTINGS_REMOVE_COMPROMISED_PASSWORD_CONFIRMATION_TITLE},
- {"removeCompromisedPasswordConfirmationDescription",
- IDS_SETTINGS_REMOVE_COMPROMISED_PASSWORD_CONFIRMATION_DESCRIPTION},
- {"alreadyChangedPasswordLink",
- IDS_SETTINGS_COMPROMISED_ALREADY_CHANGED_PASSWORD},
- {"editDisclaimerTitle", IDS_SETTINGS_COMPROMISED_EDIT_DISCLAIMER_TITLE},
- {"editDisclaimerDescription",
- IDS_SETTINGS_COMPROMISED_EDIT_DISCLAIMER_DESCRIPTION},
{"genericCreditCard", IDS_AUTOFILL_CC_GENERIC},
{"creditCards", IDS_AUTOFILL_PAYMENT_METHODS},
{"paymentsMethodsTableAriaLabel",
@@ -991,10 +981,20 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
{"enableCreditCardFIDOAuthLabel", IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_LABEL},
{"enableCreditCardFIDOAuthSublabel",
IDS_ENABLE_CREDIT_CARD_FIDO_AUTH_SUBLABEL},
+ {"enableCvcStorageLabel",
+ IDS_AUTOFILL_SETTINGS_PAGE_ENABLE_CVC_STORAGE_LABEL},
+ {"enableCvcStorageSublabel",
+ IDS_AUTOFILL_SETTINGS_PAGE_ENABLE_CVC_STORAGE_SUBLABEL},
+ {"enableCvcStorageDeleteDataSublabel",
+ IDS_AUTOFILL_SETTINGS_PAGE_ENABLE_CVC_STORAGE_WITH_DELETE_LINK_SUBLABEL},
{"enableMandatoryAuthToggleLabel",
IDS_AUTOFILL_SETTINGS_PAGE_ENABLE_PAYMENT_METHOD_MANDATORY_REAUTH_LABEL},
{"enableMandatoryAuthToggleSublabel",
IDS_AUTOFILL_SETTINGS_PAGE_ENABLE_PAYMENT_METHOD_MANDATORY_REAUTH_SUBLABEL},
+ {"bulkRemoveCvcConfirmationTitle",
+ IDS_AUTOFILL_SETTINGS_PAGE_BULK_REMOVE_CVC_TITLE},
+ {"bulkRemoveCvcConfirmationDescription",
+ IDS_AUTOFILL_SETTINGS_PAGE_BULK_REMOVE_CVC_DESCRIPTION},
{"addresses", IDS_AUTOFILL_ADDRESSES},
{"addressesTableAriaLabel", IDS_AUTOFILL_ADDRESSES_TABLE_ARIA_LABEL},
{"addressesTitle", IDS_AUTOFILL_ADDRESSES_SETTINGS_TITLE},
@@ -1035,7 +1035,6 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
{"editAddressRequiredFieldsError",
IDS_AUTOFILL_EDIT_ADDRESS_REQUIRED_FIELDS_FORM_ERROR},
{"clearCreditCard", IDS_SETTINGS_CREDIT_CARD_CLEAR},
- {"creditCardType", IDS_SETTINGS_AUTOFILL_CREDIT_CARD_TYPE_COLUMN_LABEL},
{"creditCardExpiration", IDS_SETTINGS_CREDIT_CARD_EXPIRATION_DATE},
{"creditCardName", IDS_SETTINGS_NAME_ON_CREDIT_CARD},
{"creditCardNickname", IDS_SETTINGS_CREDIT_CARD_NICKNAME},
@@ -1065,207 +1064,36 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
{"migratableCardsInfoMultiple",
IDS_SETTINGS_MULTIPLE_MIGRATABLE_CARDS_INFO},
{"remoteCreditCardLinkLabel", IDS_SETTINGS_REMOTE_CREDIT_CARD_LINK_LABEL},
- {"upiIdLabel", IDS_SETTINGS_UPI_ID_LABEL},
- {"upiIdExpirationNever", IDS_SETTINGS_UPI_ID_EXPIRATION_NEVER},
{"canMakePaymentToggleLabel", IDS_SETTINGS_CAN_MAKE_PAYMENT_TOGGLE_LABEL},
{"autofillDetail", IDS_SETTINGS_AUTOFILL_DETAIL},
{"passwords", IDS_SETTINGS_PASSWORD_MANAGER},
- {"passwordsSavePasswordsLabel",
- IDS_SETTINGS_PASSWORDS_SAVE_PASSWORDS_TOGGLE_LABEL},
- {"passwordsAutosigninLabel",
- IDS_SETTINGS_PASSWORDS_AUTOSIGNIN_CHECKBOX_LABEL},
- {"passwordsAutosigninDescription",
- IDS_SETTINGS_PASSWORDS_AUTOSIGNIN_CHECKBOX_DESC},
{"passwordsLeakDetectionLabel",
IDS_SETTINGS_PASSWORDS_LEAK_DETECTION_LABEL},
+ {"passwordsLeakDetectionLabelUpdated",
+ IDS_SETTINGS_PASSWORDS_LEAK_DETECTION_LABEL_UPDATED},
{"passwordsLeakDetectionGeneralDescription",
IDS_PASSWORD_MANAGER_LEAK_HELP_MESSAGE},
+ {"passwordsLeakDetectionGeneralDescriptionUpdated",
+ IDS_PASSWORD_MANAGER_LEAK_HELP_MESSAGE_UPDATED},
{"passwordsLeakDetectionSignedOutEnabledDescription",
IDS_SETTINGS_PASSWORDS_LEAK_DETECTION_SIGNED_OUT_ENABLED_DESC},
- {"savedPasswordsHeading", IDS_SETTINGS_PASSWORDS_SAVED_HEADING},
- {"passwordExceptionsHeading", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_HEADING},
- {"deviceOnlyPasswordsHeading",
- IDS_SETTINGS_DEVICE_PASSWORDS_ON_DEVICE_ONLY_HEADING},
- {"deviceAndAccountPasswordsHeading",
- IDS_SETTINGS_DEVICE_PASSWORDS_ON_DEVICE_AND_ACCOUNT_HEADING},
- {"deletePasswordException", IDS_SETTINGS_PASSWORDS_DELETE_EXCEPTION},
- {"removePassword", IDS_SETTINGS_PASSWORD_REMOVE},
- {"searchPasswords", IDS_SETTINGS_PASSWORD_SEARCH},
- {"showPassword", IDS_SETTINGS_PASSWORD_SHOW},
- {"hidePassword", IDS_SETTINGS_PASSWORD_HIDE},
- {"passwordDetailsTitle", IDS_SETTINGS_PASSWORDS_VIEW_DETAILS_TITLE},
- {"passwordViewDetails", IDS_SETTINGS_PASSWORD_DETAILS},
- {"editPasswordTitle", IDS_SETTINGS_PASSWORD_EDIT_TITLE},
- {"editPassword", IDS_SETTINGS_PASSWORD_EDIT},
- {"editPasswordFootnote", IDS_SETTINGS_PASSWORD_EDIT_FOOTNOTE},
- {"addPasswordTitle", IDS_SETTINGS_PASSWORD_ADD_TITLE},
- {"addPasswordFootnote", IDS_SETTINGS_PASSWORD_ADD_FOOTNOTE},
- {"addPasswordStoreOptionAccount",
- IDS_SETTINGS_PASSWORD_STORE_PICKER_OPTION_ACCOUNT},
- {"addPasswordStoreOptionDevice",
- IDS_PASSWORD_MANAGER_DESTINATION_DROPDOWN_SAVE_TO_DEVICE},
- {"addPasswordStorePickerA11yDescription",
- IDS_PASSWORD_MANAGER_DESTINATION_DROPDOWN_ACCESSIBLE_NAME},
- {"usernameAlreadyUsed", IDS_SETTINGS_PASSWORD_USERNAME_ALREADY_USED},
- {"missingTLD", IDS_SETTINGS_PASSWORD_MISSING_TLD},
- {"viewExistingPassword", IDS_SETTINGS_PASSWORD_VIEW_EXISTING_PASSWORD},
- {"viewExistingPasswordAriaDescription",
- IDS_SETTINGS_PASSWORD_VIEW_EXISTING_PASSWORD_ARIA_DESCRIPTION},
- {"copyPassword", IDS_SETTINGS_PASSWORD_COPY},
- {"sendPassword", IDS_SETTINGS_PASSWORD_SEND},
- {"copyUsername", IDS_SETTINGS_USERNAME_COPY},
- {"passwordStoredOnDevice", IDS_SETTINGS_PASSWORD_STORED_ON_DEVICE},
- {"passwordStoredInAccount", IDS_SETTINGS_PASSWORD_STORED_IN_ACCOUNT},
- {"passwordStoredInAccountAndOnDevice",
- IDS_SETTINGS_PASSWORD_STORED_IN_ACCOUNT_AND_ON_DEVICE},
- {"editPasswordWebsiteLabel", IDS_SETTINGS_PASSWORDS_WEBSITE},
- {"editPasswordAppLabel", IDS_SETTINGS_COMPROMISED_EDIT_PASSWORD_APP},
- {"editPasswordUsernameLabel", IDS_SETTINGS_PASSWORDS_USERNAME},
- {"editPasswordPasswordLabel", IDS_SETTINGS_PASSWORDS_PASSWORD},
- {"passwordNoteLabel", IDS_SETTINGS_PASSWORDS_NOTE},
- {"passwordNoNoteAdded", IDS_SETTINGS_PASSWORDS_NO_NOTE_ADDED},
- {"passwordNoteCharacterCount", IDS_SETTINGS_PASSWORDS_NOTE_CHARACTER_COUNT},
- {"passwordNoteCharacterCountWarning",
- IDS_SETTINGS_PASSWORDS_NOTE_CHARACTER_COUNT_WARNING},
- {"passwordsTimedOut", IDS_SETTINGS_PASSWORDS_TIMED_OUT},
- {"passwordsGotIt", IDS_SETTINGS_GOT_IT},
+ {"editPasskeySiteLabel", IDS_SETTINGS_PASSKEYS_SITE_LABEL},
+ {"editPasskeyUsernameLabel", IDS_SETTINGS_SECURITY_KEYS_CREDENTIAL_USERNAME_LABEL},
#if BUILDFLAG(IS_MAC)
{"passkeyLengthError", IDS_SETTINGS_PASSKEYS_LENGTH_ERROR},
- {"editPasskeySiteLabel", IDS_SETTINGS_PASSKEYS_SITE_LABEL},
{"editPasskeyDialogTitle", IDS_SETTINGS_PASSKEYS_DIALOG_TITLE},
{"passkeyEditDialogFootnote", IDS_SETTINGS_PASSKEYS_EDIT_DIALOG_FOOTNOTE},
#endif
{"noAddressesFound", IDS_SETTINGS_ADDRESS_NONE},
- {"noPasswordsFound", IDS_SETTINGS_PASSWORDS_NONE},
- {"noPasswordsFoundImport", IDS_SETTINGS_PASSWORDS_NONE_WITH_IMPORT},
- {"noExceptionsFound", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_NONE},
- {"optInAccountStorageLabel",
- IDS_SETTINGS_PASSWORDS_OPT_IN_ACCOUNT_STORAGE_LABEL},
- {"optOutAccountStorageLabel",
- IDS_SETTINGS_PASSWORDS_OPT_OUT_ACCOUNT_STORAGE_LABEL},
- {"undoRemovePassword", IDS_SETTINGS_PASSWORD_UNDO},
- {"movePasswordToAccount", IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT},
- {"passwordDeleted", IDS_SETTINGS_PASSWORD_DELETED_PASSWORD},
- {"passwordDeletedFromDevice",
- IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_DEVICE},
- {"passwordDeletedFromAccount",
- IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_ACCOUNT},
- {"passwordDeletedFromAccountAndDevice",
- IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_ACCOUNT_AND_DEVICE},
- {"passwordCopiedToClipboard", IDS_SETTINGS_PASSWORD_COPIED_TO_CLIPBOARD},
- {"passwordUsernameCopiedToClipboard",
- IDS_SETTINGS_PASSWORD_USERNAME_COPIED_TO_CLIPBOARD},
- {"passwordMovePasswordsToAccount",
- IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT},
- {"passwordMovePasswordsToAccountDialogBodyText",
- IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_BODY_TEXT},
- {"passwordMovePasswordsToAccountDialogTitle",
- IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE},
- {"passwordMoveMultiplePasswordsToAccountDialogMoveButtonText",
- IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_MOVE_BUTTON_TEXT},
- {"passwordMoveMultiplePasswordsToAccountDialogCancelButtonText",
- IDS_SETTINGS_PASSWORD_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_CANCEL_BUTTON_TEXT},
- {"passwordOpenMoveMultiplePasswordsToAccountDialogButtonText",
- IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT},
- {"passwordRemoveDialogTitle", IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_TITLE},
- {"passwordRemoveDialogBody", IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_BODY},
- {"passwordRemoveDialogRemoveButtonText",
- IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_REMOVE_BUTTON_TEXT},
- {"passwordRemoveDialogCancelButtonText",
- IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_CANCEL_BUTTON_TEXT},
- {"passwordRemoveDialogFromAccountCheckboxLabel",
- IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_FROM_ACCOUNT_CHECKBOX_LABEL},
- {"passwordRemoveDialogFromDeviceCheckboxLabel",
- IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_FROM_DEVICE_CHECKBOX_LABEL},
- {"devicePasswordsLinkLabel", IDS_SETTINGS_DEVICE_PASSWORDS_LINK_LABEL},
- {"devicePasswordsMoved",
- IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_SNACKBAR},
- {"passwordRowMoreActionsButton", IDS_SETTINGS_PASSWORD_ROW_MORE_ACTIONS},
- {"passwordRowFederatedMoreActionsButton",
- IDS_SETTINGS_PASSWORD_ROW_FEDERATED_MORE_ACTIONS},
- {"passwordTableAriaLabel", IDS_SETTINGS_PASSWORD_TABLE_ARIA_LABEL},
- {"passwordRowPasswordDetailPageButton",
- IDS_SETTINGS_PASSWORD_ROW_PASSWORD_DETAIL_PAGE},
- {"localPasswordManager",
- IDS_PASSWORD_BUBBLES_PASSWORD_MANAGER_LINK_TEXT_SAVING_ON_DEVICE},
- {"importMenuItem", IDS_SETTINGS_PASSWORDS_IMPORT_MENU_ITEM},
- {"importPasswordsTitle", IDS_SETTINGS_PASSWORDS_IMPORT_TITLE},
- {"importPasswordsErrorTitle", IDS_SETTINGS_PASSWORDS_IMPORT_ERROR_TITLE},
- {"importPasswordsCompleteTitle",
- IDS_SETTINGS_PASSWORDS_IMPORT_COMPLETE_TITLE},
- {"importPasswordsSuccessTitle",
- IDS_SETTINGS_PASSWORDS_IMPORT_SUCCESS_TITLE},
- {"importPasswordsChooseFile", IDS_SETTINGS_PASSWORDS_IMPORT_CHOOSE_FILE},
- {"importPasswordsSuccessTip", IDS_SETTINGS_PASSWORDS_IMPORT_SUCCESS_TIP},
- {"importPasswordsDeleteFileOption",
- IDS_SETTINGS_PASSWORDS_IMPORT_DELETE_FILE_OPTION},
- {"importPasswordsMissingPassword",
- IDS_SETTINGS_PASSWORDS_IMPORT_MISSING_PASSWORD},
- {"importPasswordsMissingURL", IDS_SETTINGS_PASSWORDS_IMPORT_MISSING_URL},
- {"importPasswordsInvalidURL", IDS_SETTINGS_PASSWORDS_IMPORT_INVALID_URL},
- {"importPasswordsLongURL", IDS_SETTINGS_PASSWORDS_IMPORT_LONG_URL},
- {"importPasswordsLongPassword",
- IDS_SETTINGS_PASSWORDS_IMPORT_LONG_PASSWORD},
- {"importPasswordsLongUsername",
- IDS_SETTINGS_PASSWORDS_IMPORT_LONG_USERNAME},
- {"importPasswordsLongNote", IDS_SETTINGS_PASSWORDS_IMPORT_LONG_NOTE},
- {"importPasswordsConflictDevice",
- IDS_SETTINGS_PASSWORDS_IMPORT_CONFLICT_DEVICE},
- {"importPasswordsConflictAccount",
- IDS_SETTINGS_PASSWORDS_IMPORT_CONFLICT_ACCOUNT},
- {"importPasswordsConflictsDescription",
- IDS_SETTINGS_PASSWORDS_IMPORT_CONFLICTS_DESCRIPTION},
- {"importPasswordsCancel", IDS_SETTINGS_PASSWORDS_IMPORT_CANCEL},
- {"importPasswordsSkip", IDS_SETTINGS_PASSWORDS_IMPORT_SKIP},
- {"importPasswordsReplace", IDS_SETTINGS_PASSWORDS_IMPORT_REPLACE},
- {"importPasswordsUnknownError",
- IDS_SETTINGS_PASSWORDS_IMPORT_ERROR_UNKNOWN},
- {"importPasswordsBadFormatError",
- IDS_SETTINGS_PASSWORDS_IMPORT_ERROR_BAD_FORMAT},
- {"importPasswordsGenericDescription",
- IDS_SETTINGS_PASSWORDS_IMPORT_DESCRIPTION_ACCOUNT_STORE_USERS},
- {"importPasswordsDescriptionAccount",
- IDS_SETTINGS_PASSWORDS_IMPORT_DESCRIPTION_SYNCING_USERS},
- {"importPasswordsDescriptionDevice",
- IDS_SETTINGS_PASSWORDS_IMPORT_DESCRIPTION_SIGNEDOUT_USERS},
- {"importPasswordsStorePickerA11yDescription",
- IDS_SETTINGS_PASSWORDS_IMPORT_STORE_PICKER_ACCESSIBLE_NAME},
- {"importPasswordsAlreadyActive",
- IDS_SETTINGS_PASSWORDS_IMPORT_ALREADY_ACTIVE},
- {"importPasswordsLimitExceeded",
- IDS_SETTINGS_PASSWORDS_IMPORT_ERROR_LIMIT_EXCEEDED},
- {"importPasswordsFileSizeExceeded",
- IDS_SETTINGS_PASSWORDS_IMPORT_FILE_SIZE_EXCEEDED},
- {"exportMenuItem", IDS_SETTINGS_PASSWORDS_EXPORT_MENU_ITEM},
- {"exportPasswordsTitle", IDS_SETTINGS_PASSWORDS_EXPORT_TITLE},
- {"exportPasswordsDescription", IDS_SETTINGS_PASSWORDS_EXPORT_DESCRIPTION},
- {"exportPasswords", IDS_SETTINGS_PASSWORDS_EXPORT},
- {"exportingPasswordsTitle", IDS_SETTINGS_PASSWORDS_EXPORTING_TITLE},
- {"exportPasswordsTryAgain", IDS_SETTINGS_PASSWORDS_EXPORT_TRY_AGAIN},
- {"exportPasswordsFailTitle",
- IDS_SETTINGS_PASSWORDS_EXPORTING_FAILURE_TITLE},
- {"exportPasswordsFailTips", IDS_SETTINGS_PASSWORDS_EXPORTING_FAILURE_TIPS},
- {"exportPasswordsFailTipsEnoughSpace",
- IDS_SETTINGS_PASSWORDS_EXPORTING_FAILURE_TIP_ENOUGH_SPACE},
- {"exportPasswordsFailTipsAnotherFolder",
- IDS_SETTINGS_PASSWORDS_EXPORTING_FAILURE_TIP_ANOTHER_FOLDER},
- {"managePasswordsPlaintext",
- IDS_SETTINGS_PASSWORDS_MANAGE_PASSWORDS_PLAINTEXT},
- {"savedToThisDeviceOnly", IDS_SETTINGS_PAYMENTS_SAVED_TO_THIS_DEVICE_ONLY},
- {"trustedVaultBannerLabel", IDS_SETTINGS_TRUSTED_VAULT_BANNER_LABEL},
- {"trustedVaultBannerSubLabelOfferOptIn",
- IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN},
- {"trustedVaultBannerSubLabelOptedIn",
- IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN},
{"noSearchResults", IDS_SEARCH_NO_RESULTS},
{"searchResultsPlural", IDS_SEARCH_RESULTS_PLURAL},
{"searchResultsSingular", IDS_SEARCH_RESULTS_SINGULAR},
- {"showPasswordLabel", IDS_SETTINGS_PASSWORD_SHOW_PASSWORD_A11Y},
- {"hidePasswordLabel", IDS_SETTINGS_PASSWORD_HIDE_PASSWORD_A11Y},
{"addVirtualCard", IDS_AUTOFILL_ADD_VIRTUAL_CARD},
+ {"savedToThisDeviceOnly", IDS_SETTINGS_PAYMENTS_SAVED_TO_THIS_DEVICE_ONLY},
+ {"localPasswordManager",
+ IDS_PASSWORD_BUBBLES_PASSWORD_MANAGER_LINK_TEXT_SAVING_ON_DEVICE},
{"removeVirtualCard", IDS_AUTOFILL_REMOVE_VIRTUAL_CARD},
{"editServerCard", IDS_AUTOFILL_EDIT_SERVER_CREDIT_CARD},
- {"virtualCardEnabled", IDS_AUTOFILL_VIRTUAL_CARD_ENABLED_LABEL},
{"virtualCardTurnedOn", IDS_AUTOFILL_VIRTUAL_CARD_TURNED_ON_LABEL},
{"unenrollVirtualCardDialogTitle",
IDS_AUTOFILL_VIRTUAL_CARD_UNENROLL_DIALOG_TITLE},
@@ -1301,30 +1129,6 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
GURL google_password_manager_url = GetGooglePasswordManagerURL(
password_manager::ManagePasswordsReferrer::kChromeSettings);
- html_source->AddString(
- "optInAccountStorageBody",
- l10n_util::GetStringFUTF16(
- IDS_SETTINGS_PASSWORDS_OPT_IN_ACCOUNT_STORAGE_BODY,
- base::UTF8ToUTF16(google_password_manager_url.spec())));
- html_source->AddString(
- "optOutAccountStorageBody",
- l10n_util::GetStringFUTF16(
- IDS_SETTINGS_PASSWORDS_OPT_OUT_ACCOUNT_STORAGE_BODY,
- base::UTF8ToUTF16(google_password_manager_url.spec())));
- html_source->AddString(
- "checkPasswordsErrorQuotaGoogleAccount",
- l10n_util::GetStringFUTF16(
- IDS_SETTINGS_CHECK_PASSWORDS_ERROR_QUOTA_LIMIT_GOOGLE_ACCOUNT,
- base::UTF8ToUTF16(
- password_manager::GetPasswordCheckupURL(
- password_manager::PasswordCheckupReferrer::kPasswordCheck)
- .spec())));
- html_source->AddString("googlePasswordManagerUrl",
- google_password_manager_url.spec());
- html_source->AddString("passwordCheckLearnMoreURL",
- chrome::kPasswordCheckLearnMoreURL);
- html_source->AddString("passwordManagerLearnMoreURL",
- chrome::kPasswordManagerLearnMoreURL);
html_source->AddString("manageAddressesUrl",
autofill::payments::GetManageAddressesUrl().spec());
html_source->AddString("manageCreditCardsLabel",
@@ -1335,25 +1139,10 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
autofill::payments::GetManageInstrumentsUrl().spec());
html_source->AddString("addressesAndPaymentMethodsLearnMoreURL",
chrome::kAddressesAndPaymentMethodsLearnMoreURL);
- html_source->AddString(
- "weakPasswordsDescription",
- l10n_util::GetStringFUTF16(
- IDS_SETTINGS_WEAK_PASSWORDS_DESCRIPTION,
- base::ASCIIToUTF16(chrome::kSeeMoreSecurityTipsURL)));
- html_source->AddString(
- "weakPasswordsDescriptionGeneration",
- l10n_util::GetStringFUTF16(
- IDS_SETTINGS_WEAK_PASSWORDS_DESCRIPTION_GENERATION,
- base::ASCIIToUTF16(chrome::kPasswordGenerationLearnMoreURL)));
html_source->AddString("signedOutUserLabel",
l10n_util::GetStringFUTF16(
IDS_SETTINGS_SIGNED_OUT_USER_LABEL,
base::ASCIIToUTF16(chrome::kSyncLearnMoreURL)));
- html_source->AddString(
- "signedOutUserHasCompromisedCredentialsLabel",
- l10n_util::GetStringFUTF16(
- IDS_SETTINGS_SIGNED_OUT_USER_HAS_COMPROMISED_CREDENTIALS_LABEL,
- base::ASCIIToUTF16(chrome::kSyncLearnMoreURL)));
html_source->AddString("trustedVaultOptInUrl",
chrome::kSyncTrustedVaultOptInURL);
html_source->AddString("trustedVaultLearnMoreUrl",
@@ -1361,8 +1150,9 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
bool is_guest_mode = false;
#if BUILDFLAG(IS_CHROMEOS_ASH)
- is_guest_mode = user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
- user_manager::UserManager::Get()->IsLoggedInAsPublicAccount();
+ is_guest_mode =
+ user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
+ user_manager::UserManager::Get()->IsLoggedInAsManagedGuestSession();
#else // !BUILDFLAG(IS_CHROMEOS_ASH)
is_guest_mode = profile->IsOffTheRecord();
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -1370,11 +1160,11 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
autofill::PersonalDataManagerFactory::GetForProfile(profile);
html_source->AddBoolean(
"migrationEnabled",
- !is_guest_mode && autofill::IsCreditCardMigrationEnabled(
- personal_data, profile->GetPrefs(),
- SyncServiceFactory::GetForProfile(profile),
- /*is_test_mode=*/false,
- /*log_manager=*/nullptr));
+ !is_guest_mode &&
+ autofill::IsCreditCardMigrationEnabled(
+ personal_data, SyncServiceFactory::GetForProfile(profile),
+ /*is_test_mode=*/false,
+ /*log_manager=*/nullptr));
html_source->AddBoolean("showIbansSettings",
autofill::ShouldShowIbanOnSettingsPage(
@@ -1384,6 +1174,13 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
html_source->AddBoolean("deviceAuthAvailable",
CheckDeviceAuthAvailability(web_contents));
+ html_source->AddBoolean("cvcStorageAvailable", CheckCvcStorageAvailability());
+
+ html_source->AddBoolean(
+ "autofillEnablePaymentsMandatoryReauth",
+ base::FeatureList::IsEnabled(
+ autofill::features::kAutofillEnablePaymentsMandatoryReauth));
+
html_source->AddBoolean(
"fidoAuthenticationAvailableForAutofill",
IsFidoAuthenticationAvailable(personal_data, web_contents));
@@ -1395,15 +1192,6 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
undo_accelerator.GetShortcutText()));
html_source->AddBoolean(
- "removeCardExpirationAndTypeTitles",
- base::FeatureList::IsEnabled(
- autofill::features::kAutofillRemoveCardExpirationAndTypeTitles));
-
- html_source->AddBoolean("showUpiIdSettings",
- base::FeatureList::IsEnabled(
- autofill::features::kAutofillSaveAndFillVPA));
-
- html_source->AddBoolean(
"showHonorific",
base::FeatureList::IsEnabled(
autofill::features::kAutofillEnableSupportForHonorificPrefixes));
@@ -1424,12 +1212,16 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
.spec())));
html_source->AddLocalizedStrings(kLocalizedStrings);
- // PASSWORD_VIEW page timeouts in 5 minutes:
- html_source->AddString(
- "passwordsTimedOutDescription",
- l10n_util::GetPluralStringFUTF16(
- IDS_SETTINGS_PASSWORDS_TIMED_OUT_DESCRIPTION,
- syncer::kPasswordNotesAuthValidity.Get().InMinutes()));
+
+ html_source->AddBoolean(
+ "autofillAccountProfileStorage",
+ base::FeatureList::IsEnabled(
+ autofill::features::kAutofillAccountProfileStorage));
+
+ html_source->AddBoolean(
+ "syncEnableContactInfoDataTypeInTransportMode",
+ base::FeatureList::IsEnabled(
+ syncer::kSyncEnableContactInfoDataTypeInTransportMode));
}
void AddSignOutDialogStrings(content::WebUIDataSource* html_source,
@@ -1551,17 +1343,8 @@ void AddPersonalizationOptionsStrings(content::WebUIDataSource* html_source) {
html_source->AddLocalizedStrings(kLocalizedStrings);
}
-#if BUILDFLAG(IS_CHROMEOS)
-std::string BuildOSSettingsUrl(const std::string& sub_page) {
- std::string os_settings_url = chrome::kChromeUIOSSettingsURL;
- os_settings_url.append(sub_page);
- return os_settings_url;
-}
-#endif
-
void AddBrowserSyncPageStrings(content::WebUIDataSource* html_source) {
static constexpr webui::LocalizedString kLocalizedStrings[] = {
- {"passwordsCheckboxLabel", IDS_SETTINGS_PASSWORDS_CHECKBOX_LABEL},
{"peopleSignInSyncPagePromptSecondaryWithAccount",
IDS_SETTINGS_PEOPLE_SIGN_IN_PROMPT_SECONDARY_WITH_ACCOUNT},
{"peopleSignInSyncPagePromptSecondaryWithNoAccount",
@@ -1586,6 +1369,12 @@ void AddBrowserSyncPageStrings(content::WebUIDataSource* html_source) {
"activityControlsUrlInPrivacyGuide",
chrome::kGoogleAccountActivityControlsURLInPrivacyGuide);
+ html_source->AddLocalizedString(
+ "passwordsCheckboxLabel",
+ base::FeatureList::IsEnabled(syncer::kSyncWebauthnCredentials)
+ ? IDS_SETTINGS_PASSWORDS_AND_PASSKEYS_CHECKBOX_LABEL
+ : IDS_SETTINGS_PASSWORDS_CHECKBOX_LABEL);
+
#if BUILDFLAG(IS_CHROMEOS)
html_source->AddString(
"osSyncSetupSettingsUrl",
@@ -1616,6 +1405,8 @@ void AddSyncControlsStrings(content::WebUIDataSource* html_source) {
{"historyCheckboxLabel", IDS_SETTINGS_HISTORY_CHECKBOX_LABEL},
{"extensionsCheckboxLabel", IDS_SETTINGS_EXTENSIONS_CHECKBOX_LABEL},
{"openTabsCheckboxLabel", IDS_SETTINGS_OPEN_TABS_CHECKBOX_LABEL},
+ {"savedTabGroupsCheckboxLabel",
+ IDS_SETTINGS_SAVED_TAB_GROUPS_CHECKBOX_LABEL},
{"wifiConfigurationsCheckboxLabel",
IDS_SETTINGS_WIFI_CONFIGURATIONS_CHECKBOX_LABEL},
{"syncEverythingCheckboxLabel",
@@ -1624,8 +1415,7 @@ void AddSyncControlsStrings(content::WebUIDataSource* html_source) {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
{"appCheckboxSublabel", IDS_SETTINGS_APPS_CHECKBOX_SUBLABEL},
#endif
- {"enablePaymentsIntegrationCheckboxLabel",
- IDS_AUTOFILL_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL},
+ {"paymentsCheckboxLabel", IDS_SYNC_DATATYPE_PAYMENTS},
{"nonPersonalizedServicesSectionLabel",
IDS_SETTINGS_NON_PERSONALIZED_SERVICES_SECTION_LABEL},
{"customizeSyncLabel", IDS_SETTINGS_CUSTOMIZE_SYNC},
@@ -1665,8 +1455,15 @@ void AddPeopleStrings(content::WebUIDataSource* html_source, Profile* profile) {
{"createShortcutSubtitle", IDS_SETTINGS_CREATE_SHORTCUT_SUBTITLE},
// Color picker strings:
+ {"colorsContainerLabel", IDS_NTP_THEMES_CONTAINER_LABEL},
{"colorPickerLabel", IDS_NTP_CUSTOMIZE_COLOR_PICKER_LABEL},
+ {"defaultColorName", IDS_NTP_CUSTOMIZE_DEFAULT_LABEL},
{"defaultThemeLabel", IDS_NTP_CUSTOMIZE_DEFAULT_LABEL},
+ {"greyDefaultColorName", IDS_NTP_CUSTOMIZE_GREY_DEFAULT_LABEL},
+ {"hueSliderTitle", IDS_NTP_CUSTOMIZE_COLOR_HUE_SLIDER_TITLE},
+ {"mainColorName", IDS_NTP_CUSTOMIZE_MAIN_COLOR_LABEL},
+ {"managedColorsBody", IDS_NTP_THEME_MANAGED_DIALOG_BODY},
+ {"managedColorsTitle", IDS_NTP_THEME_MANAGED_DIALOG_TITLE},
{"themesContainerLabel", IDS_SETTINGS_PICK_A_THEME_COLOR},
{"thirdPartyThemeDescription", IDS_NTP_CUSTOMIZE_3PT_THEME_DESC},
{"uninstallThirdPartyThemeButton", IDS_NTP_CUSTOMIZE_3PT_THEME_UNINSTALL},
@@ -1776,6 +1573,8 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_SAFEBROWSING_ENABLE_REPORTING_DESC},
{"safeBrowsingEnhanced", IDS_SETTINGS_SAFEBROWSING_ENHANCED},
{"safeBrowsingEnhancedDesc", IDS_SETTINGS_SAFEBROWSING_ENHANCED_DESC},
+ {"safeBrowsingEnhancedDescUpdated",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_DESC_UPDATED},
{"safeBrowsingEnhancedExpandA11yLabel",
IDS_SETTINGS_SAFEBROWSING_ENHANCED_EXPAND_ACCESSIBILITY_LABEL},
{"safeBrowsingEnhancedBulOne",
@@ -1788,18 +1587,50 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_SAFEBROWSING_ENHANCED_BULLET_FOUR},
{"safeBrowsingEnhancedBulFive",
IDS_SETTINGS_SAFEBROWSING_ENHANCED_BULLET_FIVE},
+ {"safeBrowsingEnhancedWhenOnLabel",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_WHEN_ON_LABEL},
+ {"safeBrowsingEnhancedWhenOnBulOne",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_WHEN_ON_BULLET_ONE},
+ {"safeBrowsingEnhancedWhenOnBulTwo",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_WHEN_ON_BULLET_TWO},
+ {"safeBrowsingEnhancedWhenOnBulThree",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_WHEN_ON_BULLET_THREE},
+ {"safeBrowsingEnhancedWhenOnBulFour",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_WHEN_ON_BULLET_FOUR},
+ {"safeBrowsingEnhancedWhenOnBulFive",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_WHEN_ON_BULLET_FIVE},
+ {"safeBrowsingEnhancedThingsToConsiderLabel",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_THINGS_TO_CONSIDER_LABEL},
+ {"safeBrowsingEnhancedThingsToConsiderBulOne",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_THINGS_TO_CONSIDER_BULLET_ONE},
+ {"safeBrowsingEnhancedThingsToConsiderBulTwo",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_THINGS_TO_CONSIDER_BULLET_TWO},
+ {"safeBrowsingEnhancedThingsToConsiderBulThree",
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_THINGS_TO_CONSIDER_BULLET_THREE},
{"safeBrowsingStandard", IDS_SETTINGS_SAFEBROWSING_STANDARD},
{"safeBrowsingStandardDesc", IDS_SETTINGS_SAFEBROWSING_STANDARD_DESC},
+ {"safeBrowsingStandardDescUpdated",
+ IDS_SETTINGS_SAFEBROWSING_STANDARD_DESC_UPDATED},
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+ {"safeBrowsingStandardDescUpdatedProxy",
+ IDS_SETTINGS_SAFEBROWSING_STANDARD_DESC_UPDATED_PROXY},
+#endif
{"safeBrowsingStandardExpandA11yLabel",
IDS_SETTINGS_SAFEBROWSING_STANDARD_EXPAND_ACCESSIBILITY_LABEL},
{"safeBrowsingStandardBulOne",
IDS_SETTINGS_SAFEBROWSING_STANDARD_BULLET_ONE},
{"safeBrowsingStandardBulTwo",
IDS_SETTINGS_SAFEBROWSING_STANDARD_BULLET_TWO},
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+ {"safeBrowsingStandardBulTwoProxy",
+ IDS_SETTINGS_SAFEBROWSING_STANDARD_BULLET_TWO_PROXY},
+#endif
{"safeBrowsingStandardReportingLabel",
IDS_SETTINGS_SAFEBROWSING_STANDARD_HELP_IMPROVE},
{"safeBrowsingNone", IDS_SETTINGS_SAFEBROWSING_NONE},
{"safeBrowsingNoneDesc", IDS_SETTINGS_SAFEBROWSING_NONE_DESC},
+ {"safeBrowsingNoneDescUpdated",
+ IDS_SETTINGS_SAFEBROWSING_NONE_DESC_UPDATED},
{"safeBrowsingDisableDialog",
IDS_SETTINGS_SAFEBROWSING_DISABLE_DIALOG_TITLE},
{"safeBrowsingDisableDialogDesc",
@@ -1836,7 +1667,6 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_RECENT_PERMISSIONS_BLOCKED_TWO_ITEMS},
{"recentPermissionBlockedMoreThanTwoItems",
IDS_SETTINGS_RECENT_PERMISSIONS_BLOCKED_MORE_THAN_TWO_ITEMS},
- {"networkPredictionEnabled", IDS_SETTINGS_NETWORK_PREDICTION_ENABLED_LABEL},
{"networkPredictionEnabledDesc",
IDS_SETTINGS_NETWORK_PREDICTION_ENABLED_DESC},
{"networkPredictionEnabledDescCookiesPage",
@@ -1888,8 +1718,24 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source,
html_source->AddString("firstPartySetsLearnMoreURL",
chrome::kFirstPartySetsLearnMoreURL);
- html_source->AddString("safeBrowsingHelpCenterURL",
- chrome::kSafeBrowsingHelpCenterURL);
+ bool are_friendlier_settings_enabled =
+ base::FeatureList::IsEnabled(
+ safe_browsing::kFriendlierSafeBrowsingSettingsEnhancedProtection) &&
+ base::FeatureList::IsEnabled(
+ safe_browsing::kFriendlierSafeBrowsingSettingsStandardProtection);
+ bool are_hash_realtime_lookups_enabled = safe_browsing::hash_realtime_utils::
+ IsHashRealTimeLookupEligibleInSession();
+ html_source->AddString(
+ "safeBrowsingHelpCenterURL",
+ are_friendlier_settings_enabled || are_hash_realtime_lookups_enabled
+ ? chrome::kSafeBrowsingHelpCenterUpdatedURL
+ : chrome::kSafeBrowsingHelpCenterURL);
+
+ html_source->AddString(
+ "enhancedProtectionLearnMoreLabel",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_SAFEBROWSING_ENHANCED_LEARN_MORE_LABEL,
+ base::ASCIIToUTF16(chrome::kSafeBrowsingInChromeHelpCenterURL)));
html_source->AddString("syncAndGoogleServicesLearnMoreURL",
chrome::kSyncAndGoogleServicesLearnMoreURL);
@@ -1905,6 +1751,12 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source,
html_source->AddBoolean(
"driveSuggestAvailable",
base::FeatureList::IsEnabled(omnibox::kDocumentProvider));
+ html_source->AddBoolean(
+ "driveSuggestNoSetting",
+ base::FeatureList::IsEnabled(omnibox::kDocumentProviderNoSetting));
+ html_source->AddBoolean("driveSuggestNoSyncRequirement",
+ base::FeatureList::IsEnabled(
+ omnibox::kDocumentProviderNoSyncRequirement));
bool show_secure_dns = IsSecureDnsAvailable();
bool link_secure_dns = ShouldLinkSecureDnsOsSettings();
@@ -1918,10 +1770,6 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source,
chromeos::settings::mojom::kPrivacyAndSecuritySectionPath);
#endif
- html_source->AddBoolean(
- "showHttpsOnlyModeSetting",
- base::FeatureList::IsEnabled(features::kHttpsOnlyMode));
-
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
html_source->AddBoolean("showChromeRootStoreCertificates",
#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
@@ -1948,10 +1796,6 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source,
html_source->AddString("advancedProtectionURL",
advanced_protection_url.spec());
- html_source->AddBoolean("showPreloadingSubPage",
- base::FeatureList::IsEnabled(
- features::kPreloadingDesktopSettingsSubPage));
-
AddPersonalizationOptionsStrings(html_source);
AddSecureDnsStrings(html_source);
}
@@ -2270,97 +2114,114 @@ void AddPrivacySandboxStrings(content::WebUIDataSource* html_source,
void AddPrivacyGuideStrings(content::WebUIDataSource* html_source) {
static constexpr webui::LocalizedString kLocalizedStrings[] = {
- {"privacyGuideLabel", IDS_SETTINGS_PRIVACY_GUIDE_LABEL},
- {"privacyGuideSublabel", IDS_SETTINGS_PRIVACY_GUIDE_SUBLABEL},
- {"privacyGuidePromoHeader", IDS_SETTINGS_PRIVACY_GUIDE_PROMO_HEADER},
- {"privacyGuidePromoBody", IDS_SETTINGS_PRIVACY_GUIDE_PROMO_BODY},
- {"privacyGuidePromoStartButton",
- IDS_SETTINGS_PRIVACY_GUIDE_PROMO_START_BUTTON},
- {"privacyGuideBackToSettingsAriaLabel",
- IDS_SETTINGS_PRIVACY_GUIDE_BACK_TO_SETTINGS_ARIA_LABEL},
- {"privacyGuideBackToSettingsAriaRoleDescription",
- IDS_SETTINGS_PRIVACY_GUIDE_BACK_TO_SETTINGS_ARIA_ROLE_DESC},
- {"privacyGuideBackButton", IDS_SETTINGS_PRIVACY_GUIDE_BACK_BUTTON},
- {"privacyGuideSteps", IDS_SETTINGS_PRIVACY_GUIDE_STEPS},
- {"privacyGuideNextButton", IDS_SETTINGS_PRIVACY_GUIDE_NEXT_BUTTON},
- {"privacyGuideFeatureDescriptionHeader",
- IDS_SETTINGS_PRIVACY_GUIDE_FEATURE_DESCRIPTION_HEADER},
- {"privacyGuideThingsToConsider",
- IDS_SETTINGS_PRIVACY_GUIDE_THINGS_TO_CONSIDER},
- {"privacyGuideWelcomeCardHeader",
- IDS_SETTINGS_PRIVACY_GUIDE_WELCOME_CARD_HEADER},
- {"privacyGuideWelcomeCardSubHeader",
- IDS_SETTINGS_PRIVACY_GUIDE_WELCOME_CARD_SUB_HEADER},
- {"privacyGuideCompletionCardHeader",
- IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_HEADER},
- {"privacyGuideCompletionCardSubHeader",
- IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_SUB_HEADER},
- {"privacyGuideCompletionCardSubHeaderNoLinks",
- IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_SUB_HEADER_NO_LINKS},
- {"privacyGuideCompletionCardLeaveButton",
- IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_LEAVE_BUTTON},
- {"privacyGuideCompletionCardPrivacySandboxLabel",
- IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_LABEL},
- {"privacyGuideCompletionCardPrivacySandboxSubLabel",
- IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL},
- {"privacyGuideCompletionCardWaaLabel",
- IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_WAA_LABEL},
- {"privacyGuideCompletionCardWaaSubLabel",
- IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_WAA_SUB_LABEL},
- {"privacyGuideMsbbCardHeader",
- IDS_SETTINGS_PRIVACY_GUIDE_MSBB_CARD_HEADER},
- {"privacyGuideMsbbFeatureDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_MSBB_FEATURE_DESCRIPTION1},
- {"privacyGuideMsbbFeatureDescription2",
- IDS_SETTINGS_PRIVACY_GUIDE_MSBB_FEATURE_DESCRIPTION2},
- {"privacyGuideMsbbPrivacyDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_MSBB_PRIVACY_DESCRIPTION1},
- {"privacyGuideMsbbPrivacyDescription2",
- IDS_SETTINGS_PRIVACY_GUIDE_MSBB_PRIVACY_DESCRIPTION2},
- {"privacyGuideHistorySyncCardHeader",
- IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_CARD_HEADER},
- {"privacyGuideHistorySyncSettingLabel",
- IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_SETTING_LABEL},
- {"privacyGuideHistorySyncFeatureDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_FEATURE_DESCRIPTION1},
- {"privacyGuideHistorySyncFeatureDescription2",
- IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_FEATURE_DESCRIPTION2},
- {"privacyGuideHistorySyncPrivacyDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_PRIVACY_DESCRIPTION1},
- {"privacyGuideCookiesCardHeader",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_HEADER},
- {"privacyGuideCookiesCardBlockTpcIncognitoSubheader",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_SUBHEADER},
- {"privacyGuideCookiesCardBlockTpcIncognitoFeatureDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_FEATURE_DESCRIPTION1},
- {"privacyGuideCookiesCardBlockTpcIncognitoFeatureDescription2",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_FEATURE_DESCRIPTION2},
- {"privacyGuideCookiesCardBlockTpcIncognitoPrivacyDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_PRIVACY_DESCRIPTION1},
- {"privacyGuideCookiesCardBlockTpcIncognitoPrivacyDescription2",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_PRIVACY_DESCRIPTION2},
- {"privacyGuideCookiesCardBlockTpcSubheader",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_SUBHEADER},
- {"privacyGuideCookiesCardBlockTpcFeatureDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_FEATURE_DESCRIPTION1},
- {"privacyGuideCookiesCardBlockTpcFeatureDescription2",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_FEATURE_DESCRIPTION2},
- {"privacyGuideCookiesCardBlockTpcPrivacyDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_PRIVACY_DESCRIPTION1},
- {"privacyGuideSafeBrowsingCardHeader",
- IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_HEADER},
- {"privacyGuideSafeBrowsingCardEnhancedProtectionPrivacyDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_ENHANCED_PROTECTION_PRIVACY_DESCRIPTION1},
- {"privacyGuideSafeBrowsingCardEnhancedProtectionPrivacyDescription2",
- IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_ENHANCED_PROTECTION_PRIVACY_DESCRIPTION2},
- {"privacyGuideSafeBrowsingCardEnhancedProtectionPrivacyDescription3",
- IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_ENHANCED_PROTECTION_PRIVACY_DESCRIPTION3},
- {"privacyGuideSafeBrowsingCardStandardProtectionFeatureDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_FEATURE_DESCRIPTION1},
- {"privacyGuideSafeBrowsingCardStandardProtectionFeatureDescription2",
- IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_FEATURE_DESCRIPTION2},
- {"privacyGuideSafeBrowsingCardStandardProtectionPrivacyDescription1",
- IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_PRIVACY_DESCRIPTION1},
+ {"privacyGuideLabel", IDS_SETTINGS_PRIVACY_GUIDE_LABEL},
+ {"privacyGuideSublabel", IDS_SETTINGS_PRIVACY_GUIDE_SUBLABEL},
+ {"privacyGuidePromoHeader", IDS_SETTINGS_PRIVACY_GUIDE_PROMO_HEADER},
+ {"privacyGuidePromoBody", IDS_SETTINGS_PRIVACY_GUIDE_PROMO_BODY},
+ {"privacyGuidePromoStartButton",
+ IDS_SETTINGS_PRIVACY_GUIDE_PROMO_START_BUTTON},
+ {"privacyGuideBackToSettingsAriaLabel",
+ IDS_SETTINGS_PRIVACY_GUIDE_BACK_TO_SETTINGS_ARIA_LABEL},
+ {"privacyGuideBackToSettingsAriaRoleDescription",
+ IDS_SETTINGS_PRIVACY_GUIDE_BACK_TO_SETTINGS_ARIA_ROLE_DESC},
+ {"privacyGuideBackButton", IDS_SETTINGS_PRIVACY_GUIDE_BACK_BUTTON},
+ {"privacyGuideSteps", IDS_SETTINGS_PRIVACY_GUIDE_STEPS},
+ {"privacyGuideNextButton", IDS_SETTINGS_PRIVACY_GUIDE_NEXT_BUTTON},
+ {"privacyGuideFeatureDescriptionHeader",
+ IDS_SETTINGS_PRIVACY_GUIDE_FEATURE_DESCRIPTION_HEADER},
+ {"privacyGuideThingsToConsider",
+ IDS_SETTINGS_PRIVACY_GUIDE_THINGS_TO_CONSIDER},
+ {"privacyGuideWelcomeCardHeader",
+ IDS_SETTINGS_PRIVACY_GUIDE_WELCOME_CARD_HEADER},
+ {"privacyGuideWelcomeCardSubHeader",
+ IDS_SETTINGS_PRIVACY_GUIDE_WELCOME_CARD_SUB_HEADER},
+ {"privacyGuideCompletionCardHeader",
+ IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_HEADER},
+ {"privacyGuideCompletionCardSubHeader",
+ IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_SUB_HEADER},
+ {"privacyGuideCompletionCardSubHeaderNoLinks",
+ IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_SUB_HEADER_NO_LINKS},
+ {"privacyGuideCompletionCardLeaveButton",
+ IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_LEAVE_BUTTON},
+ {"privacyGuideCompletionCardPrivacySandboxLabel",
+ IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_LABEL},
+ {"privacyGuideCompletionCardPrivacySandboxSubLabel",
+ IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL},
+ {"privacyGuideCompletionCardWaaLabel",
+ IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_WAA_LABEL},
+ {"privacyGuideCompletionCardWaaSubLabel",
+ IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_WAA_SUB_LABEL},
+ {"privacyGuideMsbbCardHeader", IDS_SETTINGS_PRIVACY_GUIDE_MSBB_CARD_HEADER},
+ {"privacyGuideMsbbFeatureDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_MSBB_FEATURE_DESCRIPTION1},
+ {"privacyGuideMsbbFeatureDescription2",
+ IDS_SETTINGS_PRIVACY_GUIDE_MSBB_FEATURE_DESCRIPTION2},
+ {"privacyGuideMsbbFeatureDescription3",
+ IDS_SETTINGS_PRIVACY_GUIDE_MSBB_FEATURE_DESCRIPTION3},
+ {"privacyGuideMsbbPrivacyDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_MSBB_PRIVACY_DESCRIPTION1},
+ {"privacyGuideMsbbPrivacyDescription2",
+ IDS_SETTINGS_PRIVACY_GUIDE_MSBB_PRIVACY_DESCRIPTION2},
+ {"privacyGuideHistorySyncCardHeader",
+ IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_CARD_HEADER},
+ {"privacyGuideHistorySyncSettingLabel",
+ IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_SETTING_LABEL},
+ {"privacyGuideHistorySyncFeatureDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_FEATURE_DESCRIPTION1},
+ {"privacyGuideHistorySyncFeatureDescription2",
+ IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_FEATURE_DESCRIPTION2},
+ {"privacyGuideHistorySyncPrivacyDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_HISTORY_SYNC_PRIVACY_DESCRIPTION1},
+ {"privacyGuideCookiesCardHeader",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_HEADER},
+ {"privacyGuideCookiesCardBlockTpcIncognitoSubheader",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_SUBHEADER},
+ {"privacyGuideCookiesCardBlockTpcIncognitoFeatureDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_FEATURE_DESCRIPTION1},
+ {"privacyGuideCookiesCardBlockTpcIncognitoFeatureDescription2",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_FEATURE_DESCRIPTION2},
+ {"privacyGuideCookiesCardBlockTpcIncognitoPrivacyDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_PRIVACY_DESCRIPTION1},
+ {"privacyGuideCookiesCardBlockTpcIncognitoPrivacyDescription2",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_INCOGNITO_PRIVACY_DESCRIPTION2},
+ {"privacyGuideCookiesCardBlockTpcSubheader",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_SUBHEADER},
+ {"privacyGuideCookiesCardBlockTpcFeatureDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_FEATURE_DESCRIPTION1},
+ {"privacyGuideCookiesCardBlockTpcFeatureDescription2",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_FEATURE_DESCRIPTION2},
+ {"privacyGuideCookiesCardBlockTpcPrivacyDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_COOKIES_CARD_BLOCK_TPC_PRIVACY_DESCRIPTION1},
+ {"privacyGuideSafeBrowsingCardHeader",
+ IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_HEADER},
+ {"privacyGuideSafeBrowsingCardEnhancedProtectionPrivacyDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_ENHANCED_PROTECTION_PRIVACY_DESCRIPTION1},
+ {"privacyGuideSafeBrowsingCardEnhancedProtectionPrivacyDescription2",
+ IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_ENHANCED_PROTECTION_PRIVACY_DESCRIPTION2},
+ {"privacyGuideSafeBrowsingCardEnhancedProtectionPrivacyDescription3",
+ IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_ENHANCED_PROTECTION_PRIVACY_DESCRIPTION3},
+ {"privacyGuideSafeBrowsingCardStandardProtectionFeatureDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_FEATURE_DESCRIPTION1},
+ {"privacyGuideSafeBrowsingCardStandardProtectionFeatureDescription2",
+ IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_FEATURE_DESCRIPTION2},
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+ {"privacyGuideSafeBrowsingCardStandardProtectionFeatureDescription2Proxy",
+ IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_FEATURE_DESCRIPTION2_PROXY},
+#endif
+ {"privacyGuideSafeBrowsingCardStandardProtectionPrivacyDescription1",
+ IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_PRIVACY_DESCRIPTION1},
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+ {"privacyGuideSafeBrowsingCardStandardProtectionPrivacyDescription1Proxy",
+ IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_PRIVACY_DESCRIPTION1_PROXY},
+#endif
+ {"privacyGuideSearchSuggestionsCardHeader",
+ IDS_SETTINGS_PRIVACY_GUIDE_SEARCH_SUGGESTIONS_CARD_HEADER},
+ {"privacyGuideSearchSuggestionsFeatureDescription1",
+ IDS_SETTINGS_PRIVACY_SEARCH_SUGGESTIONS_FEATURE_DESCRIPTION1},
+ {"privacyGuideSearchSuggestionsPrivacyDescription1",
+ IDS_SETTINGS_PRIVACY_SEARCH_SUGGESTIONS_PRIVACY_DESCRIPTION1},
+ {"privacyGuideSearchSuggestionsPrivacyDescription2",
+ IDS_SETTINGS_PRIVACY_SEARCH_SUGGESTIONS_PRIVACY_DESCRIPTION2},
};
html_source->AddLocalizedStrings(kLocalizedStrings);
}
@@ -2453,6 +2314,30 @@ void AddSafetyCheckStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_TOAST_LABEL},
{"safetyCheckUnusedSitePermissionsUndoLabel",
IDS_SETTINGS_SAFETY_CHECK_TOAST_UNDO_BUTTON_LABEL},
+ {"safetyCheckUnusedSitePermissionsSettingLabel",
+ IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_SETTING_LABEL},
+ {"safetyCheckUnusedSitePermissionsSettingSublabel",
+ IDS_SETTINGS_SAFETY_CHECK_UNUSED_SITE_PERMISSIONS_SETTING_SUBLABEL},
+ };
+ html_source->AddLocalizedStrings(kLocalizedStrings);
+}
+
+void AddSafetyHubStrings(content::WebUIDataSource* html_source) {
+ static constexpr webui::LocalizedString kLocalizedStrings[] = {
+ {"safetyHub", IDS_SETTINGS_SAFETY_HUB},
+ {"safetyHubEntryPointNothingToDo",
+ IDS_SETTINGS_SAFETY_HUB_ENTRY_POINT_NOTHING_TO_DO},
+ {"safetyHubEntryPointButton", IDS_SETTINGS_SAFETY_HUB_ENTRY_POINT_BUTTON},
+ {"safetyHubPageCardSectionHeader",
+ IDS_SETTINGS_SAFETY_HUB_PAGE_CARD_SECTION_HEADER},
+ {"safetyHubPageModuleSectionHeader",
+ IDS_SETTINGS_SAFETY_HUB_PAGE_MODULE_SECTION_HEADER},
+ {"safetyHubPageUserEduSectionHeader",
+ IDS_SETTINGS_SAFETY_HUB_PAGE_USER_EDU_SECTION_HEADER},
+ {"safetyHubEmptyStateModuleHeader",
+ IDS_SETTINGS_SAFETY_HUB_EMPTY_STATE_MODULE_HEADER},
+ {"safetyHubEmptyStateModuleSubheader",
+ IDS_SETTINGS_SAFETY_HUB_EMPTY_STATE_MODULE_SUBHEADER},
};
html_source->AddLocalizedStrings(kLocalizedStrings);
}
@@ -2705,6 +2590,10 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
IDS_SITE_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS},
{"siteSettingsAutomaticDownloadsMidSentence",
IDS_SITE_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS_MID_SENTENCE},
+ {"siteSettingsAutoPictureInPicture",
+ IDS_SITE_SETTINGS_TYPE_AUTO_PICTURE_IN_PICTURE},
+ {"siteSettingsAutoPictureInPictureMidSentence",
+ IDS_SITE_SETTINGS_TYPE_AUTO_PICTURE_IN_PICTURE_MID_SENTENCE},
{"siteSettingsBackgroundSync", IDS_SITE_SETTINGS_TYPE_BACKGROUND_SYNC},
{"siteSettingsBackgroundSyncMidSentence",
IDS_SITE_SETTINGS_TYPE_BACKGROUND_SYNC_MID_SENTENCE},
@@ -2870,10 +2759,10 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
{"siteSettingsCookieRemoveSite",
IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_SITE},
{"siteSettingsDelete", IDS_SETTINGS_SITE_SETTINGS_DELETE},
- {"siteSettingsClearAllStorageDialogTitle",
- IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_DIALOG_TITLE},
- {"siteSettingsClearDisplayedStorageDialogTitle",
- IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_DIALOG_TITLE},
+ {"siteSettingsDeleteAllStorageDialogTitle",
+ IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_DIALOG_TITLE},
+ {"siteSettingsDeleteDisplayedStorageDialogTitle",
+ IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_DIALOG_TITLE},
{"siteSettingsFirstPartySetsLearnMore",
IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_LEARN_MORE},
{"siteSettingsFirstPartySetsLearnMoreAccessibility",
@@ -2882,18 +2771,18 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_DESCRIPTION},
{"siteSettingsClearDisplayedStorageDescription",
IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_DESCRIPTION},
- {"siteSettingsClearAllStorageLabel",
- IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_LABEL},
- {"siteSettingsClearDisplayedStorageLabel",
- IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_LABEL},
- {"siteSettingsClearAllStorageConfirmation",
- IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_CONFIRMATION},
- {"siteSettingsClearDisplayedStorageConfirmation",
- IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION},
- {"siteSettingsClearAllStorageConfirmationInstalled",
- IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_CONFIRMATION_INSTALLED},
- {"siteSettingsClearDisplayedStorageConfirmationInstalled",
- IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION_INSTALLED},
+ {"siteSettingsDeleteAllStorageLabel",
+ IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_LABEL},
+ {"siteSettingsDeleteDisplayedStorageLabel",
+ IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_LABEL},
+ {"siteSettingsDeleteAllStorageConfirmation",
+ IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_CONFIRMATION},
+ {"siteSettingsDeleteDisplayedStorageConfirmation",
+ IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_CONFIRMATION},
+ {"siteSettingsDeleteAllStorageConfirmationInstalled",
+ IDS_SETTINGS_SITE_SETTINGS_DELETE_ALL_STORAGE_CONFIRMATION_INSTALLED},
+ {"siteSettingsDeleteDisplayedStorageConfirmationInstalled",
+ IDS_SETTINGS_SITE_SETTINGS_DELETE_DISPLAYED_STORAGE_CONFIRMATION_INSTALLED},
{"siteSettingsClearAllStorageSignOut",
IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_SIGN_OUT},
{"siteSettingsClearDisplayedStorageSignOut",
@@ -2904,23 +2793,22 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_MORE_ACTIONS_TITLE},
{"firstPartySetsShowRelatedSitesButton",
IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_SHOW_RELATED_SITES_BUTTON},
- {"firstPartySetsSiteClearStorageButton",
- IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_SITE_CLEAR_STORAGE_BUTTON},
+ {"firstPartySetsSiteDeleteStorageButton",
+ IDS_SETTINGS_SITE_SETTINGS_FIRST_PARTY_SETS_SITE_DELETE_STORAGE_BUTTON},
{"siteSettingsSiteClearStorage",
IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE},
{"siteSettingsSiteClearStorageConfirmation",
IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_CONFIRMATION},
{"siteSettingsSiteClearStorageConfirmationNew",
IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_CONFIRMATION_NEW},
- {"siteSettingsSiteClearStorageDialogTitle",
- IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_DIALOG_TITLE},
+ {"siteSettingsSiteDeleteStorageDialogTitle",
+ IDS_SETTINGS_SITE_SETTINGS_SITE_DELETE_STORAGE_DIALOG_TITLE},
{"siteSettingsSiteClearStorageSignOut",
IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_SIGN_OUT},
- {"siteSettingsSiteClearStorageOfflineData",
- IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_STORAGE_OFFLINE_DATA},
+ {"siteSettingsSiteDeleteStorageOfflineData",
+ IDS_SETTINGS_SITE_SETTINGS_SITE_DELETE_STORAGE_OFFLINE_DATA},
{"siteSettingsRemoveSiteAdPersonalization",
IDS_SETTINGS_SITE_SETTINGS_REMOVE_SITE_AD_PERSONALIZATION},
- {"siteSettingsSiteGroupDelete", IDS_SETTINGS_SITE_SETTINGS_GROUP_DELETE},
{"siteSettingsSiteGroupDeleteOfflineData",
IDS_SETTINGS_SITE_SETTINGS_SITE_GROUP_DELETE_OFFLINE_DATA},
{"siteSettingsSiteResetAll", IDS_SETTINGS_SITE_SETTINGS_SITE_RESET_ALL},
@@ -2994,6 +2882,16 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_ALLOWED_EXCEPTIONS},
{"siteSettingsAutomaticDownloadsBlockedExceptions",
IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS_BLOCKED_EXCEPTIONS},
+ {"siteSettingsAutoPictureInPictureDescription",
+ IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_DESCRIPTION},
+ {"siteSettingsAutoPictureInPictureAllowed",
+ IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_ALLOWED},
+ {"siteSettingsAutoPictureInPictureBlocked",
+ IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_BLOCKED},
+ {"siteSettingsAutoPictureInPictureAllowedExceptions",
+ IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_ALLOWED_EXCEPTIONS},
+ {"siteSettingsAutoPictureInPictureBlockedExceptions",
+ IDS_SETTINGS_SITE_SETTINGS_AUTO_PICTURE_IN_PICTURE_BLOCKED_EXCEPTIONS},
{"siteSettingsBackgroundSyncDescription",
IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_DESCRIPTION},
{"siteSettingsBackgroundSyncAllowed",
@@ -3272,8 +3170,8 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_ALLOWED_SUB_LABEL},
{"siteSettingsSiteDataBlockedSubLabel",
IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_BLOCKED_SUB_LABEL},
- {"siteSettingsSiteDataClearOnExitSubLabel",
- IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_CLEAR_ON_EXIT_SUB_LABEL},
+ {"siteSettingsSiteDataDeleteOnExitSubLabel",
+ IDS_SETTINGS_SITE_SETTINGS_PAGE_SITE_DATA_DELETE_ON_EXIT_SUB_LABEL},
{"siteSettingsAntiAbuse", IDS_SITE_SETTINGS_TYPE_ANTI_ABUSE},
{"siteSettingsAntiAbuseDescription", IDS_SETTINGS_ANTI_ABUSE_DESCRIPTION},
{"siteSettingsAntiAbuseEnabledSubLabel",
@@ -3335,10 +3233,6 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
features::kWebBluetoothNewPermissionsBackend));
html_source->AddBoolean(
- "enableMathMLCore",
- base::FeatureList::IsEnabled(blink::features::kMathMLCore));
-
- html_source->AddBoolean(
"showPersistentPermissions",
base::FeatureList::IsEnabled(
features::kFileSystemAccessPersistentPermissions));
@@ -3359,6 +3253,10 @@ void AddStorageAccessStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_STORAGE_ACCESS_ALLOWED_EXCEPTIONS},
{"storageAccessBlockedExceptions",
IDS_SETTINGS_STORAGE_ACCESS_BLOCKED_EXCEPTIONS},
+ {"storageAccessResetAll", IDS_SETTINGS_STORAGE_ACCESS_RESET_ALL},
+ {"storageAccessResetSite", IDS_SETTINGS_STORAGE_ACCESS_RESET_SITE},
+ {"storageAccessOpenExpand", IDS_SETTINGS_STORAGE_ACCESS_OPEN_EXPAND},
+ {"storageAccessCloseExpand", IDS_SETTINGS_STORAGE_ACCESS_CLOSE_EXPAND},
};
html_source->AddLocalizedStrings(kLocalizedStrings);
}
@@ -3390,8 +3288,8 @@ void AddSiteDataPageStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_SITE_DATA_PAGE_CUSTOMIZED_BEHAVIOR_DESCRIPTION},
{"siteDataPageAllowExceptionsSubHeading",
IDS_SETTINGS_SITE_DATA_PAGE_ALLOW_EXCEPTIONS_SUB_HEADING},
- {"siteDataPageClearOnExitExceptionsSubHeading",
- IDS_SETTINGS_SITE_DATA_PAGE_CLEAR_ON_EXIT_EXCEPTIONS_SUB_HEADING},
+ {"siteDataPageDeleteOnExitExceptionsSubHeading",
+ IDS_SETTINGS_SITE_DATA_PAGE_DELETE_ON_EXIT_EXCEPTIONS_SUB_HEADING},
{"siteDataPageBlockExceptionsSubHeading",
IDS_SETTINGS_SITE_DATA_PAGE_BLOCK_EXCEPTIONS_SUB_HEADING},
{"siteDataPageBlockConfirmDialogTitle",
@@ -3610,6 +3508,7 @@ void AddLocalizedStrings(content::WebUIDataSource* html_source,
AddPrivacyGuideStrings(html_source);
AddPrivacyStrings(html_source, profile);
AddSafetyCheckStrings(html_source);
+ AddSafetyHubStrings(html_source);
AddResetStrings(html_source, profile);
AddSearchEnginesStrings(html_source);
AddSearchInSettingsStrings(html_source);
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc
index f9f09c625df..673e25b2261 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc
@@ -24,7 +24,7 @@
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "chrome/browser/ui/webui/theme_source.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
@@ -156,8 +156,6 @@ void ManageProfileHandler::HandleSetProfileIconToGaiaAvatar(
// below.
ProfileMetrics::LogProfileAvatarSelection(SIZE_MAX);
}
-
- ProfileMetrics::LogProfileUpdate(profile_->GetPath());
}
void ManageProfileHandler::HandleSetProfileIconToDefaultAvatar(
@@ -179,8 +177,6 @@ void ManageProfileHandler::HandleSetProfileName(const base::Value::List& args) {
base::TrimWhitespace(new_profile_name, base::TRIM_ALL, &new_profile_name);
CHECK(!new_profile_name.empty());
profiles::UpdateProfileName(profile_, new_profile_name);
-
- ProfileMetrics::LogProfileUpdate(profile_->GetPath());
}
void ManageProfileHandler::HandleRequestProfileShortcutStatus(
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler_unittest.cc
index 8c989a928f8..dc129a6908d 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler_unittest.cc
@@ -8,7 +8,7 @@
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
index c205a2310e3..b6b8a979bb3 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
@@ -15,10 +15,14 @@
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
+#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
#include "chrome/browser/webauthn/cablev2_devices.h"
#include "chrome/browser/webauthn/local_credential_management.h"
+#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
@@ -1128,6 +1132,10 @@ void PasskeysHandler::RegisterMessages() {
base::BindRepeating(&PasskeysHandler::HandleHasPasskeys,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "passkeysManagePasskeys",
+ base::BindRepeating(&PasskeysHandler::HandleManagePasskeys,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"passkeysEnumerate",
base::BindRepeating(&PasskeysHandler::HandleEnumerate,
base::Unretained(this)));
@@ -1160,6 +1168,30 @@ void PasskeysHandler::OnHasPasskeysComplete(std::string callback_id,
base::Value(has_passkeys));
}
+void PasskeysHandler::HandleManagePasskeys(const base::Value::List& args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK_EQ(0u, args.size());
+
+ AllowJavascript();
+
+#if BUILDFLAG(IS_WIN)
+ auto* windows_api = device::WinWebAuthnApi::GetDefault();
+ // webauthn.dll version six includes management support, so if at least that
+ // version is found then Windows does management natively.
+ constexpr int kWebAuthnDLLWithManagementSupport = 6;
+ if (windows_api->IsAvailable() &&
+ windows_api->Version() >= kWebAuthnDLLWithManagementSupport) {
+ platform_util::OpenExternal(GURL("ms-settings:savedpasskeys"));
+ return;
+ }
+#endif
+
+ // If no system management exists, fall back to Chrome's own settings UI.
+ chrome::ShowSettingsSubPage(
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()),
+ chrome::kPasskeysSubPage);
+}
+
void PasskeysHandler::HandleEnumerate(const base::Value::List& args) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_EQ(1u, args.size());
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.h
index 6d772176b77..2d7707d2149 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/settings_security_key_handler.h
@@ -293,6 +293,8 @@ class PasskeysHandler : public SettingsPageUIHandler {
void HandleHasPasskeys(const base::Value::List& args);
void OnHasPasskeysComplete(std::string callback_id, bool has_passkeys);
+ void HandleManagePasskeys(const base::Value::List& args);
+
void HandleEnumerate(const base::Value::List& args);
void DoEnumerate(std::string callback_id);
void OnEnumerateComplete(
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_ui.cc b/chromium/chrome/browser/ui/webui/settings/settings_ui.cc
index 6003e415372..e60bce2c707 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chromium/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -19,6 +19,8 @@
#include "chrome/browser/commerce/shopping_service_factory.h"
#include "chrome/browser/download/bubble/download_bubble_prefs.h"
#include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
+#include "chrome/browser/performance_manager/public/user_tuning/user_tuning_utils.h"
+#include "chrome/browser/preloading/preloading_features.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service_factory.h"
#include "chrome/browser/profiles/profile.h"
@@ -30,6 +32,7 @@
#include "chrome/browser/ui/managed_ui.h"
#include "chrome/browser/ui/passwords/ui_utils.h"
#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/webui/cr_components/customize_color_scheme_mode/customize_color_scheme_mode_handler.h"
#include "chrome/browser/ui/webui/extension_control_handler.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/browser/ui/webui/managed_ui_handler.h"
@@ -84,6 +87,7 @@
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_utils.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/sync/base/features.h"
@@ -94,6 +98,7 @@
#include "crypto/crypto_buildflags.h"
#include "printing/buildflags/buildflags.h"
#include "services/network/public/cpp/features.h"
+#include "third_party/blink/public/common/features.h"
#include "ui/base/interaction/element_identifier.h"
#if !BUILDFLAG(OPTIMIZE_WEBUI)
@@ -142,7 +147,9 @@
#include "components/user_manager/user.h"
#include "ui/base/ui_base_features.h"
#else // !BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/search/background/ntp_custom_background_service_factory.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
+#include "chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h"
#include "chrome/browser/ui/webui/customize_themes/chrome_customize_themes_handler.h"
#include "chrome/browser/ui/webui/settings/captions_handler.h"
#include "chrome/browser/ui/webui/settings/settings_default_browser_handler.h"
@@ -168,6 +175,10 @@
#include "components/password_manager/core/common/password_manager_pref_names.h"
#endif
+#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/ui/webui/settings/mac_system_settings_handler.h"
+#endif
+
namespace settings {
// static
@@ -246,11 +257,7 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
AddSettingsPageUIHandler(
std::make_unique<SecurityKeysBioEnrollmentHandler>());
AddSettingsPageUIHandler(std::make_unique<SecurityKeysPhonesHandler>());
-
- if (base::FeatureList::IsEnabled(
- password_manager::features::kPasswordManagerRedesign)) {
- AddSettingsPageUIHandler(std::make_unique<PasswordManagerHandler>());
- }
+ AddSettingsPageUIHandler(std::make_unique<PasswordManagerHandler>());
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
AddSettingsPageUIHandler(std::make_unique<PasskeysHandler>());
#endif
@@ -269,6 +276,10 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
#endif
+#if BUILDFLAG(IS_MAC)
+ AddSettingsPageUIHandler(std::make_unique<MacSystemSettingsHandler>());
+#endif
+
#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
bool has_incompatible_applications =
IncompatibleApplicationsUpdater::HasCachedApplications();
@@ -290,29 +301,6 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
"turnOffSyncAllowedForManagedProfiles",
base::FeatureList::IsEnabled(kDisallowManagedProfileSignout));
- const bool enable_new_password_manager_page = base::FeatureList::IsEnabled(
- password_manager::features::kPasswordManagerRedesign);
-
- // Turn-off all Password related features when kPasswordManagerRedesign is on.
- html_source->AddBoolean(
- "enablePasswordsImportM2",
- !enable_new_password_manager_page &&
- base::FeatureList::IsEnabled(
- password_manager::features::kPasswordsImportM2));
-
- html_source->AddBoolean(
- "enablePasswordViewPage",
- !enable_new_password_manager_page &&
- base::FeatureList::IsEnabled(syncer::kPasswordNotesWithBackup));
-
- html_source->AddBoolean("enableSendPasswords",
- !enable_new_password_manager_page &&
- base::FeatureList::IsEnabled(
- password_manager::features::kSendPasswords));
-
- html_source->AddBoolean("enableNewPasswordManagerPage",
- enable_new_password_manager_page);
-
commerce::ShoppingService* shopping_service =
commerce::ShoppingServiceFactory::GetForBrowserContext(profile);
html_source->AddBoolean("changePriceEmailNotificationsEnabled",
@@ -322,12 +310,6 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
->FetchPriceEmailPref();
}
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
- html_source->AddBoolean(
- "enableDesktopDetailedLanguageSettings",
- base::FeatureList::IsEnabled(language::kDesktopDetailedLanguageSettings));
-#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
-
#if BUILDFLAG(IS_CHROMEOS_ASH)
html_source->AddBoolean(
"userCannotManuallyEnterPassword",
@@ -352,6 +334,14 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
!chrome::ShouldDisplayManagedUi(profile) && !profile->IsChild();
html_source->AddBoolean("showPrivacyGuide", show_privacy_guide);
+ html_source->AddBoolean("enablePrivacyGuide3", base::FeatureList::IsEnabled(
+ features::kPrivacyGuide3));
+
+ html_source->AddBoolean(
+ "enablePrivacyGuidePreload",
+ base::FeatureList::IsEnabled(features::kPrivacyGuidePreload) &&
+ base::FeatureList::IsEnabled(features::kPrivacyGuide3));
+
html_source->AddBoolean(
"enableExtendedSettingsDescriptions",
base::FeatureList::IsEnabled(features::kExtendedSettingsDescriptions));
@@ -364,8 +354,26 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
"enableEsbCollapse",
safe_browsing::kEsbIphBubbleAndCollapseSettingsEnableCollapse.Get());
- html_source->AddBoolean("downloadBubbleEnabled",
- download::IsDownloadBubbleEnabled(profile));
+ html_source->AddBoolean(
+ "enableFriendlierSafeBrowsingSettings",
+ base::FeatureList::IsEnabled(
+ safe_browsing::kFriendlierSafeBrowsingSettingsEnhancedProtection) &&
+ base::FeatureList::IsEnabled(
+ safe_browsing::
+ kFriendlierSafeBrowsingSettingsStandardProtection));
+
+ html_source->AddBoolean("enableHashPrefixRealTimeLookups",
+ safe_browsing::hash_realtime_utils::
+ IsHashRealTimeLookupEligibleInSession());
+
+ html_source->AddBoolean(
+ "enablePageContentSetting",
+ base::FeatureList::IsEnabled(features::kPageContentOptIn));
+
+ html_source->AddBoolean(
+ "downloadBubblePartialViewControlledByPref",
+ download::IsDownloadBubbleEnabled(profile) &&
+ download::IsDownloadBubblePartialViewControlledByPref());
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
html_source->AddBoolean(
@@ -386,38 +394,9 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
// Add a handler to provide pluralized strings.
auto plural_string_handler = std::make_unique<PluralStringHandler>();
- plural_string_handler->AddLocalizedString(
- "compromisedPasswords", IDS_SETTINGS_COMPROMISED_PASSWORDS_COUNT);
- plural_string_handler->AddLocalizedString(
- "insecurePasswords", IDS_SETTINGS_INSECURE_PASSWORDS_COUNT);
- plural_string_handler->AddLocalizedString("weakPasswords",
- IDS_SETTINGS_WEAK_PASSWORDS_COUNT);
plural_string_handler->AddLocalizedString("securityKeysNewPIN",
IDS_SETTINGS_SECURITY_KEYS_NEW_PIN);
plural_string_handler->AddLocalizedString(
- "movePasswordsToAccount",
- IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT);
- plural_string_handler->AddLocalizedString(
- "safetyCheckPasswordsCompromised",
- IDS_SETTINGS_COMPROMISED_PASSWORDS_COUNT_SHORT);
- plural_string_handler->AddLocalizedString(
- "safetyCheckPasswordsWeak", IDS_SETTINGS_WEAK_PASSWORDS_COUNT_SHORT);
- plural_string_handler->AddLocalizedString(
- "importPasswordsSuccessSummaryDevice",
- IDS_SETTINGS_PASSWORDS_IMPORT_SUCCESS_SUMMARY_DEVICE);
- plural_string_handler->AddLocalizedString(
- "importPasswordsConflictsTitle",
- IDS_SETTINGS_PASSWORDS_IMPORT_CONFLICTS_TITLE);
- plural_string_handler->AddLocalizedString(
- "importPasswordsSuccessSummaryAccount",
- IDS_SETTINGS_PASSWORDS_IMPORT_SUCCESS_SUMMARY_ACCOUNT);
- plural_string_handler->AddLocalizedString(
- "importPasswordsBadRowsFormat",
- IDS_SETTINGS_PASSWORDS_IMPORT_BAD_ROWS_FORMAT);
- plural_string_handler->AddLocalizedString(
- "importPasswordsFailuresSummary",
- IDS_SETTINGS_PASSWORDS_IMPORT_FAILURES_SUMMARY);
- plural_string_handler->AddLocalizedString(
"safetyCheckExtensionsReviewLabel",
IDS_SETTINGS_SAFETY_CHECK_REVIEW_EXTENSIONS);
plural_string_handler->AddLocalizedString(
@@ -507,6 +486,11 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
html_source->AddBoolean("enableSafetyHub",
base::FeatureList::IsEnabled(features::kSafetyHub));
+ html_source->AddBoolean("is3pcdCookieSettingsRedesignEnabled",
+ base::FeatureList::IsEnabled(
+ content_settings::features::
+ kThirdPartyCookieDeprecationCookieSettings));
+
// Performance
AddSettingsPageUIHandler(std::make_unique<PerformanceHandler>());
html_source->AddBoolean(
@@ -517,12 +501,23 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
"isDiscardExceptionsImprovementsEnabled",
base::FeatureList::IsEnabled(
performance_manager::features::kDiscardExceptionsImprovements));
+ html_source->AddBoolean("isPerformanceSettingsPreloadingSubpageEnabled",
+ base::FeatureList::IsEnabled(
+ features::kPerformanceSettingsPreloadingSubpage));
+ html_source->AddBoolean(
+ "isBatterySaverModeManagedByOS",
+ performance_manager::user_tuning::IsBatterySaverModeManagedByOS());
html_source->AddBoolean(
"enablePermissionStorageAccessApi",
base::FeatureList::IsEnabled(
permissions::features::kPermissionStorageAccessAPI));
+ html_source->AddBoolean(
+ "autoPictureInPictureEnabled",
+ base::FeatureList::IsEnabled(
+ blink::features::kMediaSessionEnterPictureInPicture));
+
TryShowHatsSurveyWithTimeout();
}
@@ -592,6 +587,17 @@ void SettingsUI::BindInterface(
}
customize_themes_factory_receiver_.Bind(std::move(pending_receiver));
}
+
+void SettingsUI::BindInterface(
+ mojo::PendingReceiver<
+ theme_color_picker::mojom::ThemeColorPickerHandlerFactory>
+ pending_receiver) {
+ if (theme_color_picker_handler_factory_receiver_.is_bound()) {
+ theme_color_picker_handler_factory_receiver_.reset();
+ }
+ theme_color_picker_handler_factory_receiver_.Bind(
+ std::move(pending_receiver));
+}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
void SettingsUI::BindInterface(
@@ -629,6 +635,18 @@ void SettingsUI::CreateCustomizeThemesHandler(
std::move(pending_client), std::move(pending_handler),
web_ui()->GetWebContents(), Profile::FromWebUI(web_ui()));
}
+
+void SettingsUI::CreateThemeColorPickerHandler(
+ mojo::PendingReceiver<theme_color_picker::mojom::ThemeColorPickerHandler>
+ handler,
+ mojo::PendingRemote<theme_color_picker::mojom::ThemeColorPickerClient>
+ client) {
+ theme_color_picker_handler_ = std::make_unique<ThemeColorPickerHandler>(
+ std::move(handler), std::move(client),
+ NtpCustomBackgroundServiceFactory::GetForProfile(
+ Profile::FromWebUI(web_ui())),
+ web_ui()->GetWebContents());
+}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
void SettingsUI::CreateHelpBubbleHandler(
@@ -639,6 +657,29 @@ void SettingsUI::CreateHelpBubbleHandler(
std::vector<ui::ElementIdentifier>{kEnhancedProtectionSettingElementId});
}
+void SettingsUI::CreateCustomizeColorSchemeModeHandler(
+ mojo::PendingRemote<
+ customize_color_scheme_mode::mojom::CustomizeColorSchemeModeClient>
+ client,
+ mojo::PendingReceiver<
+ customize_color_scheme_mode::mojom::CustomizeColorSchemeModeHandler>
+ handler) {
+ customize_color_scheme_mode_handler_ =
+ std::make_unique<CustomizeColorSchemeModeHandler>(
+ std::move(client), std::move(handler), Profile::FromWebUI(web_ui()));
+}
+
+void SettingsUI::BindInterface(
+ mojo::PendingReceiver<customize_color_scheme_mode::mojom::
+ CustomizeColorSchemeModeHandlerFactory>
+ pending_receiver) {
+ if (customize_color_scheme_mode_handler_factory_receiver_.is_bound()) {
+ customize_color_scheme_mode_handler_factory_receiver_.reset();
+ }
+ customize_color_scheme_mode_handler_factory_receiver_.Bind(
+ std::move(pending_receiver));
+}
+
WEB_UI_CONTROLLER_TYPE_IMPL(SettingsUI)
} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_ui.h b/chromium/chrome/browser/ui/webui/settings/settings_ui.h
index 12d67325eed..33585999da8 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_ui.h
+++ b/chromium/chrome/browser/ui/webui/settings/settings_ui.h
@@ -14,10 +14,12 @@
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "ui/webui/mojo_web_ui_controller.h"
+#include "ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom.h"
#include "ui/webui/resources/cr_components/help_bubble/help_bubble.mojom.h"
#if !BUILDFLAG(IS_CHROMEOS_ASH)
#include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
+#include "ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.mojom.h"
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
namespace content {
@@ -30,18 +32,24 @@ class PrefRegistrySyncable;
#if !BUILDFLAG(IS_CHROMEOS_ASH)
class ChromeCustomizeThemesHandler;
+class ThemeColorPickerHandler;
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
+class CustomizeColorSchemeModeHandler;
namespace settings {
// The WebUI handler for chrome://settings.
-class SettingsUI : public ui::MojoWebUIController,
- public help_bubble::mojom::HelpBubbleHandlerFactory
+class SettingsUI
+ : public ui::MojoWebUIController,
+ public help_bubble::mojom::HelpBubbleHandlerFactory,
+ public customize_color_scheme_mode::mojom::
+ CustomizeColorSchemeModeHandlerFactory
#if !BUILDFLAG(IS_CHROMEOS_ASH)
// chrome://settings/manageProfile which only exists on !OS_CHROMEOS
// requires mojo bindings.
,
- public customize_themes::mojom::CustomizeThemesHandlerFactory
+ public customize_themes::mojom::CustomizeThemesHandlerFactory,
+ public theme_color_picker::mojom::ThemeColorPickerHandlerFactory
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
{
public:
@@ -67,6 +75,13 @@ class SettingsUI : public ui::MojoWebUIController,
void BindInterface(mojo::PendingReceiver<
customize_themes::mojom::CustomizeThemesHandlerFactory>
pending_receiver);
+
+ // Instantiates the implementor of the
+ // theme_color_picker::mojom::ThemeColorPickerHandlerFactory mojo interface
+ // passing the pending receiver that will be internally bound.
+ void BindInterface(mojo::PendingReceiver<
+ theme_color_picker::mojom::ThemeColorPickerHandlerFactory>
+ pending_receiver);
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
// Implements support for help bubbles (IPH, tutorials, etc.) in settings
@@ -75,6 +90,11 @@ class SettingsUI : public ui::MojoWebUIController,
mojo::PendingReceiver<help_bubble::mojom::HelpBubbleHandlerFactory>
pending_receiver);
+ void BindInterface(
+ mojo::PendingReceiver<customize_color_scheme_mode::mojom::
+ CustomizeColorSchemeModeHandlerFactory>
+ pending_receiver);
+
private:
void AddSettingsPageUIHandler(
std::unique_ptr<content::WebUIMessageHandler> handler);
@@ -90,9 +110,19 @@ class SettingsUI : public ui::MojoWebUIController,
mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
pending_handler) override;
+ // theme_color_picker::mojom::ThemeColorPickerHandlerFactory:
+ void CreateThemeColorPickerHandler(
+ mojo::PendingReceiver<theme_color_picker::mojom::ThemeColorPickerHandler>
+ handler,
+ mojo::PendingRemote<theme_color_picker::mojom::ThemeColorPickerClient>
+ client) override;
+
std::unique_ptr<ChromeCustomizeThemesHandler> customize_themes_handler_;
mojo::Receiver<customize_themes::mojom::CustomizeThemesHandlerFactory>
customize_themes_factory_receiver_{this};
+ std::unique_ptr<ThemeColorPickerHandler> theme_color_picker_handler_;
+ mojo::Receiver<theme_color_picker::mojom::ThemeColorPickerHandlerFactory>
+ theme_color_picker_handler_factory_receiver_{this};
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
// help_bubble::mojom::HelpBubbleHandlerFactory:
@@ -105,6 +135,20 @@ class SettingsUI : public ui::MojoWebUIController,
mojo::Receiver<help_bubble::mojom::HelpBubbleHandlerFactory>
help_bubble_handler_factory_receiver_{this};
+ void CreateCustomizeColorSchemeModeHandler(
+ mojo::PendingRemote<
+ customize_color_scheme_mode::mojom::CustomizeColorSchemeModeClient>
+ client,
+ mojo::PendingReceiver<
+ customize_color_scheme_mode::mojom::CustomizeColorSchemeModeHandler>
+ handler) override;
+
+ std::unique_ptr<CustomizeColorSchemeModeHandler>
+ customize_color_scheme_mode_handler_;
+ mojo::Receiver<customize_color_scheme_mode::mojom::
+ CustomizeColorSchemeModeHandlerFactory>
+ customize_color_scheme_mode_handler_factory_receiver_{this};
+
WEB_UI_CONTROLLER_TYPE_DECL();
};
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_utils_mac.mm b/chromium/chrome/browser/ui/webui/settings/settings_utils_mac.mm
index 68c89f0688d..96204b8e5be 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_utils_mac.mm
+++ b/chromium/chrome/browser/ui/webui/settings/settings_utils_mac.mm
@@ -6,20 +6,16 @@
#include "chrome/browser/ui/webui/settings/settings_utils.h"
+#include "base/apple/foundation_util.h"
+#include "base/apple/osstatus_logging.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
-#include "base/mac/foundation_util.h"
#include "base/mac/launch_application.h"
-#include "base/mac/mac_logging.h"
#include "base/mac/mac_util.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
namespace {
void ValidateFontFamily(PrefService* prefs, const char* family_pref_name) {
// The native font settings dialog saved fonts by the font name, rather
@@ -48,7 +44,7 @@ void ShowNetworkProxySettings(content::WebContents* web_contents) {
void ShowManageSSLCertificates(content::WebContents* web_contents) {
NSURL* keychain_app = [NSWorkspace.sharedWorkspace
URLForApplicationWithBundleIdentifier:@"com.apple.keychainaccess"];
- base::mac::LaunchApplication(base::mac::NSURLToFilePath(keychain_app),
+ base::mac::LaunchApplication(base::apple::NSURLToFilePath(keychain_app),
/*command_line_args=*/{}, /*url_specs=*/{},
/*options=*/{}, base::DoNothing());
}
diff --git a/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
index b1429fa4f30..458ca9bf5da 100644
--- a/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
+++ b/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
@@ -40,12 +40,8 @@
#include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chromeos/startup/browser_params_proxy.h"
-#endif
-
#if BUILDFLAG(IS_CHROMEOS)
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#endif
namespace settings {
@@ -63,25 +59,6 @@ std::u16string GetHelpUrlWithBoard(const std::string& original_url) {
} // namespace
#endif
-// Lacros side-by-side warning should be shown as long as both Lacros and Ash
-// browsers enabled.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-bool ShouldShowLacrosSideBySideWarningInAsh() {
- return base::FeatureList::IsEnabled(
- syncer::kSyncSettingsShowLacrosSideBySideWarning) &&
- crosapi::browser_util::IsAshWebBrowserEnabled() &&
- crosapi::browser_util::IsLacrosEnabled();
-}
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-bool ShouldShowLacrosSideBySideWarningInLacros() {
- return base::FeatureList::IsEnabled(
- syncer::kSyncSettingsShowLacrosSideBySideWarning) &&
- !chromeos::BrowserParamsProxy::Get()->StandaloneBrowserIsOnlyBrowser();
-}
-#endif
-
void AddCaptionSubpageStrings(content::WebUIDataSource* html_source) {
static constexpr webui::LocalizedString kLocalizedStrings[] = {
{"captionsTitle", IDS_SETTINGS_CAPTIONS},
@@ -130,6 +107,21 @@ void AddCaptionSubpageStrings(content::WebUIDataSource* html_source) {
AddLiveCaptionSectionStrings(html_source);
}
+// Live Caption subtitle depends on whether multi-language is supported, and on
+// Ash also depends on whether system-wide live caption is enabled.
+int GetLiveCaptionSubtitle(const bool multi_language) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ if (!ash::features::IsSystemLiveCaptionEnabled()) {
+ return multi_language
+ ? IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE_BROWSER_ONLY
+ : IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE_BROWSER_ONLY_ENGLISH_ONLY;
+ }
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ return multi_language
+ ? IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE
+ : IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE_ENGLISH_ONLY;
+}
+
void AddLiveCaptionSectionStrings(content::WebUIDataSource* html_source) {
html_source->AddLocalizedString(
"captionsEnableLiveCaptionTitle",
@@ -140,6 +132,9 @@ void AddLiveCaptionSectionStrings(content::WebUIDataSource* html_source) {
html_source->AddLocalizedString(
"captionsEnableLiveTranslateSubtitle",
IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_TRANSLATE_SUBTITLE);
+ html_source->AddLocalizedString(
+ "captionsMaskOffensiveWordsTitle",
+ IDS_SETTINGS_CAPTIONS_MASK_OFFENSIVE_WORDS_TITLE);
const bool liveCaptionMultiLanguageEnabled =
base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage);
@@ -148,9 +143,8 @@ void AddLiveCaptionSectionStrings(content::WebUIDataSource* html_source) {
base::FeatureList::IsEnabled(media::kLiveTranslate);
const int live_caption_subtitle_message =
- liveCaptionMultiLanguageEnabled
- ? IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE
- : IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE_ENGLISH_ONLY;
+ GetLiveCaptionSubtitle(liveCaptionMultiLanguageEnabled);
+
html_source->AddLocalizedString("captionsEnableLiveCaptionSubtitle",
live_caption_subtitle_message);
html_source->AddBoolean("enableLiveCaption",
@@ -209,14 +203,6 @@ void AddSharedSyncPageStrings(content::WebUIDataSource* html_source) {
{"existingPassphraseLabelWithDate",
IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE},
{"existingPassphraseLabel", IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM},
- // Settings warning for Lacros side-by-side mode.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- {"syncSettingsLacrosSideBySideWarning",
- IDS_SYNC_SETTINGS_SIDE_BY_SIDE_WARNING_ASH},
-#elif BUILDFLAG(IS_CHROMEOS_LACROS)
- {"syncSettingsLacrosSideBySideWarning",
- IDS_SYNC_SETTINGS_SIDE_BY_SIDE_WARNING_LACROS},
-#endif
};
html_source->AddLocalizedStrings(kLocalizedStrings);
@@ -259,15 +245,11 @@ void AddSharedSyncPageStrings(content::WebUIDataSource* html_source) {
base::ASCIIToUTF16(chrome::kSyncEncryptionHelpURL)));
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
- html_source->AddBoolean("shouldShowLacrosSideBySideWarning",
- ShouldShowLacrosSideBySideWarningInAsh());
html_source->AddBoolean(
"showSyncSettingsRevamp",
base::FeatureList::IsEnabled(syncer::kSyncChromeOSAppsToggleSharing) &&
crosapi::browser_util::IsLacrosEnabled());
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
- html_source->AddBoolean("shouldShowLacrosSideBySideWarning",
- ShouldShowLacrosSideBySideWarningInLacros());
html_source->AddBoolean(
"showSyncSettingsRevamp",
base::FeatureList::IsEnabled(syncer::kSyncChromeOSAppsToggleSharing));
diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc
index 18e8059ce26..fd419094ade 100644
--- a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -48,6 +48,7 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/page_info/page_info_infobar_delegate.h"
+#include "chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/url_identity.h"
#include "chrome/browser/ui/webui/settings/recent_site_settings_helper.h"
@@ -372,12 +373,16 @@ bool IsPatternValidForType(const std::string& pattern_string,
return true;
}
-void UpdateDataFromModel(SiteSettingsHandler::AllSitesMap* all_sites_map,
- std::map<url::Origin, int64_t>* origin_size_map,
- const url::Origin& origin,
- int64_t size) {
+void UpdateDataFromModel(
+ SiteSettingsHandler::AllSitesMap* all_sites_map,
+ std::map<url::Origin, int64_t>* origin_size_map,
+ const url::Origin& origin,
+ int64_t size,
+ absl::optional<GroupingKey> partition_grouping_key = absl::nullopt) {
UpdateDataForOrigin(origin, size, origin_size_map);
- InsertOriginIntoGroup(all_sites_map, origin);
+ InsertOriginIntoGroup(all_sites_map, origin,
+ /*is_origin_with_cookies=*/false,
+ partition_grouping_key);
}
void LogAllSitesAction(AllSitesAction2 action) {
@@ -386,8 +391,8 @@ void LogAllSitesAction(AllSitesAction2 action) {
int GetNumCookieExceptionsOfTypes(HostContentSettingsMap* map,
const std::set<ContentSetting> types) {
- ContentSettingsForOneType output;
- map->GetSettingsForOneType(ContentSettingsType::COOKIES, &output);
+ ContentSettingsForOneType output =
+ map->GetSettingsForOneType(ContentSettingsType::COOKIES);
return base::ranges::count_if(
output, [types](const ContentSettingPatternSource setting) {
return types.count(
@@ -801,6 +806,11 @@ void SiteSettingsHandler::RegisterMessages() {
base::BindRepeating(&SiteSettingsHandler::HandleGetExceptionList,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "getStorageAccessExceptionList",
+ base::BindRepeating(
+ &SiteSettingsHandler::HandleGetStorageAccessExceptionList,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"getFileSystemGrants",
base::BindRepeating(&SiteSettingsHandler::HandleGetFileSystemGrants,
base::Unretained(this)));
@@ -1006,6 +1016,8 @@ void SiteSettingsHandler::OnGetUsageInfo() {
if (!entry.Matches(usage_origin)) {
continue;
}
+ // TODO(crbug.com/1468277): Step 5 - Check if the entry is backed by a
+ // StorageKey and count the size if the top-site matches too.
size += entry.data_details->storage_size;
}
@@ -1162,6 +1174,8 @@ void SiteSettingsHandler::HandleClearUnpartitionedUsage(
RemoveNonTreeModelData(affected_origins);
}
+// TODO(crbug.com/1468277): Step 5 - Handle clearing partitioned entries as
+// well where origin matches the top-site in the browsing data model.
void SiteSettingsHandler::HandleClearPartitionedUsage(
const base::Value::List& args) {
CHECK_EQ(2U, args.size());
@@ -1497,6 +1511,35 @@ void SiteSettingsHandler::HandleGetExceptionList(
ResolveJavascriptCallback(callback_id, exceptions);
}
+void SiteSettingsHandler::HandleGetStorageAccessExceptionList(
+ const base::Value::List& args) {
+ AllowJavascript();
+
+ CHECK_EQ(2U, args.size());
+ const base::Value& callback_id = args[0];
+
+ ContentSetting setting;
+ CHECK(content_settings::ContentSettingFromString(args[1].GetString(),
+ &setting));
+
+ Profile* incognito_ =
+ profile_->HasPrimaryOTRProfile()
+ ? profile_->GetPrimaryOTRProfile(/*create_if_needed=*/true)
+ : nullptr;
+
+ // On Chrome OS in Guest mode the incognito profile is the primary profile,
+ // so do not fetch an extra copy of the same exceptions.
+ if (incognito_ && incognito_ == profile_) {
+ incognito_ = nullptr;
+ }
+
+ base::Value::List exceptions;
+ site_settings::GetStorageAccessExceptions(setting, profile_, incognito_,
+ web_ui(), &exceptions);
+
+ ResolveJavascriptCallback(callback_id, exceptions);
+}
+
void SiteSettingsHandler::HandleGetChooserExceptionList(
const base::Value::List& args) {
AllowJavascript();
@@ -1604,10 +1647,7 @@ void SiteSettingsHandler::HandleRevokeFileSystemGrant(
ChromeFileSystemAccessPermissionContext* permission_context =
FileSystemAccessPermissionContextFactory::GetForProfile(profile_);
- permission_context->RevokeGrant(
- origin, file_path,
- ChromeFileSystemAccessPermissionContext::PersistedPermissionOptions::
- kUpdatePersistedPermission);
+ permission_context->RevokeGrant(origin, file_path);
}
void SiteSettingsHandler::HandleRevokeFileSystemGrants(
@@ -1627,9 +1667,7 @@ void SiteSettingsHandler::HandleRevokeFileSystemGrants(
ChromeFileSystemAccessPermissionContext* permission_context =
FileSystemAccessPermissionContextFactory::GetForProfile(profile_);
- permission_context->RevokeGrants(
- origin, ChromeFileSystemAccessPermissionContext::
- PersistedPermissionOptions::kUpdatePersistedPermission);
+ permission_context->RevokeGrants(origin);
}
void SiteSettingsHandler::HandleSetOriginPermissions(
@@ -2182,6 +2220,16 @@ void SiteSettingsHandler::ObserveSourcesForProfile(Profile* profile) {
chooser_observations_.AddObservation(bluetooth_context);
}
+ if (base::FeatureList::IsEnabled(
+ features::kFileSystemAccessPersistentPermissions)) {
+ auto* file_system_access_permission_context =
+ FileSystemAccessPermissionContextFactory::GetForProfile(profile);
+ if (!chooser_observations_.IsObservingSource(
+ file_system_access_permission_context)) {
+ chooser_observations_.AddObservation(
+ file_system_access_permission_context);
+ }
+ }
observed_profiles_.AddObservation(profile);
}
@@ -2210,22 +2258,20 @@ void SiteSettingsHandler::StopObservingSourcesForProfile(Profile* profile) {
chooser_observations_.RemoveObservation(bluetooth_context);
}
+ if (base::FeatureList::IsEnabled(
+ features::kFileSystemAccessPersistentPermissions)) {
+ auto* file_system_access_permission_context =
+ FileSystemAccessPermissionContextFactory::GetForProfile(profile);
+ if (chooser_observations_.IsObservingSource(
+ file_system_access_permission_context)) {
+ chooser_observations_.RemoveObservation(
+ file_system_access_permission_context);
+ }
+ }
+
observed_profiles_.RemoveObservation(profile);
}
-void SiteSettingsHandler::TreeNodesAdded(ui::TreeModel* model,
- ui::TreeModelNode* parent,
- size_t start,
- size_t count) {}
-
-void SiteSettingsHandler::TreeNodesRemoved(ui::TreeModel* model,
- ui::TreeModelNode* parent,
- size_t start,
- size_t count) {}
-
-void SiteSettingsHandler::TreeNodeChanged(ui::TreeModel* model,
- ui::TreeModelNode* node) {}
-
void SiteSettingsHandler::TreeModelEndBatchDeprecated(CookiesTreeModel* model) {
ModelBuilt();
}
@@ -2255,8 +2301,19 @@ void SiteSettingsHandler::GetOriginStorage(
},
[](const url::Origin& origin) { return origin; }},
*entry.data_owner);
+
+ // If the storage is backed by a StorageKey we need to ensure the grouping
+ // key matches the top-site and doesn't default to the origin in the UI.
+ absl::optional<GroupingKey> partition_grouping_key = absl::nullopt;
+ const blink::StorageKey* storage_key =
+ absl::get_if<blink::StorageKey>(&entry.data_key.get());
+ if (storage_key != nullptr && storage_key->IsThirdPartyContext()) {
+ partition_grouping_key = GroupingKey::Create(
+ url::Origin::Create(GURL(storage_key->top_level_site().Serialize())));
+ }
UpdateDataFromModel(all_sites_map, origin_size_map, origin,
- entry.data_details->storage_size);
+ entry.data_details->storage_size,
+ partition_grouping_key);
}
}
@@ -2411,7 +2468,7 @@ void SiteSettingsHandler::RemoveNonTreeModelData(
}
}
- // Remove any Browsing Data Model data associated with the origins host.
+ // Remove any Browsing Data Model data associated with the origins.
// TODO(crbug.com/1271155) - When the browsing data model supports all storage
// types, re-work this handler to work directly with primary hosts as defined
// by the model.
@@ -2421,8 +2478,13 @@ void SiteSettingsHandler::RemoveNonTreeModelData(
// been created by permission info).
if (browsing_data_model_) {
for (const auto& origin : origins) {
- browsing_data_model_->RemoveBrowsingData(origin.host(),
- base::DoNothing());
+ browsing_data_model_->RemoveBrowsingData(origin, base::DoNothing());
+
+ // Also remove Browsing Data Model data associated with the origin's host.
+ if (origin.GetURL().SchemeIsHTTPOrHTTPS()) {
+ browsing_data_model_->RemoveBrowsingData(origin.host(),
+ base::DoNothing());
+ }
}
}
@@ -2463,6 +2525,14 @@ void SiteSettingsHandler::ClearAllSitesMapForTesting() {
all_sites_map_.clear();
}
+CookiesTreeModel* SiteSettingsHandler::GetCookiesTreeModelForTesting() {
+ return cookies_tree_model_.get();
+}
+
+BrowsingDataModel* SiteSettingsHandler::GetBrowsingDataModelForTesting() {
+ return browsing_data_model_.get();
+}
+
void SiteSettingsHandler::SendCookieSettingDescription() {
FireWebUIListener("cookieSettingDescriptionChanged",
base::Value(GetCookieSettingDescription(profile_)));
@@ -2473,45 +2543,45 @@ void SiteSettingsHandler::SendCookieSettingDescription() {
// {
// "origin" : <string>;
// "filePath" : <string>;
-// "isWritable" : <bool>;
+// "displayName" : <string>;
// "isDirectory" : <bool>;
// }
// Dictionary keys for an individual permission grant in
// the returned `grants` List.
-// Note that while the `isWritable` and `isDirectory` values
-// are implied by the names of the grant lists, the
-// `FileSystemPermissionGrant` type contains these attributes
-// in order to make the data more easily accessible from the UI code.
-//
// Schema (per origin):
// [
// ...
// {
// "origin" : <string>;
-// "directoryReadGrants" : FileSystemPermissionGrant[];
-// "directoryWriteGrants" : FileSystemPermissionGrant[];
-// "fileReadGrants" : FileSystemPermissionGrant[];
-// "fileWriteGrants" : FileSystemPermissionGrant[];
+// "viewGrants" : FileSystemPermissionGrant[];
+// "editGrants" : FileSystemPermissionGrant[];
// }
// ...
// ]
+
+// TODO(crbug.com/1373962): Store the `FilePathToValue(file_path)` value
+// as a separate variable throughout the `PopulateFileSystemGrantData` function
+// where possible, instead of computing the value every time that it is needed.
base::Value::List SiteSettingsHandler::PopulateFileSystemGrantData() {
base::Value::List grants;
// TODO(crbug.com/1373962): Remove feature flag check after persisted
// permissions is fully launched.
if (!base::FeatureList::IsEnabled(
- features::kFileSystemAccessPersistentPermissions))
+ features::kFileSystemAccessPersistentPermissions)) {
return grants;
+ }
+
ChromeFileSystemAccessPermissionContext* permission_context =
FileSystemAccessPermissionContextFactory::GetForProfile(profile_);
- std::vector<url::Origin> origins_with_grants =
+ std::set<url::Origin> origins_with_grants =
permission_context->GetOriginsWithGrants();
for (auto& origin : origins_with_grants) {
ChromeFileSystemAccessPermissionContext::Grants grantObj =
- permission_context->GetPermissionGrants(origin);
+ permission_context->ConvertObjectsToGrants(
+ permission_context->GetGrantedObjects(origin));
if (grantObj.file_read_grants.empty() &&
grantObj.file_write_grants.empty() &&
grantObj.directory_read_grants.empty() &&
@@ -2519,13 +2589,15 @@ base::Value::List SiteSettingsHandler::PopulateFileSystemGrantData() {
continue;
}
- base::Value::Dict file_system_permission_grant;
- base::Value::List directory_read_grants;
- base::Value::List directory_write_grants;
- base::Value::List file_read_grants;
- base::Value::List file_write_grants;
+ base::Value::Dict origin_file_system_permission_grants;
+ base::Value::List view_grants;
+ base::Value::List edit_grants;
+ std::vector<base::Value> directory_edit_grants_file_paths;
+ std::vector<base::Value> file_edit_grants_file_paths;
+
std::string origin_string = origin.GetURL().spec();
- file_system_permission_grant.Set(site_settings::kOrigin, origin_string);
+ origin_file_system_permission_grants.Set(site_settings::kOrigin,
+ origin_string);
// Populate the `file_system_permission_grant` object with allowed
// permissions.
@@ -2534,48 +2606,58 @@ base::Value::List SiteSettingsHandler::PopulateFileSystemGrantData() {
directory_write_grant.Set(site_settings::kOrigin, origin_string);
directory_write_grant.Set(site_settings::kFilePath,
FilePathToValue(file_path));
- directory_write_grant.Set(site_settings::kIsWritable, true);
+ directory_write_grant.Set(site_settings::kDisplayName,
+ FilePathToValue(file_path));
directory_write_grant.Set(site_settings::kIsDirectory, true);
- directory_write_grants.Append(std::move(directory_write_grant));
+ directory_edit_grants_file_paths.push_back(FilePathToValue(file_path));
+ edit_grants.Append(std::move(directory_write_grant));
}
- file_system_permission_grant.Set(site_settings::kDirectoryWriteGrants,
- std::move(directory_write_grants));
for (auto& file_path : grantObj.directory_read_grants) {
+ if (base::Contains(directory_edit_grants_file_paths,
+ FilePathToValue(file_path))) {
+ continue;
+ }
base::Value::Dict directory_read_grant;
directory_read_grant.Set(site_settings::kOrigin, origin_string);
directory_read_grant.Set(site_settings::kFilePath,
FilePathToValue(file_path));
- directory_read_grant.Set(site_settings::kIsWritable, false);
+ directory_read_grant.Set(site_settings::kDisplayName,
+ FilePathToValue(file_path));
directory_read_grant.Set(site_settings::kIsDirectory, true);
- directory_read_grants.Append(std::move(directory_read_grant));
+ view_grants.Append(std::move(directory_read_grant));
}
- file_system_permission_grant.Set(site_settings::kDirectoryReadGrants,
- std::move(directory_read_grants));
for (auto& file_path : grantObj.file_write_grants) {
base::Value::Dict file_write_grant;
file_write_grant.Set(site_settings::kOrigin, origin_string);
file_write_grant.Set(site_settings::kFilePath,
FilePathToValue(file_path));
- file_write_grant.Set(site_settings::kIsWritable, true);
+ file_write_grant.Set(site_settings::kDisplayName,
+ FilePathToValue(file_path));
file_write_grant.Set(site_settings::kIsDirectory, false);
- file_write_grants.Append(std::move(file_write_grant));
+ file_edit_grants_file_paths.push_back(FilePathToValue(file_path));
+ edit_grants.Append(std::move(file_write_grant));
}
- file_system_permission_grant.Set(site_settings::kFileWriteGrants,
- std::move(file_write_grants));
for (auto& file_path : grantObj.file_read_grants) {
+ if (base::Contains(file_edit_grants_file_paths,
+ FilePathToValue(file_path))) {
+ continue;
+ }
base::Value::Dict file_read_grant;
file_read_grant.Set(site_settings::kOrigin, origin_string);
file_read_grant.Set(site_settings::kFilePath, FilePathToValue(file_path));
- file_read_grant.Set(site_settings::kIsWritable, false);
+ file_read_grant.Set(site_settings::kDisplayName,
+ FilePathToValue(file_path));
file_read_grant.Set(site_settings::kIsDirectory, false);
- file_read_grants.Append((base::Value(std::move(file_read_grant))));
+ view_grants.Append((base::Value(std::move(file_read_grant))));
}
- file_system_permission_grant.Set(site_settings::kFileReadGrants,
- std::move(file_read_grants));
- grants.Append(std::move(file_system_permission_grant));
+ origin_file_system_permission_grants.Set("viewGrants",
+ std::move(view_grants));
+ origin_file_system_permission_grants.Set("editGrants",
+ std::move(edit_grants));
+ grants.Append(std::move(origin_file_system_permission_grants));
}
return grants;
}
@@ -2585,6 +2667,9 @@ void SiteSettingsHandler::SendNotificationPermissionReviewList() {
features::kSafetyCheckNotificationPermissions)) {
return;
}
+ NotificationPermissionsReviewService* service =
+ NotificationPermissionsReviewServiceFactory::GetForProfile(profile_);
+ CHECK(service);
// Notify observers that the permission review list could have changed. Note
// that the list is not guaranteed to have changed. In places where
// determining whether the list has changed is cause for performance concerns,
@@ -2593,7 +2678,7 @@ void SiteSettingsHandler::SendNotificationPermissionReviewList() {
// HandleSetCategoryPermissionForPattern.
FireWebUIListener(
site_settings::kNotificationPermissionsReviewListMaybeChangedEvent,
- site_settings::PopulateNotificationPermissionReviewData(profile_));
+ service->PopulateNotificationPermissionReviewData(profile_));
}
} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h
index 5a22fb63250..e6393723f3e 100644
--- a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -102,15 +102,6 @@ class SiteSettingsHandler
// CookiesTreeModel::Observer:
// TODO(https://crbug.com/835712): Listen for backend data changes and notify
// WebUI
- void TreeNodesAdded(ui::TreeModel* model,
- ui::TreeModelNode* parent,
- size_t start,
- size_t count) override;
- void TreeNodesRemoved(ui::TreeModel* model,
- ui::TreeModelNode* parent,
- size_t start,
- size_t count) override;
- void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override;
void TreeModelEndBatchDeprecated(CookiesTreeModel* model) override;
// content_settings::Observer:
@@ -129,103 +120,8 @@ class SiteSettingsHandler
void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change);
- private:
- friend class SiteSettingsHandlerBaseTest;
- friend class SiteSettingsHandlerChooserExceptionTest;
- friend class SiteSettingsHandlerInfobarTest;
- // TODO(crbug.com/1373962): Remove this friend class when
- // Persistent Permissions is launched.
- friend class PersistentPermissionsSiteSettingsHandlerTest;
- FRIEND_TEST_ALL_PREFIXES(PersistentPermissionsSiteSettingsHandlerTest,
- HandleGetFileSystemGrants);
- FRIEND_TEST_ALL_PREFIXES(PersistentPermissionsSiteSettingsHandlerTest,
- HandleRevokeFileSystemGrant);
- FRIEND_TEST_ALL_PREFIXES(PersistentPermissionsSiteSettingsHandlerTest,
- HandleRevokeFileSystemGrants);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerInfobarTest,
- SettingPermissionsTriggersInfobar);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- BlockAutoplay_SendOnRequest);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, BlockAutoplay_Update);
-#if BUILDFLAG(ENABLE_PLUGINS)
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- ChangingFlashSettingForSiteIsRemembered);
-#endif
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, Cookies);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, DefaultSettingSource);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExceptionHelpers);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExtensionDisplayName);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAllSites);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetRecentSitePermissions);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, OnStorageFetched);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAndSetDefault);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAndSetForInvalidURLs);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAndSetOriginPermissions);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, Incognito);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, IncognitoExceptions);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- ResetCategoryPermissionForEmbargoedOrigins);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- ResetCategoryPermissionForInvalidOrigins);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, Origins);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, Patterns);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, PatternsAndContentType);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, SessionOnlyException);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ZoomLevels);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- HandleClearSiteGroupDataAndCookies);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- HandleClearUnpartitionedUsage);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ClearClientHints);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ClearReducedAcceptLanguage);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- HandleClearPartitionedUsage);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, CookieSettingDescription);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, HandleGetFormattedBytes);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- NotificationPermissionRevokeUkm);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExcludeWebUISchemesInLists);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- IncludeWebUISchemesInGetOriginPermissions);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, HandleGetUsageInfo);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
- HandleGetFpsMembershipLabel);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, NonTreeModelDeletion);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, FirstPartySetsMembership);
- FRIEND_TEST_ALL_PREFIXES(
- SiteSettingsHandlerInfobarTest,
- SettingPermissionsDoesNotTriggerInfobarOnDifferentProfile);
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, HandleGetExtensionName);
-#endif
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, IsolatedWebAppUsageInfo);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerIsolatedWebAppTest, ZoomLevel);
- FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerIsolatedWebAppTest,
- ZoomLevelsSortedByAppName);
-
- // Rebuilds the BrowsingDataModel & CookiesTreeModel. Pending requests are
- // serviced when both models are built.
- void RebuildModels();
- void ModelBuilt();
void ServicePendingRequests();
- // Add or remove this class as an observer for content settings and chooser
- // contexts corresponding to |profile|.
- void ObserveSourcesForProfile(Profile* profile);
- void StopObservingSourcesForProfile(Profile* profile);
-
- // Calculates the data storage that has been used for each origin, and
- // stores the information in the |all_sites_map| and |origin_size_map|.
- void GetOriginStorage(AllSitesMap* all_sites_map,
- std::map<url::Origin, int64_t>* origin_size_map);
-
- // Calculates the number of cookies for each etld+1 and each host, and
- // stores the information in the |all_sites_map| and |host_cookie_map|.
- void GetHostCookies(
- AllSitesMap* all_sites_map,
- std::map<std::pair<std::string, absl::optional<std::string>>, int>*
- host_cookie_map);
-
// Asynchronously fetches the usage for a given origin. Replies back with
// OnGetUsageInfo above.
void HandleFetchUsageTotal(const base::Value::List& args);
@@ -248,12 +144,6 @@ class SiteSettingsHandler
// the front end when fetching finished.
void HandleGetAllSites(const base::Value::List& args);
- // Returns a list of content settings types that are controlled via a standard
- // permissions UI and should be made visible to the user. There is a single
- // nullable string argument, which represents an associated origin. See
- // `SiteSettingsPrefsBrowserProxy#getCategoryList`.
- void HandleGetCategoryList(const base::Value::List& args);
-
// Returns a string for display describing the current cookie settings.
void HandleGetCookieSettingDescription(const base::Value::List& args);
@@ -268,11 +158,6 @@ class SiteSettingsHandler
// this list back to the front end.
void OnStorageFetched();
- // Returns a list of sites, grouped by their effective top level domain plus
- // 1, with their cookies number and data usage information. This method will
- // only be called after HandleGetAllSites is called.
- base::Value::List PopulateCookiesAndUsageData(Profile* profile);
-
// Converts a given number of bytes into a human-readable format, with data
// units.
void HandleGetFormattedBytes(const base::Value::List& args);
@@ -280,6 +165,10 @@ class SiteSettingsHandler
// Returns the list of site exceptions for a given content settings type.
void HandleGetExceptionList(const base::Value::List& args);
+ // Returns the list of storage access site exceptions for a given content
+ // setting (such as enabled or blocked).
+ void HandleGetStorageAccessExceptionList(const base::Value::List& args);
+
// Returns the list of chooser exceptions for a given chooser type.
void HandleGetChooserExceptionList(const base::Value::List& args);
@@ -302,6 +191,7 @@ class SiteSettingsHandler
// Handles setting and resetting an origin permission.
void HandleResetCategoryPermissionForPattern(const base::Value::List& args);
+ // TODO(1466127): Add tests for HandleSetCategoryPermissionForPattern.
void HandleSetCategoryPermissionForPattern(const base::Value::List& args);
// TODO(andypaicu, crbug.com/880684): Update to only expect a list of three
@@ -310,39 +200,88 @@ class SiteSettingsHandler
// Handles resetting a chooser exception for the given site.
void HandleResetChooserExceptionForSite(const base::Value::List& args);
- // Returns whether a given string is a valid origin.
- void HandleIsOriginValid(const base::Value::List& args);
-
// Returns whether the pattern is valid given the type.
void HandleIsPatternValidForType(const base::Value::List& args);
// Looks up whether an incognito session is active.
void HandleUpdateIncognitoStatus(const base::Value::List& args);
- // Notifies the JS side whether incognito is enabled.
- void SendIncognitoStatus(Profile* profile, bool was_destroyed);
-
// Handles the request for a list of all zoom levels.
void HandleFetchZoomLevels(const base::Value::List& args);
- // Sends the zoom level list down to the web ui.
- void SendZoomLevels();
-
// Removes a particular zoom level for a given host.
void HandleRemoveZoomLevel(const base::Value::List& args);
// Handles the request to send block autoplay state.
void HandleFetchBlockAutoplayStatus(const base::Value::List& args);
- // Notifies the JS side about the state of the block autoplay toggle.
- void SendBlockAutoplayStatus();
-
// Updates the block autoplay enabled pref when the UI is toggled.
void HandleSetBlockAutoplayEnabled(const base::Value::List& args);
// Clear web storage data and cookies from CookiesTreeModel for a site group.
void HandleClearSiteGroupDataAndCookies(const base::Value::List& args);
+ void ClearAllSitesMapForTesting();
+
+ void SetModelsForTesting(
+ std::unique_ptr<CookiesTreeModel> cookies_tree_model,
+ std::unique_ptr<BrowsingDataModel> browsing_data_model);
+
+ CookiesTreeModel* GetCookiesTreeModelForTesting();
+ BrowsingDataModel* GetBrowsingDataModelForTesting();
+
+ private:
+ friend class SiteSettingsHandlerBaseTest;
+ friend class SiteSettingsHandlerInfobarTest;
+ // TODO(crbug.com/1373962): Remove this friend class when
+ // Persistent Permissions is launched.
+ friend class PersistentPermissionsSiteSettingsHandlerTest;
+
+ // Rebuilds the BrowsingDataModel & CookiesTreeModel. Pending requests are
+ // serviced when both models are built.
+ void RebuildModels();
+ void ModelBuilt();
+
+ // Add or remove this class as an observer for content settings and chooser
+ // contexts corresponding to |profile|.
+ void ObserveSourcesForProfile(Profile* profile);
+ void StopObservingSourcesForProfile(Profile* profile);
+
+ // Calculates the data storage that has been used for each origin, and
+ // stores the information in the |all_sites_map| and |origin_size_map|.
+ void GetOriginStorage(AllSitesMap* all_sites_map,
+ std::map<url::Origin, int64_t>* origin_size_map);
+
+ // Calculates the number of cookies for each etld+1 and each host, and
+ // stores the information in the |all_sites_map| and |host_cookie_map|.
+ void GetHostCookies(
+ AllSitesMap* all_sites_map,
+ std::map<std::pair<std::string, absl::optional<std::string>>, int>*
+ host_cookie_map);
+
+ // Returns a list of content settings types that are controlled via a standard
+ // permissions UI and should be made visible to the user. There is a single
+ // nullable string argument, which represents an associated origin. See
+ // `SiteSettingsPrefsBrowserProxy#getCategoryList`.
+ void HandleGetCategoryList(const base::Value::List& args);
+
+ // Returns a list of sites, grouped by their effective top level domain plus
+ // 1, with their cookies number and data usage information. This method will
+ // only be called after HandleGetAllSites is called.
+ base::Value::List PopulateCookiesAndUsageData(Profile* profile);
+
+ // Returns whether a given string is a valid origin.
+ void HandleIsOriginValid(const base::Value::List& args);
+
+ // Notifies the JS side about the state of the block autoplay toggle.
+ void SendBlockAutoplayStatus();
+
+ // Notifies the JS side whether incognito is enabled.
+ void SendIncognitoStatus(Profile* profile, bool was_destroyed);
+
+ // Sends the zoom level list down to the web ui.
+ void SendZoomLevels();
+
// Record metrics for actions on All Sites Page.
void HandleRecordAction(const base::Value::List& args);
@@ -355,12 +294,6 @@ class SiteSettingsHandler
// CookiesTreeModel is deprecated.
void RemoveNonTreeModelData(const std::vector<url::Origin>& origins);
- void SetModelsForTesting(
- std::unique_ptr<CookiesTreeModel> cookies_tree_model,
- std::unique_ptr<BrowsingDataModel> browsing_data_model);
-
- void ClearAllSitesMapForTesting();
-
// Notifies the JS side the effective cookies setting has changed and
// provides the updated description label for display.
void SendCookieSettingDescription();
diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index 9fd8fd8bf8f..77eef6ce215 100644
--- a/chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -6,6 +6,7 @@
#include <memory>
#include <string>
+#include <tuple>
#include <utility>
#include <vector>
@@ -26,6 +27,7 @@
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/simple_test_clock.h"
+#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
@@ -97,6 +99,7 @@
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/app_update.h"
#include "components/site_engagement/content/site_engagement_score.h"
+#include "components/strings/grit/components_strings.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/browsing_data_remover.h"
@@ -150,6 +153,7 @@ using ::testing::Return;
using ::testing::UnorderedElementsAre;
using GroupingKey = settings::SiteSettingsHandler::GroupingKey;
+using PermissionStatus = blink::mojom::PermissionStatus;
constexpr char kCallbackId[] = "test-callback-id";
constexpr char kSetting[] = "setting";
@@ -178,6 +182,14 @@ const struct PatternContentTypeTestCase {
{{"http://127.0.0.1", "location"}, {true, ""}}, // Localhost is secure.
{{"http://[::1]", "location"}, {true, ""}}};
+struct EmbeddingStorageAccessException {
+ const std::string embedding_origin;
+ const std::string embedding_display_name;
+ const bool incognito;
+ const bool embargoed = false;
+ const int lifetime_in_days = 0;
+};
+
// Matchers to make verifying GroupingKey contents easier.
MATCHER_P(IsEtldPlus1, etld_plus1, "") {
return arg == std::string("etld:") + etld_plus1;
@@ -296,11 +308,7 @@ class ContentSettingSourceSetter {
class SiteSettingsHandlerBaseTest : public testing::Test {
public:
- SiteSettingsHandlerBaseTest()
- : kNotifications(site_settings::ContentSettingsTypeToGroupName(
- ContentSettingsType::NOTIFICATIONS)),
- kCookies(site_settings::ContentSettingsTypeToGroupName(
- ContentSettingsType::COOKIES)) {
+ SiteSettingsHandlerBaseTest() {
// Fully initialize |profile_| in the constructor since some children
// classes need it right away for SetUp().
testing_profile_manager_ = std::make_unique<TestingProfileManager>(
@@ -487,6 +495,201 @@ class SiteSettingsHandlerBaseTest : public testing::Test {
*source);
}
+ void SetContentSettingCustomScope(
+ std::string primary_pattern,
+ std::string secondary_pattern,
+ ContentSettingsType content_setting_type,
+ ContentSetting content_setting,
+ size_t expected_total_calls = 1U,
+ bool is_incognito = false,
+ base::TimeDelta lifetime = base::TimeDelta(),
+ bool is_auto_granted = false) {
+ HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(
+ is_incognito ? incognito_profile() : profile());
+
+ content_settings::ContentSettingConstraints constraints;
+ constraints.set_lifetime(lifetime);
+ if (is_auto_granted) {
+ constraints.set_session_model(
+ content_settings::SessionModel::NonRestorableUserSession);
+ }
+
+ map->SetContentSettingCustomScope(
+ ContentSettingsPattern::FromString(primary_pattern),
+ ContentSettingsPattern::FromString(secondary_pattern),
+ content_setting_type, content_setting, constraints);
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+ ASSERT_EQ("contentSettingSitePermissionChanged",
+ web_ui()->call_data().back()->arg1()->GetString());
+ }
+
+ void ValidateStorageAccessList(size_t expected_total_calls,
+ size_t expected_num_groups) {
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIResponse", data.function_name());
+
+ ASSERT_TRUE(data.arg1()->is_string());
+ EXPECT_EQ(kCallbackId, data.arg1()->GetString());
+ ASSERT_TRUE(data.arg2()->is_bool());
+ ASSERT_TRUE(data.arg2()->GetBool());
+
+ ASSERT_TRUE(data.arg3()->is_list());
+ EXPECT_EQ(expected_num_groups, data.arg3()->GetList().size());
+ }
+
+ void ValidateStorageAccessException(
+ const std::string& expected_origin,
+ const std::string& expected_display_name,
+ const ContentSetting expected_setting,
+ const std::vector<EmbeddingStorageAccessException>
+ expected_embedding_exceptions,
+ size_t index = 0) {
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+
+ const base::Value::Dict& exception =
+ data.arg3()->GetList()[index].GetDict();
+
+ const auto* origin = exception.FindString(site_settings::kOrigin);
+ ASSERT_TRUE(origin);
+ ASSERT_EQ(expected_origin, *origin);
+
+ const auto* display_name =
+ exception.FindString(site_settings::kDisplayName);
+ ASSERT_TRUE(display_name);
+ ASSERT_EQ(expected_display_name, *display_name);
+
+ // Simple description and incognito should only be present for static
+ // exceptions.
+ const auto* description = exception.FindString(site_settings::kDescription);
+ ASSERT_FALSE(description);
+ absl::optional<bool> incognito =
+ exception.FindBool(site_settings::kIncognito);
+ ASSERT_FALSE(incognito.has_value());
+
+ const auto expected_close_description = l10n_util::GetPluralStringFUTF8(
+ IDS_DEL_SITE_SETTINGS_COUNTER, expected_embedding_exceptions.size());
+ const auto* close_description =
+ exception.FindString(site_settings::kCloseDescription);
+ ASSERT_TRUE(close_description);
+ ASSERT_EQ(expected_close_description, *close_description);
+
+ const auto expected_open_description = l10n_util::GetStringUTF8(
+ (expected_setting == ContentSetting::CONTENT_SETTING_ALLOW)
+ ? IDS_SETTINGS_STORAGE_ACCESS_ALLOWED_SITE_LABEL
+ : IDS_SETTINGS_STORAGE_ACCESS_BLOCKED_SITE_LABEL);
+ const auto* open_description =
+ exception.FindString(site_settings::kOpenDescription);
+ ASSERT_TRUE(open_description);
+ ASSERT_EQ(expected_open_description, *open_description);
+
+ const auto* setting = exception.FindString(site_settings::kSetting);
+ ASSERT_TRUE(setting);
+ ASSERT_EQ(content_settings::ContentSettingToString(expected_setting),
+ *setting);
+
+ const auto* exceptions_list =
+ exception.FindList(site_settings::kExceptions);
+ ASSERT_TRUE(exceptions_list);
+ ASSERT_EQ(expected_embedding_exceptions.size(), exceptions_list->size());
+
+ for (size_t i = 0; i < expected_embedding_exceptions.size(); i++) {
+ ValidateStorageAccessEmbeddingException(expected_embedding_exceptions[i],
+ (*exceptions_list)[i].GetDict());
+ }
+ }
+
+ void ValidateStorageAccessEmbeddingException(
+ EmbeddingStorageAccessException expected_embedding_exception,
+ const base::Value::Dict& embedding_exception) {
+ const auto* embedding_origin =
+ embedding_exception.FindString(site_settings::kEmbeddingOrigin);
+ ASSERT_TRUE(embedding_origin);
+ ASSERT_EQ(expected_embedding_exception.embedding_origin, *embedding_origin);
+
+ const auto* embedding_display_name =
+ embedding_exception.FindString(site_settings::kEmbeddingDisplayName);
+ ASSERT_TRUE(embedding_display_name);
+ ASSERT_EQ(expected_embedding_exception.embedding_display_name,
+ *embedding_display_name);
+
+ absl::optional<bool> incognito =
+ embedding_exception.FindBool(site_settings::kIncognito);
+ ASSERT_TRUE(incognito.has_value());
+ EXPECT_EQ(expected_embedding_exception.incognito, *incognito);
+
+ const std::string* description =
+ embedding_exception.FindString(site_settings::kDescription);
+
+ if (expected_embedding_exception.lifetime_in_days == 0 &&
+ !expected_embedding_exception.embargoed) {
+ ASSERT_FALSE(description);
+ return;
+ }
+
+ std::string expected_description =
+ expected_embedding_exception.embargoed
+ ? l10n_util::GetStringUTF8(
+ IDS_PAGE_INFO_PERMISSION_AUTOMATICALLY_BLOCKED)
+ : l10n_util::GetPluralStringFUTF8(
+ IDS_SETTINGS_EXPIRES_AFTER_TIME_LABEL,
+ expected_embedding_exception.lifetime_in_days);
+ ASSERT_TRUE(description);
+ ASSERT_EQ(expected_description, *description);
+ }
+
+ void ValidateStaticStorageAccessException(
+ const std::string& expected_origin,
+ const std::string& expected_display_name,
+ const ContentSetting expected_setting,
+ bool expected_incognito,
+ size_t index = 0) {
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+
+ const base::Value::Dict& exception =
+ data.arg3()->GetList()[index].GetDict();
+
+ const auto* origin = exception.FindString(site_settings::kOrigin);
+ ASSERT_TRUE(origin);
+ ASSERT_EQ(expected_origin, *origin);
+
+ const auto* display_name =
+ exception.FindString(site_settings::kDisplayName);
+ ASSERT_TRUE(display_name);
+ ASSERT_EQ(expected_display_name, *display_name);
+
+ // Close and open description should only be present for non-static
+ // exceptions.
+ const auto* close_description =
+ exception.FindString(site_settings::kCloseDescription);
+ ASSERT_FALSE(close_description);
+ const auto* open_description =
+ exception.FindString(site_settings::kOpenDescription);
+ ASSERT_FALSE(open_description);
+
+ std::string expected_description = l10n_util::GetStringUTF8(
+ IDS_PAGE_INFO_PERMISSION_AUTOMATICALLY_BLOCKED);
+ const auto* description = exception.FindString(site_settings::kDescription);
+ ASSERT_TRUE(description);
+ ASSERT_EQ(expected_description, *description);
+
+ const auto* setting = exception.FindString(site_settings::kSetting);
+ ASSERT_TRUE(setting);
+ ASSERT_EQ(content_settings::ContentSettingToString(expected_setting),
+ *setting);
+
+ absl::optional<bool> incognito =
+ exception.FindBool(site_settings::kIncognito);
+ ASSERT_TRUE(incognito.has_value());
+ EXPECT_EQ(expected_incognito, *incognito);
+
+ const auto* exceptions_list =
+ exception.FindList(site_settings::kExceptions);
+ ASSERT_TRUE(exceptions_list);
+ ASSERT_EQ(0U, exceptions_list->size());
+ }
+
void ValidateNoOrigin(size_t expected_total_calls) {
EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
@@ -718,18 +921,27 @@ class SiteSettingsHandlerBaseTest : public testing::Test {
models.browsing_data_model->AddBrowsingData(
url::Origin::Create(GURL("https://www.google.com")),
BrowsingDataModel::StorageType::kTrustTokens, 50000000000);
+ models.browsing_data_model->AddBrowsingData(
+ blink::StorageKey::Create(
+ url::Origin::Create(GURL("https://google.com/")),
+ net::SchemefulSite(
+ url::Origin::Create(GURL("https://www.example.com/"))),
+ blink::mojom::AncestorChainBit::kCrossSite,
+ /*third_party_partitioning_allowed=*/true),
+ BrowsingDataModel::StorageType::kQuotaStorage, 100);
}));
}
void SetupModelsWithIsolatedWebAppData(
- const std::string& isolated_web_app_url,
- int64_t usage) {
+ const std::vector<std::pair<std::string, int64_t>>& iwa_url_and_usage) {
SetupModels(base::BindLambdaForTesting([&](const TestModels& models) {
- models.browsing_data_model->AddBrowsingData(
- url::Origin::Create(GURL(isolated_web_app_url)),
- static_cast<BrowsingDataModel::StorageType>(
- ChromeBrowsingDataModelDelegate::StorageType::kIsolatedWebApp),
- usage);
+ for (const auto& url_and_usage : iwa_url_and_usage) {
+ models.browsing_data_model->AddBrowsingData(
+ url::Origin::Create(GURL(url_and_usage.first)),
+ static_cast<BrowsingDataModel::StorageType>(
+ ChromeBrowsingDataModelDelegate::StorageType::kIsolatedWebApp),
+ url_and_usage.second);
+ }
}));
}
@@ -747,7 +959,7 @@ class SiteSettingsHandlerBaseTest : public testing::Test {
std::vector<CookieTreeNode*> GetHostNodes(GURL url) {
std::vector<CookieTreeNode*> nodes;
for (const auto& host_node :
- handler()->cookies_tree_model_->GetRoot()->children()) {
+ handler()->GetCookiesTreeModelForTesting()->GetRoot()->children()) {
if (host_node->GetDetailedInfo().origin.GetURL() == url) {
nodes.push_back(host_node.get());
}
@@ -822,18 +1034,25 @@ class SiteSettingsHandlerBaseTest : public testing::Test {
}
// Content setting group name for the relevant ContentSettingsType.
- const std::string kNotifications;
- const std::string kCookies;
+ const std::string_view kNotifications =
+ site_settings::ContentSettingsTypeToGroupName(
+ ContentSettingsType::NOTIFICATIONS);
+ const std::string_view kCookies =
+ site_settings::ContentSettingsTypeToGroupName(
+ ContentSettingsType::COOKIES);
const ContentSettingsType kPermissionNotifications =
ContentSettingsType::NOTIFICATIONS;
+ const ContentSettingsType kPermissionStorageAccess =
+ ContentSettingsType::STORAGE_ACCESS;
// The number of listeners that are expected to fire when any content setting
// is changed.
const size_t kNumberContentSettingListeners = 2;
private:
- content::BrowserTaskEnvironment task_environment_;
+ content::BrowserTaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
std::unique_ptr<TestingProfileManager> testing_profile_manager_;
raw_ptr<TestingProfile> profile_ = nullptr;
raw_ptr<Profile, DanglingUntriaged> incognito_profile_ = nullptr;
@@ -881,6 +1100,8 @@ TEST_F(SiteSettingsHandlerTest, GetAndSetDefault) {
// Flaky on CrOS and Linux. https://crbug.com/930481
TEST_F(SiteSettingsHandlerTest, GetAllSites) {
+ SetupModels();
+
base::Value::List get_all_sites_args;
get_all_sites_args.Append(kCallbackId);
@@ -977,9 +1198,9 @@ TEST_F(SiteSettingsHandlerTest, GetAllSites) {
url4, ContentSettingsType::NOTIFICATIONS, false);
}
EXPECT_EQ(
- CONTENT_SETTING_BLOCK,
+ PermissionStatus::DENIED,
auto_blocker->GetEmbargoResult(url4, ContentSettingsType::NOTIFICATIONS)
- ->content_setting);
+ ->status);
handler()->HandleGetAllSites(get_all_sites_args);
{
@@ -1016,9 +1237,9 @@ TEST_F(SiteSettingsHandlerTest, GetAllSites) {
url3, ContentSettingsType::NOTIFICATIONS, false);
}
EXPECT_EQ(
- CONTENT_SETTING_BLOCK,
+ PermissionStatus::DENIED,
auto_blocker->GetEmbargoResult(url3, ContentSettingsType::NOTIFICATIONS)
- ->content_setting);
+ ->status);
clock.Advance(base::Days(8));
EXPECT_FALSE(
auto_blocker->GetEmbargoResult(url3, ContentSettingsType::NOTIFICATIONS)
@@ -1046,9 +1267,9 @@ TEST_F(SiteSettingsHandlerTest, GetAllSites) {
url5, ContentSettingsType::NOTIFICATIONS, false);
}
EXPECT_EQ(
- CONTENT_SETTING_BLOCK,
+ PermissionStatus::DENIED,
auto_blocker->GetEmbargoResult(url5, ContentSettingsType::NOTIFICATIONS)
- ->content_setting);
+ ->status);
clock.Advance(base::Days(8));
EXPECT_FALSE(
auto_blocker->GetEmbargoResult(url5, ContentSettingsType::NOTIFICATIONS)
@@ -1217,8 +1438,8 @@ TEST_F(SiteSettingsHandlerTest, Cookies) {
TEST_F(SiteSettingsHandlerTest, GetRecentSitePermissions) {
// Constants used only in this test.
- std::string kAllowed = content_settings::ContentSettingToString(
- ContentSetting::CONTENT_SETTING_ALLOW);
+ std::string kAllowed =
+ content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW);
std::string kBlocked = content_settings::ContentSettingToString(
ContentSetting::CONTENT_SETTING_BLOCK);
std::string kEmbargo =
@@ -1351,10 +1572,10 @@ TEST_F(SiteSettingsHandlerTest, OnStorageFetched) {
const base::Value::List* origin_list = site_group.FindList("origins");
ASSERT_TRUE(origin_list);
- // There will be 2 origins in this case. Cookie node with url
+ // There will be 3 origins in this case. Cookie node with url
// http://www.example.com/ will be treat as https://www.example.com/ because
// this url existed in the storage nodes.
- EXPECT_EQ(2U, origin_list->size());
+ EXPECT_EQ(3U, origin_list->size());
const base::Value::Dict& origin_info_0 = (*origin_list)[0].GetDict();
@@ -1363,16 +1584,27 @@ TEST_F(SiteSettingsHandlerTest, OnStorageFetched) {
EXPECT_EQ(0, origin_info_0.FindDouble("engagement"));
EXPECT_EQ(0, origin_info_0.FindDouble("usage"));
EXPECT_EQ(1, origin_info_0.FindDouble("numCookies"));
+ EXPECT_FALSE(origin_info_0.FindBool("isPartitioned").value_or(false));
const base::Value::Dict& origin_info_1 = (*origin_list)[1].GetDict();
+ EXPECT_EQ("https://google.com/",
+ CHECK_DEREF(origin_info_1.FindString("origin")));
+ EXPECT_EQ(0, origin_info_1.FindDouble("engagement"));
+ EXPECT_EQ(0, origin_info_1.FindDouble("usage"));
+ EXPECT_EQ(0, origin_info_1.FindDouble("numCookies"));
+ EXPECT_TRUE(origin_info_1.FindBool("isPartitioned").value_or(false));
+
+ const base::Value::Dict& origin_info_2 = (*origin_list)[2].GetDict();
+
// Even though in the cookies the scheme is http, it still stored as https
// because there is https data stored.
EXPECT_EQ("https://www.example.com/",
- CHECK_DEREF(origin_info_1.FindString("origin")));
- EXPECT_EQ(0, origin_info_1.FindDouble("engagement"));
- EXPECT_EQ(2, origin_info_1.FindDouble("usage"));
- EXPECT_EQ(1, origin_info_1.FindDouble("numCookies"));
+ CHECK_DEREF(origin_info_2.FindString("origin")));
+ EXPECT_EQ(0, origin_info_2.FindDouble("engagement"));
+ EXPECT_EQ(2, origin_info_2.FindDouble("usage"));
+ EXPECT_EQ(1, origin_info_2.FindDouble("numCookies"));
+ EXPECT_FALSE(origin_info_2.FindBool("isPartitioned").value_or(false));
}
{
@@ -1641,10 +1873,10 @@ TEST_F(SiteSettingsHandlerTest, ResetCategoryPermissionForEmbargoedOrigins) {
}
// Check that origin is under embargo.
EXPECT_EQ(
- CONTENT_SETTING_BLOCK,
+ PermissionStatus::DENIED,
auto_blocker
->GetEmbargoResult(GURL(kOriginToEmbargo), kPermissionNotifications)
- ->content_setting);
+ ->status);
}
// Check there are 2 blocked origins.
@@ -1994,12 +2226,14 @@ TEST_F(SiteSettingsHandlerTest, ExceptionHelpers) {
CONTENT_SETTING_BLOCK,
site_settings::SiteSettingSourceToString(
site_settings::SiteSettingSource::kPreference),
- false);
+ /*expiration=*/base::Time::Now(), /*incognito=*/false);
CHECK(exception.FindString(site_settings::kOrigin));
CHECK(exception.FindString(site_settings::kDisplayName));
CHECK(exception.FindString(site_settings::kEmbeddingOrigin));
CHECK(exception.FindString(site_settings::kSetting));
+ // Notifications should not have a description.
+ CHECK(!exception.FindString(site_settings::kDescription));
CHECK(exception.FindBool(site_settings::kIncognito).has_value());
base::Value::List args;
@@ -2141,6 +2375,44 @@ TEST_F(SiteSettingsHandlerTest, ZoomLevels) {
EXPECT_EQ(default_level, level);
}
+TEST_F(SiteSettingsHandlerTest, TemporaryCookieExceptions) {
+ // Set a temporary exception directly, instead of relying on any helpers that
+ // have duration configurable via feature parameters.
+ constexpr int kExpirationDurationInDays = 100;
+
+ content_settings::ContentSettingConstraints constraints;
+ constraints.set_lifetime(base::Days(kExpirationDurationInDays));
+ constraints.set_session_model(content_settings::SessionModel::Durable);
+
+ HostContentSettingsMap* host_content_settings_map =
+ HostContentSettingsMapFactory::GetForProfile(profile());
+
+ host_content_settings_map->SetContentSettingCustomScope(
+ ContentSettingsPattern::Wildcard(),
+ ContentSettingsPattern::FromURL(GURL("https://example.com")),
+ ContentSettingsType::COOKIES, ContentSetting::CONTENT_SETTING_ALLOW,
+ constraints);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(kCookies);
+
+ handler()->HandleGetExceptionList(get_exception_list_args);
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ const base::Value::List& exception_list = data.arg3()->GetList();
+ EXPECT_EQ(1UL, exception_list.size());
+
+ // Mirror the logic in the helper to avoid flakes on time edges.
+ auto time_diff = (base::Time::Now() + base::Days(kExpirationDurationInDays))
+ .LocalMidnight() -
+ base::Time::Now().LocalMidnight();
+
+ EXPECT_EQ(l10n_util::GetPluralStringFUTF8(
+ IDS_SETTINGS_EXPIRES_AFTER_TIME_LABEL, time_diff.InDays()),
+ CHECK_DEREF(exception_list[0].GetDict().FindString("description")));
+}
+
class SiteSettingsHandlerIsolatedWebAppTest : public SiteSettingsHandlerTest {
public:
void SetUp() override {
@@ -2178,7 +2450,7 @@ class SiteSettingsHandlerIsolatedWebAppTest : public SiteSettingsHandlerTest {
TEST_F(SiteSettingsHandlerIsolatedWebAppTest, AllSitesDisplaysAppName) {
GURL https_url("https://" + iwa_url().host());
- SetupModelsWithIsolatedWebAppData(iwa_url().spec(), 50);
+ SetupModelsWithIsolatedWebAppData({{iwa_url().spec(), 50}});
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile());
map->SetContentSettingDefaultScope(iwa_url(), iwa_url(),
@@ -2267,9 +2539,7 @@ TEST_F(SiteSettingsHandlerIsolatedWebAppTest, ZoomLevelsSortedByAppName) {
class SiteSettingsHandlerInfobarTest : public BrowserWithTestWindowTest {
public:
- SiteSettingsHandlerInfobarTest()
- : kNotifications(site_settings::ContentSettingsTypeToGroupName(
- ContentSettingsType::NOTIFICATIONS)) {}
+ SiteSettingsHandlerInfobarTest() = default;
SiteSettingsHandlerInfobarTest(const SiteSettingsHandlerInfobarTest&) =
delete;
SiteSettingsHandlerInfobarTest& operator=(
@@ -2367,7 +2637,9 @@ class SiteSettingsHandlerInfobarTest : public BrowserWithTestWindowTest {
GetTestingFactories());
}
- const std::string kNotifications;
+ const std::string_view kNotifications =
+ site_settings::ContentSettingsTypeToGroupName(
+ ContentSettingsType::NOTIFICATIONS);
private:
content::TestWebUI web_ui_;
@@ -2631,6 +2903,7 @@ TEST_F(SiteSettingsHandlerTest, BlockAutoplay_Update) {
}
TEST_F(SiteSettingsHandlerTest, ExcludeWebUISchemesInLists) {
+ SetupModels();
const ContentSettingsType content_settings_type =
ContentSettingsType::NOTIFICATIONS;
// Register WebUIAllowlist auto-granted permissions.
@@ -2753,6 +3026,477 @@ TEST_F(SiteSettingsHandlerTest, IncludeWebUISchemesInGetOriginPermissions) {
}
}
+TEST_F(SiteSettingsHandlerTest, StorageAccessExceptions_DiffPatterns) {
+ const std::string kOrigin("https://[*.]google.com:443");
+ const std::string kDisplayName("google.com");
+
+ const std::string kOrigin2("https://[*.]google2.com:443");
+ const std::string kDisplayName2("google2.com");
+
+ const std::string kEmbeddingOrigin("https://[*.]example.com:443");
+ const std::string kEmbeddingDisplayName("example.com");
+
+ const std::string kEmbeddingOrigin2("https://[*.]example2.com:443");
+ const std::string kEmbeddingDisplayName2("example2.com");
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK);
+ SetContentSettingCustomScope(kOrigin2, kEmbeddingOrigin2,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK,
+ /*expected_total_calls=*/2U);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that the exception list is correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/3U,
+ /*expected_num_groups=*/2U);
+
+ // Verify that the first group exception is correct.
+ ValidateStorageAccessException(
+ kOrigin, kDisplayName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/false}},
+ /*index=*/0U);
+
+ // Verify that the second group exception is correct.
+ ValidateStorageAccessException(
+ kOrigin2, kDisplayName2, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin2, kEmbeddingDisplayName2, /*incognito=*/false}},
+ /*index=*/1U);
+}
+
+TEST_F(SiteSettingsHandlerTest, StorageAccessExceptions_SamePrimaryPattern) {
+ const std::string kOrigin("https://[*.]google.com:443");
+ const std::string kDisplayName("google.com");
+
+ const std::string kEmbeddingOrigin("https://[*.]example.com:443");
+ const std::string kEmbeddingDisplayName("example.com");
+
+ const std::string kEmbeddingOrigin2("https://[*.]example2.com:443");
+ const std::string kEmbeddingDisplayName2("example2.com");
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK);
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin2,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK,
+ /*expected_total_calls=*/2U);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that the group exception is correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/3U,
+ /*expected_num_groups=*/1U);
+
+ ValidateStorageAccessException(
+ kOrigin, kDisplayName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin2, kEmbeddingDisplayName2, /*incognito=*/false},
+ {kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/false}},
+ /*index=*/0U);
+}
+
+TEST_F(SiteSettingsHandlerTest, StorageAccessExceptions_DiffType) {
+ const std::string kOrigin("https://[*.]google.com:443");
+ const std::string kEmbeddingOrigin("https://[*.]example.com:443");
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that no exception is returned.
+ ValidateNoOrigin(2U);
+}
+
+TEST_F(SiteSettingsHandlerTest, StorageAccessExceptions_AutoGranted) {
+ const std::string kOrigin("https://[*.]google.com:443");
+ const std::string kEmbeddingOrigin("https://[*.]example.com:443");
+
+ SetContentSettingCustomScope(
+ kOrigin, kEmbeddingOrigin, kPermissionStorageAccess,
+ CONTENT_SETTING_BLOCK, /*expected_total_calls=*/1U,
+ /*is_incognito=*/false, /*lifetime=*/base::TimeDelta(),
+ /*is_auto_granted=*/true);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that no exception is returned since auto granted exceptions should
+ // not be returned.
+ ValidateNoOrigin(2U);
+}
+
+TEST_F(SiteSettingsHandlerTest, StorageAccessExceptions_Incognito) {
+ const std::string kOrigin("https://[*.]google.com:443");
+ const std::string kDisplayName("google.com");
+
+ const std::string kEmbeddingOrigin("https://[*.]example.com:443");
+ const std::string kEmbeddingDisplayName("example.com");
+
+ CreateIncognitoProfile();
+ ValidateIncognitoExists(true, 1U);
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK,
+ /*expected_total_calls=*/2U,
+ /*is_incognito=*/true);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that the group exception is correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/3U,
+ /*expected_num_groups=*/1U);
+ ValidateStorageAccessException(
+ kOrigin, kDisplayName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/true}});
+}
+
+TEST_F(SiteSettingsHandlerTest, StorageAccessExceptions_NormalAndIncognito) {
+ const std::string kOrigin("https://[*.]google.com:443");
+ const std::string kDisplayName("google.com");
+
+ const std::string kOrigin2("https://[*.]google2.com:443");
+ const std::string kDisplayName2("google2.com");
+
+ const std::string kEmbeddingOrigin("https://[*.]example.com:443");
+ const std::string kEmbeddingDisplayName("example.com");
+
+ const std::string kEmbeddingOrigin2("https://[*.]example2.com:443");
+ const std::string kEmbeddingDisplayName2("example2.com");
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK);
+
+ CreateIncognitoProfile();
+ ValidateIncognitoExists(true, 2U);
+
+ SetContentSettingCustomScope(kOrigin2, kEmbeddingOrigin2,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK,
+ /*expected_total_calls=*/3U,
+ /*is_incognito=*/true);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ ValidateStorageAccessList(/*expected_total_calls=*/4U,
+ /*expected_num_groups=*/2U);
+
+ // Verify that group exception for non-incognito is correct.
+ ValidateStorageAccessException(
+ kOrigin, kDisplayName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/false}},
+ /*index=*/0U);
+
+ // Verify that group exception for incognito is correct.
+ ValidateStorageAccessException(
+ kOrigin2, kDisplayName2, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin2, kEmbeddingDisplayName2, /*incognito=*/true}},
+ /*index=*/1U);
+
+ DestroyIncognitoProfile();
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Check that the incognito exception gets deleted if the incognito profile is
+ // destroyed.
+ ValidateStorageAccessList(/*expected_total_calls=*/6U,
+ /*expected_num_groups=*/1U);
+ ValidateStorageAccessException(
+ kOrigin, kDisplayName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/false}},
+ /*index=*/0U);
+}
+
+TEST_F(SiteSettingsHandlerTest,
+ StorageAccessExceptions_NormalAndIncognito_SamePatterns) {
+ const std::string kOrigin("https://[*.]google.com:443");
+ const std::string kDisplayName("google.com");
+
+ const std::string kEmbeddingOrigin("https://[*.]example.com:443");
+ const std::string kEmbeddingDisplayName("example.com");
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK);
+
+ CreateIncognitoProfile();
+ ValidateIncognitoExists(true, 2U);
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK,
+ /*expected_total_calls=*/3U,
+ /*is_incognito=*/true);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that group exception for non-incognito and incognito is correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/4U,
+ /*expected_num_groups=*/1U);
+ ValidateStorageAccessException(
+ kOrigin, kDisplayName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/false},
+ {kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/true}});
+}
+
+TEST_F(SiteSettingsHandlerTest, StorageAccessExceptions_Extension) {
+ auto extension = LoadExtension(kExtensionName);
+ auto extension_url = extension->url().spec();
+
+ const std::string kEmbeddingOrigin("https://[*.]example.com:443");
+ const std::string kEmbeddingDisplayName("example.com");
+
+ SetContentSettingCustomScope(extension_url, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that the grouped exception is correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/2U,
+ /*expected_num_groups=*/1U);
+ ValidateStorageAccessException(
+ extension_url, kExtensionName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/false}});
+
+ // When the extension is unloaded, the display name should be the plain url.
+ UnloadExtension(extension->id());
+ // Display name does not have a trailing '/'.
+ const auto extensionDisplayName =
+ extension_url.substr(0, extension_url.length() - 1);
+
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+ ValidateStorageAccessList(/*expected_total_calls=*/3U,
+ /*expected_num_groups=*/1U);
+
+ ValidateStorageAccessException(
+ extension_url, extensionDisplayName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/false}});
+}
+
+TEST_P(SiteSettingsHandlerTest, StorageAccessExceptions_Description_All) {
+ const std::string kOrigin("google.com");
+ const std::string kEmbeddingOrigin("example.com");
+
+ const ContentSetting content_setting =
+ GetParam() ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, content_setting);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(content_setting));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that the grouped exception is correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/2U,
+ /*expected_num_groups=*/1U);
+
+ ValidateStorageAccessException(
+ kOrigin, kOrigin, content_setting,
+ {{kEmbeddingOrigin, kEmbeddingOrigin, /*incognito=*/false}});
+}
+
+TEST_F(SiteSettingsHandlerTest, StorageAccessExceptions_Description_Embargoed) {
+ const std::string kOrigin("https://google.com:443");
+ const std::string kDisplayName("google.com");
+
+ // Set an embargoed setting.
+ permissions::PermissionDecisionAutoBlocker* auto_blocker =
+ PermissionDecisionAutoBlockerFactory::GetForProfile(profile());
+ base::SimpleTestClock clock;
+ clock.SetNow(base::Time::Now());
+ auto_blocker->SetClockForTesting(&clock);
+ for (int i = 0; i < 3; ++i) {
+ auto_blocker->RecordDismissAndEmbargo(GURL(kOrigin),
+ kPermissionStorageAccess, false);
+ }
+ EXPECT_EQ(
+ PermissionStatus::DENIED,
+ auto_blocker->GetEmbargoResult(GURL(kOrigin), kPermissionStorageAccess)
+ ->status);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that group exception with an embargoed is correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/1U,
+ /*expected_num_groups=*/1U);
+ ValidateStaticStorageAccessException(kOrigin, kDisplayName,
+ CONTENT_SETTING_BLOCK,
+ /*expected_incognito=*/false);
+}
+
+TEST_F(SiteSettingsHandlerTest,
+ StorageAccessExceptions_Description_EmbargoedTwoProfiles) {
+ const std::string kOrigin("https://google.com:443");
+ const std::string kDisplayName("google.com");
+
+ // Set an embargoed setting for regular and incognito profile
+ base::SimpleTestClock clock;
+ clock.SetNow(base::Time::Now());
+
+ permissions::PermissionDecisionAutoBlocker* auto_blocker =
+ PermissionDecisionAutoBlockerFactory::GetForProfile(profile());
+ auto_blocker->SetClockForTesting(&clock);
+ for (int i = 0; i < 3; ++i) {
+ auto_blocker->RecordDismissAndEmbargo(GURL(kOrigin),
+ kPermissionStorageAccess, false);
+ }
+ EXPECT_EQ(
+ PermissionStatus::DENIED,
+ auto_blocker->GetEmbargoResult(GURL(kOrigin), kPermissionStorageAccess)
+ ->status);
+
+ CreateIncognitoProfile();
+ permissions::PermissionDecisionAutoBlocker* auto_blocker_incognito =
+ PermissionDecisionAutoBlockerFactory::GetForProfile(incognito_profile());
+ auto_blocker_incognito->SetClockForTesting(&clock);
+ for (int i = 0; i < 3; ++i) {
+ auto_blocker_incognito->RecordDismissAndEmbargo(
+ GURL(kOrigin), kPermissionStorageAccess, false);
+ }
+ EXPECT_EQ(PermissionStatus::DENIED,
+ auto_blocker_incognito
+ ->GetEmbargoResult(GURL(kOrigin), kPermissionStorageAccess)
+ ->status);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that group exception with an embargoed origin in two profiles is
+ // correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/2U,
+ /*expected_num_groups=*/1U);
+ ValidateStorageAccessException(kOrigin, kDisplayName, CONTENT_SETTING_BLOCK,
+ {{std::string(), "*", /*incognito=*/false,
+ /*embargoed=*/true, 0},
+ {std::string(), "*", /*incognito=*/true,
+ /*embargoed=*/true, 0}});
+}
+
+typedef std::pair<std::string_view, std::string_view> OriginStringParams;
+class StorageAccessSiteSettingsHandlerTest
+ : public SiteSettingsHandlerBaseTest,
+ public testing::WithParamInterface<
+ std::tuple<OriginStringParams, OriginStringParams>> {};
+
+// Several pairs <origin, displayName> to test on both the embedded and
+// embedding.
+constexpr OriginStringParams kOrigins[] = {
+ {"192.168.0.1", "192.168.0.1"},
+ {"google.com", "google.com"},
+ {"google.com:443", "google.com"},
+ {"docs.google.com:443", "docs.google.com"},
+ {"https://[*.]google.com", "google.com"},
+ {"https://[*.]docs.example.com:443", "docs.example.com"},
+ {"chrome-extension://peoadpeiejnhkmpaakpnompolbglelel/",
+ "chrome-extension://peoadpeiejnhkmpaakpnompolbglelel"}};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ StorageAccessSiteSettingsHandlerTest,
+ testing::Combine(testing::ValuesIn(kOrigins),
+ testing::ValuesIn(kOrigins)));
+
+TEST_P(StorageAccessSiteSettingsHandlerTest, StorageAccessExceptions_Origins) {
+ OriginStringParams embedded = std::get<0>(GetParam());
+ const std::string kOrigin(std::get<0>(embedded));
+ const std::string kDisplayName(std::get<1>(embedded));
+
+ OriginStringParams embedding = std::get<1>(GetParam());
+ const std::string kEmbeddingOrigin(std::get<0>(embedding));
+ const std::string kEmbeddingDisplayName(std::get<1>(embedding));
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK);
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that the grouped exception is correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/2U,
+ /*expected_num_groups=*/1U);
+
+ ValidateStorageAccessException(
+ kOrigin, kDisplayName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/false}});
+}
+
+class StorageAccessSiteSettingsHandlerLifetimeTest
+ : public SiteSettingsHandlerBaseTest,
+ public testing::WithParamInterface<int> {};
+
+// Range for the lifetime in days of a Storage Access permission.
+INSTANTIATE_TEST_SUITE_P(All,
+ StorageAccessSiteSettingsHandlerLifetimeTest,
+ testing::Range(0, 3));
+
+TEST_P(StorageAccessSiteSettingsHandlerLifetimeTest,
+ StorageAccessExceptions_Description) {
+ const std::string kOrigin("https://[*.]google.com:443");
+ const std::string kDisplayName("google.com");
+
+ const std::string kEmbeddingOrigin("https://[*.]example.com:443");
+ const std::string kEmbeddingDisplayName("example.com");
+
+ const int kLifetimeInDays = GetParam();
+
+ SetContentSettingCustomScope(kOrigin, kEmbeddingOrigin,
+ kPermissionStorageAccess, CONTENT_SETTING_BLOCK,
+ /*expected_total_calls=*/1U,
+ /*is_incognito=*/false,
+ base::Days(kLifetimeInDays));
+
+ base::Value::List get_exception_list_args;
+ get_exception_list_args.Append(kCallbackId);
+ get_exception_list_args.Append(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleGetStorageAccessExceptionList(get_exception_list_args);
+
+ // Verify that group exception description with expiration is correct.
+ ValidateStorageAccessList(/*expected_total_calls=*/2U,
+ /*expected_num_groups=*/1U);
+ ValidateStorageAccessException(
+ kOrigin, kDisplayName, CONTENT_SETTING_BLOCK,
+ {{kEmbeddingOrigin, kEmbeddingDisplayName, /*incognito=*/false,
+ /*embargoed=*/false, kLifetimeInDays}});
+}
+
class PersistentPermissionsSiteSettingsHandlerTest
: public SiteSettingsHandlerTest {
void SetUp() override {
@@ -2799,42 +3543,36 @@ TEST_F(PersistentPermissionsSiteSettingsHandlerTest,
const base::FilePath kTestPath4 =
base::FilePath(FILE_PATH_LITERAL("/f/g/h/"));
+ context->SetOriginHasExtendedPermissionForTesting(kTestOrigin1);
+ context->SetOriginHasExtendedPermissionForTesting(kTestOrigin2);
+
// Populate the `grants` object with permissions.
- auto file_read_grant = context->GetPersistedReadPermissionGrantForTesting(
+ auto file_read_grant = context->GetExtendedReadPermissionGrantForTesting(
kTestOrigin1, kTestPath,
ChromeFileSystemAccessPermissionContext::HandleType::kFile);
- auto file_write_grant = context->GetPersistedWritePermissionGrantForTesting(
+ auto file_write_grant = context->GetExtendedWritePermissionGrantForTesting(
kTestOrigin2, kTestPath2,
ChromeFileSystemAccessPermissionContext::HandleType::kFile);
- auto directory_read_grant =
- context->GetPersistedReadPermissionGrantForTesting(
- kTestOrigin1, kTestPath3,
- ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
+ auto directory_read_grant = context->GetExtendedReadPermissionGrantForTesting(
+ kTestOrigin1, kTestPath3,
+ ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
auto directory_write_grant =
- context->GetPersistedWritePermissionGrantForTesting(
+ context->GetExtendedWritePermissionGrantForTesting(
kTestOrigin2, kTestPath4,
ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
- EXPECT_EQ(context->GetPermissionGrants(kTestOrigin1).file_read_grants.size(),
- 1UL);
- EXPECT_EQ(context->GetPermissionGrants(kTestOrigin2).file_read_grants.size(),
- 0UL);
- EXPECT_EQ(context->GetPermissionGrants(kTestOrigin1).file_write_grants.size(),
- 0UL);
- EXPECT_EQ(context->GetPermissionGrants(kTestOrigin2).file_write_grants.size(),
- 1UL);
- EXPECT_EQ(
- context->GetPermissionGrants(kTestOrigin1).directory_read_grants.size(),
- 1UL);
- EXPECT_EQ(
- context->GetPermissionGrants(kTestOrigin2).directory_read_grants.size(),
- 0UL);
- EXPECT_EQ(
- context->GetPermissionGrants(kTestOrigin1).directory_write_grants.size(),
- 0UL);
- EXPECT_EQ(
- context->GetPermissionGrants(kTestOrigin2).directory_write_grants.size(),
- 1UL);
+ auto kTestOrigin1Grants =
+ context->ConvertObjectsToGrants(context->GetGrantedObjects(kTestOrigin1));
+ auto kTestOrigin2Grants =
+ context->ConvertObjectsToGrants(context->GetGrantedObjects(kTestOrigin2));
+ EXPECT_EQ(kTestOrigin1Grants.file_read_grants.size(), 1UL);
+ EXPECT_EQ(kTestOrigin2Grants.file_read_grants.size(), 0UL);
+ EXPECT_EQ(kTestOrigin1Grants.file_write_grants.size(), 0UL);
+ EXPECT_EQ(kTestOrigin2Grants.file_write_grants.size(), 1UL);
+ EXPECT_EQ(kTestOrigin1Grants.directory_read_grants.size(), 1UL);
+ EXPECT_EQ(kTestOrigin2Grants.directory_read_grants.size(), 0UL);
+ EXPECT_EQ(kTestOrigin1Grants.directory_write_grants.size(), 0UL);
+ EXPECT_EQ(kTestOrigin2Grants.directory_write_grants.size(), 1UL);
base::Value::List get_file_system_permissions_args;
get_file_system_permissions_args.Append(kCallbackId);
@@ -2851,55 +3589,42 @@ TEST_F(PersistentPermissionsSiteSettingsHandlerTest,
EXPECT_EQ(CHECK_DEREF(second_grant.FindString(site_settings::kOrigin)),
"https://www.b.com/");
- const base::Value::List* kTestOrigin1FileReadGrants =
- first_grant.FindList(site_settings::kFileReadGrants);
- const base::Value::List* kTestOrigin1FileWriteGrants =
- first_grant.FindList(site_settings::kFileWriteGrants);
- const base::Value::List* kTestOrigin1DirectoryReadGrants =
- first_grant.FindList(site_settings::kDirectoryReadGrants);
- const base::Value::List* kTestOrigin1DirectoryWriteGrants =
- first_grant.FindList(site_settings::kDirectoryWriteGrants);
-
- const base::Value::List* kTestOrigin2FileReadGrants =
- second_grant.FindList(site_settings::kFileReadGrants);
- const base::Value::List* kTestOrigin2FileWriteGrants =
- second_grant.FindList(site_settings::kFileWriteGrants);
- const base::Value::List* kTestOrigin2DirectoryReadGrants =
- second_grant.FindList(site_settings::kDirectoryReadGrants);
- const base::Value::List* kTestOrigin2DirectoryWriteGrants =
- second_grant.FindList(site_settings::kDirectoryWriteGrants);
+ const base::Value::List* kTestOrigin1ViewGrants =
+ first_grant.FindList(site_settings::kViewGrants);
+ const base::Value::List* kTestOrigin1EditGrants =
+ first_grant.FindList(site_settings::kEditGrants);
+
+ const base::Value::List* kTestOrigin2ViewGrants =
+ second_grant.FindList(site_settings::kViewGrants);
+ const base::Value::List* kTestOrigin2EditGrants =
+ second_grant.FindList(site_settings::kEditGrants);
// Checks that the grants for test origins are populated as expected.
- EXPECT_FALSE(CHECK_DEREF(kTestOrigin1FileReadGrants)[0]
- .GetDict()
- .FindBool(site_settings::kIsDirectory)
- .value_or(true));
- ASSERT_TRUE(kTestOrigin1FileWriteGrants != nullptr);
- EXPECT_TRUE(kTestOrigin1FileWriteGrants->empty());
+ EXPECT_TRUE(CHECK_DEREF(kTestOrigin1ViewGrants)[0]
+ .GetDict()
+ .FindBool(site_settings::kIsDirectory)
+ .value_or(false));
EXPECT_EQ(
- CHECK_DEREF(
- CHECK_DEREF(kTestOrigin1DirectoryReadGrants)[0].GetDict().FindString(
- site_settings::kFilePath)),
- "/e/");
- ASSERT_TRUE(kTestOrigin1DirectoryWriteGrants != nullptr);
- EXPECT_TRUE(kTestOrigin1DirectoryWriteGrants->empty());
+ CHECK_DEREF(CHECK_DEREF(kTestOrigin1ViewGrants)[1].GetDict().FindString(
+ site_settings::kFilePath)),
+ "/a/b");
+ ASSERT_TRUE(kTestOrigin1EditGrants != nullptr);
+ EXPECT_TRUE(kTestOrigin1EditGrants->empty());
// In the case of kTestOrigin2, check that when an origin has an
// associated 'write' grant, that the grant is only recorded in the
// respective write grants list, and is not recorded in the origin's
// read grants list.
- ASSERT_TRUE(kTestOrigin2FileReadGrants != nullptr);
- EXPECT_TRUE(kTestOrigin2FileReadGrants->empty());
- EXPECT_TRUE(CHECK_DEREF(kTestOrigin2FileWriteGrants)[0]
- .GetDict()
- .FindBool(site_settings::kIsWritable)
- .value_or(false));
- ASSERT_TRUE(kTestOrigin2DirectoryReadGrants != nullptr);
- EXPECT_TRUE(kTestOrigin2DirectoryReadGrants->empty());
- EXPECT_TRUE(CHECK_DEREF(kTestOrigin2DirectoryWriteGrants)[0]
- .GetDict()
- .FindBool(site_settings::kIsDirectory)
- .value_or(false));
+ ASSERT_TRUE(kTestOrigin2ViewGrants != nullptr);
+ EXPECT_TRUE(kTestOrigin2ViewGrants->empty());
+ EXPECT_EQ(
+ CHECK_DEREF(CHECK_DEREF(kTestOrigin2EditGrants)[0].GetDict().FindString(
+ site_settings::kDisplayName)),
+ "/f/g/h/");
+ EXPECT_FALSE(CHECK_DEREF(kTestOrigin2EditGrants)[1]
+ .GetDict()
+ .FindBool(site_settings::kIsDirectory)
+ .value_or(true));
}
// RevokeGrant() revokes a single File System Access permission grant,
@@ -2918,20 +3643,22 @@ TEST_F(PersistentPermissionsSiteSettingsHandlerTest,
const base::FilePath kTestPath4 =
base::FilePath(FILE_PATH_LITERAL("/f/g/h/"));
+ context->SetOriginHasExtendedPermissionForTesting(kTestOrigin1);
+ context->SetOriginHasExtendedPermissionForTesting(kTestOrigin2);
+
// Populate the `grants` object with permissions.
- auto file_read_grant = context->GetPersistedReadPermissionGrantForTesting(
+ auto file_read_grant = context->GetExtendedReadPermissionGrantForTesting(
kTestOrigin1, kTestPath,
ChromeFileSystemAccessPermissionContext::HandleType::kFile);
- auto directory_read_grant =
- context->GetPersistedReadPermissionGrantForTesting(
- kTestOrigin1, kTestPath2,
- ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
+ auto directory_read_grant = context->GetExtendedReadPermissionGrantForTesting(
+ kTestOrigin1, kTestPath2,
+ ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
auto directory_write_grant =
- context->GetPersistedWritePermissionGrantForTesting(
+ context->GetExtendedWritePermissionGrantForTesting(
kTestOrigin2, kTestPath3,
ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
auto second_directory_write_grant =
- context->GetPersistedWritePermissionGrantForTesting(
+ context->GetExtendedWritePermissionGrantForTesting(
kTestOrigin2, kTestPath4,
ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
@@ -2947,14 +3674,10 @@ TEST_F(PersistentPermissionsSiteSettingsHandlerTest,
const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
const base::Value::List& grants = data.arg3()->GetList();
- // After revoking the `file_read_grant` for kTestOrigin1, only one
- // `directory_read_grant` should remain when retrieving the file system grants
- // for kTestOrigin1.
- EXPECT_TRUE(
- grants[0].GetDict().FindList(site_settings::kFileReadGrants)->empty());
- EXPECT_EQ(
- grants[0].GetDict().FindList(site_settings::kDirectoryReadGrants)->size(),
- 1UL);
+ // After revoking the `file_read_grant` for kTestOrigin1, only one view grant
+ // should remain when retrieving the file system grants for kTestOrigin1.
+ EXPECT_EQ(grants[0].GetDict().FindList(site_settings::kViewGrants)->size(),
+ 1UL);
// Revoking a single grant from an origin with multiple grants in a given
// grants list only revokes the grant with the given file path.
@@ -2971,13 +3694,11 @@ TEST_F(PersistentPermissionsSiteSettingsHandlerTest,
*web_ui()->call_data().back();
const base::Value::List& updated_grants = updated_data.arg3()->GetList();
- EXPECT_EQ(updated_grants[1]
- .GetDict()
- .FindList(site_settings::kDirectoryWriteGrants)
- ->size(),
- 1UL);
+ EXPECT_EQ(
+ updated_grants[1].GetDict().FindList(site_settings::kEditGrants)->size(),
+ 1UL);
EXPECT_EQ(CHECK_DEREF(CHECK_DEREF(updated_grants[1].GetDict().FindList(
- site_settings::kDirectoryWriteGrants))[0]
+ site_settings::kEditGrants))[0]
.GetDict()
.FindString(site_settings::kFilePath)),
"/f/g/h/");
@@ -2999,19 +3720,21 @@ TEST_F(PersistentPermissionsSiteSettingsHandlerTest,
const base::FilePath kTestPath4 =
base::FilePath(FILE_PATH_LITERAL("/f/g/h/"));
+ context->SetOriginHasExtendedPermissionForTesting(kTestOrigin1);
+ context->SetOriginHasExtendedPermissionForTesting(kTestOrigin2);
+
// Populate the `grants` object with permissions.
- auto file_read_grant = context->GetPersistedReadPermissionGrantForTesting(
+ auto file_read_grant = context->GetExtendedReadPermissionGrantForTesting(
kTestOrigin1, kTestPath,
ChromeFileSystemAccessPermissionContext::HandleType::kFile);
- auto file_write_grant = context->GetPersistedWritePermissionGrantForTesting(
+ auto file_write_grant = context->GetExtendedWritePermissionGrantForTesting(
kTestOrigin2, kTestPath2,
ChromeFileSystemAccessPermissionContext::HandleType::kFile);
- auto directory_read_grant =
- context->GetPersistedReadPermissionGrantForTesting(
- kTestOrigin1, kTestPath3,
- ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
+ auto directory_read_grant = context->GetExtendedReadPermissionGrantForTesting(
+ kTestOrigin1, kTestPath3,
+ ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
auto directory_write_grant =
- context->GetPersistedWritePermissionGrantForTesting(
+ context->GetExtendedWritePermissionGrantForTesting(
kTestOrigin2, kTestPath4,
ChromeFileSystemAccessPermissionContext::HandleType::kDirectory);
@@ -3039,16 +3762,15 @@ TEST_F(PersistentPermissionsSiteSettingsHandlerTest,
// All grants are revoked for kTestOrigin1, and the grants for kTestOrigin2
// are unaffected.
EXPECT_EQ(updated_grants.size(), 1UL);
- EXPECT_EQ(updated_grants[0]
- .GetDict()
- .FindList(site_settings::kFileWriteGrants)
- ->size(),
- 1UL);
- EXPECT_EQ(updated_grants[0]
- .GetDict()
- .FindList(site_settings::kDirectoryWriteGrants)
- ->size(),
- 1UL);
+ EXPECT_EQ(
+ updated_grants[0].GetDict().FindList(site_settings::kEditGrants)->size(),
+ 2UL);
+
+ // Expect that the WebUIListenerCallback was triggered.
+ EXPECT_EQ(web_ui()->call_data()[0]->function_name(),
+ "cr.webUIListenerCallback");
+
+ EXPECT_EQ(updated_data.arg1()->GetString(), kCallbackId);
}
namespace {
@@ -4680,8 +5402,10 @@ TEST_F(SiteSettingsHandlerUsbTest, HandleSetOriginPermissionsPolicyOnly) {
TEST_F(SiteSettingsHandlerTest, HandleClearSiteGroupDataAndCookies) {
SetupModels();
- EXPECT_EQ(28u,
- handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
+ EXPECT_EQ(28u, handler()
+ ->GetCookiesTreeModelForTesting()
+ ->GetRoot()
+ ->GetTotalNodeCount());
auto verify_site_group = [](const base::Value& site_group,
std::string expected_etld_plus1) {
@@ -4721,20 +5445,25 @@ TEST_F(SiteSettingsHandlerTest, HandleClearSiteGroupDataAndCookies) {
}
}
- EXPECT_EQ(19u,
- handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
+ EXPECT_EQ(19u, handler()
+ ->GetCookiesTreeModelForTesting()
+ ->GetRoot()
+ ->GetTotalNodeCount());
storage_and_cookie_list = GetOnStorageFetchedSentList();
- EXPECT_EQ(3U, storage_and_cookie_list.size());
- verify_site_group(storage_and_cookie_list[0], "google.com");
+ EXPECT_EQ(4U, storage_and_cookie_list.size());
+ verify_site_group(storage_and_cookie_list[0], "example.com");
+ verify_site_group(storage_and_cookie_list[1], "google.com");
args.clear();
args.Append(GroupingKey::CreateFromEtldPlus1("google.com").Serialize());
handler()->HandleClearSiteGroupDataAndCookies(args);
- EXPECT_EQ(14u,
- handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
+ EXPECT_EQ(14u, handler()
+ ->GetCookiesTreeModelForTesting()
+ ->GetRoot()
+ ->GetTotalNodeCount());
storage_and_cookie_list = GetOnStorageFetchedSentList();
EXPECT_EQ(2U, storage_and_cookie_list.size());
@@ -4747,7 +5476,7 @@ TEST_F(SiteSettingsHandlerTest, HandleClearSiteGroupDataAndCookies) {
// No nodes representing storage partitioned on google.com.au should be
// present.
for (const auto& host_node :
- handler()->cookies_tree_model_->GetRoot()->children()) {
+ handler()->GetCookiesTreeModelForTesting()->GetRoot()->children()) {
for (const auto& storage_node : host_node->children()) {
if (storage_node->GetDetailedInfo().node_type !=
CookieTreeNode::DetailedInfo::TYPE_COOKIES) {
@@ -4779,18 +5508,22 @@ TEST_F(SiteSettingsHandlerTest, HandleClearSiteGroupDataAndCookies) {
TEST_P(SiteSettingsHandlerTest, HandleClearUnpartitionedUsage) {
SetupModels();
- EXPECT_EQ(28u,
- handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
- EXPECT_EQ(1, std::distance(handler()->browsing_data_model_->begin(),
- handler()->browsing_data_model_->end()));
+ EXPECT_EQ(28u, handler()
+ ->GetCookiesTreeModelForTesting()
+ ->GetRoot()
+ ->GetTotalNodeCount());
+ EXPECT_EQ(2,
+ std::distance(handler()->GetBrowsingDataModelForTesting()->begin(),
+ handler()->GetBrowsingDataModelForTesting()->end()));
base::Value::List args;
args.Append(GetParam() ? "https://www.example.com/"
: "http://www.example.com/");
handler()->HandleClearUnpartitionedUsage(args);
- EXPECT_EQ(1, std::distance(handler()->browsing_data_model_->begin(),
- handler()->browsing_data_model_->end()));
+ EXPECT_EQ(2,
+ std::distance(handler()->GetBrowsingDataModelForTesting()->begin(),
+ handler()->GetBrowsingDataModelForTesting()->end()));
// Confirm that only the unpartitioned items for example.com have been
// cleared.
@@ -4830,8 +5563,9 @@ TEST_P(SiteSettingsHandlerTest, HandleClearUnpartitionedUsage) {
args.Append("https://www.google.com/");
handler()->HandleClearUnpartitionedUsage(args);
- EXPECT_EQ(0, std::distance(handler()->browsing_data_model_->begin(),
- handler()->browsing_data_model_->end()));
+ EXPECT_EQ(1,
+ std::distance(handler()->GetBrowsingDataModelForTesting()->begin(),
+ handler()->GetBrowsingDataModelForTesting()->end()));
// Clearing Site Specific Media Licenses Tests
#if BUILDFLAG(IS_WIN)
@@ -4883,7 +5617,6 @@ TEST_F(SiteSettingsHandlerTest, ClearClientHints) {
HostContentSettingsMap* host_content_settings_map =
HostContentSettingsMapFactory::GetForProfile(profile());
- ContentSettingsForOneType client_hints_settings;
// Add setting for the two hosts host[0], host[1].
base::Value client_hint_platform_version(14);
@@ -4908,8 +5641,9 @@ TEST_F(SiteSettingsHandlerTest, ClearClientHints) {
base::Value::List args;
args.Append(GroupingKey::CreateFromEtldPlus1("example.com").Serialize());
handler()->HandleClearSiteGroupDataAndCookies(args);
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::CLIENT_HINTS, &client_hints_settings);
+ ContentSettingsForOneType client_hints_settings =
+ host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::CLIENT_HINTS);
EXPECT_EQ(2U, client_hints_settings.size());
EXPECT_EQ(ContentSettingsPattern::FromURLNoWildcard(hosts[2]),
@@ -4931,8 +5665,8 @@ TEST_F(SiteSettingsHandlerTest, ClearClientHints) {
handler()->HandleClearUnpartitionedUsage(args);
// Validate the client hint has been cleared.
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::CLIENT_HINTS, &client_hints_settings);
+ client_hints_settings = host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::CLIENT_HINTS);
EXPECT_EQ(1U, client_hints_settings.size());
// www.google.com should be the only remaining entry.
@@ -4950,8 +5684,8 @@ TEST_F(SiteSettingsHandlerTest, ClearClientHints) {
handler()->HandleClearUnpartitionedUsage(args);
// Validate the client hint has been cleared.
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::CLIENT_HINTS, &client_hints_settings);
+ client_hints_settings = host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::CLIENT_HINTS);
EXPECT_EQ(0U, client_hints_settings.size());
}
@@ -4966,7 +5700,6 @@ TEST_F(SiteSettingsHandlerTest, ClearReducedAcceptLanguage) {
HostContentSettingsMap* host_content_settings_map =
HostContentSettingsMapFactory::GetForProfile(profile());
- ContentSettingsForOneType accept_language_settings;
std::string language = "en-us";
base::Value::Dict accept_language_dictionary;
@@ -4983,8 +5716,9 @@ TEST_F(SiteSettingsHandlerTest, ClearReducedAcceptLanguage) {
base::Value::List args;
args.Append(GroupingKey::CreateFromEtldPlus1("example.com").Serialize());
handler()->HandleClearSiteGroupDataAndCookies(args);
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::REDUCED_ACCEPT_LANGUAGE, &accept_language_settings);
+ ContentSettingsForOneType accept_language_settings =
+ host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::REDUCED_ACCEPT_LANGUAGE);
EXPECT_EQ(2U, accept_language_settings.size());
EXPECT_EQ(ContentSettingsPattern::FromURLNoWildcard(hosts[2]),
@@ -5008,8 +5742,8 @@ TEST_F(SiteSettingsHandlerTest, ClearReducedAcceptLanguage) {
handler()->HandleClearUnpartitionedUsage(args);
// Validate the reduce accept language has been cleared.
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::REDUCED_ACCEPT_LANGUAGE, &accept_language_settings);
+ accept_language_settings = host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::REDUCED_ACCEPT_LANGUAGE);
EXPECT_EQ(1U, accept_language_settings.size());
// www.google.com should be the only remaining entry.
@@ -5028,8 +5762,8 @@ TEST_F(SiteSettingsHandlerTest, ClearReducedAcceptLanguage) {
handler()->HandleClearUnpartitionedUsage(args);
// Validate the reduced accept language has been cleared.
- host_content_settings_map->GetSettingsForOneType(
- ContentSettingsType::REDUCED_ACCEPT_LANGUAGE, &accept_language_settings);
+ accept_language_settings = host_content_settings_map->GetSettingsForOneType(
+ ContentSettingsType::REDUCED_ACCEPT_LANGUAGE);
EXPECT_EQ(0U, accept_language_settings.size());
}
@@ -5037,10 +5771,14 @@ TEST_F(SiteSettingsHandlerTest, HandleClearPartitionedUsage) {
// Confirm that removing unpartitioned storage correctly removes the
// appropriate nodes.
SetupModels();
- EXPECT_EQ(28u,
- handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
- EXPECT_EQ(1, std::distance(handler()->browsing_data_model_->begin(),
- handler()->browsing_data_model_->end()));
+
+ EXPECT_EQ(28u, handler()
+ ->GetCookiesTreeModelForTesting()
+ ->GetRoot()
+ ->GetTotalNodeCount());
+ EXPECT_EQ(2,
+ std::distance(handler()->GetBrowsingDataModelForTesting()->begin(),
+ handler()->GetBrowsingDataModelForTesting()->end()));
base::Value::List args;
args.Append("https://www.example.com/");
@@ -5076,8 +5814,9 @@ TEST_F(SiteSettingsHandlerTest, HandleClearPartitionedUsage) {
// Should not have affected the browsing data model.
// TODO(crbug.com/1271155): Update when partitioned storage is represented
// by the browsing data model.
- EXPECT_EQ(1, std::distance(handler()->browsing_data_model_->begin(),
- handler()->browsing_data_model_->end()));
+ EXPECT_EQ(2,
+ std::distance(handler()->GetBrowsingDataModelForTesting()->begin(),
+ handler()->GetBrowsingDataModelForTesting()->end()));
}
TEST_F(SiteSettingsHandlerTest, CookieSettingDescription) {
@@ -5219,10 +5958,13 @@ TEST_F(SiteSettingsHandlerTest, HandleGetUsageInfo) {
// Confirm that usage info only returns unpartitioned storage.
SetupModels();
- EXPECT_EQ(28u,
- handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
- EXPECT_EQ(1, std::distance(handler()->browsing_data_model_->begin(),
- handler()->browsing_data_model_->end()));
+ EXPECT_EQ(28u, handler()
+ ->GetCookiesTreeModelForTesting()
+ ->GetRoot()
+ ->GetTotalNodeCount());
+ EXPECT_EQ(2,
+ std::distance(handler()->GetBrowsingDataModelForTesting()->begin(),
+ handler()->GetBrowsingDataModelForTesting()->end()));
base::Value::List args;
args.Append("http://www.example.com");
@@ -5242,7 +5984,7 @@ TEST_F(SiteSettingsHandlerTest, HandleGetUsageInfo) {
args.Append("http://google.com");
handler()->HandleFetchUsageTotal(args);
handler()->ServicePendingRequests();
- ValidateUsageInfo("http://google.com", "", "2 cookies",
+ ValidateUsageInfo("http://google.com", "100 B", "2 cookies",
"2 sites in google.com's group", false);
args.clear();
@@ -5324,7 +6066,7 @@ TEST_F(SiteSettingsHandlerTest, IsolatedWebAppUsageInfo) {
std::string iwa_url =
"isolated-app://"
"aerugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic/";
- SetupModelsWithIsolatedWebAppData(iwa_url, 1000);
+ SetupModelsWithIsolatedWebAppData({{iwa_url, 1000}});
base::Value::List args;
args.Append(iwa_url);
@@ -5337,4 +6079,71 @@ TEST_F(SiteSettingsHandlerTest, IsolatedWebAppUsageInfo) {
/*expected_fps_member_count_string=*/"", /*expected_fps_policy=*/false);
}
+TEST_F(SiteSettingsHandlerTest, IsolatedWebAppClearSiteGroupDataAndCookies) {
+ GURL iwa_url1(
+ "isolated-app://"
+ "abcdefztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic/");
+ GURL iwa_url2(
+ "isolated-app://"
+ "aerugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic/");
+ SetupModelsWithIsolatedWebAppData(
+ {{iwa_url1.spec(), 1000}, {iwa_url2.spec(), 2000}});
+
+ auto verify_site_group = [](const base::Value& site_group,
+ const GURL& expected_origin,
+ int64_t expected_usage) {
+ ASSERT_TRUE(site_group.is_dict());
+ EXPECT_THAT(CHECK_DEREF(site_group.GetDict().FindString("groupingKey")),
+ IsOrigin(expected_origin));
+ ASSERT_EQ(1U, site_group.GetDict().FindList("origins")->size());
+ const base::Value::Dict& origin_info =
+ site_group.GetDict().FindList("origins")->front().GetDict();
+ EXPECT_EQ(expected_usage, origin_info.FindDouble("usage").value());
+ };
+
+ base::Value::List all_sites_list = GetOnStorageFetchedSentList();
+ EXPECT_EQ(2U, all_sites_list.size());
+ verify_site_group(all_sites_list[0], iwa_url1, 1000);
+ verify_site_group(all_sites_list[1], iwa_url2, 2000);
+
+ base::Value::List args;
+ args.Append(GroupingKey::Create(url::Origin::Create(iwa_url1)).Serialize());
+ handler()->HandleClearSiteGroupDataAndCookies(args);
+
+ all_sites_list = GetOnStorageFetchedSentList();
+ EXPECT_EQ(1U, all_sites_list.size());
+ verify_site_group(all_sites_list[0], iwa_url2, 2000);
+}
+
+TEST_F(SiteSettingsHandlerTest, IsolatedWebAppClearUnpartitionedUsage) {
+ GURL iwa_url(
+ "isolated-app://"
+ "abcdefztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic/");
+ SetupModelsWithIsolatedWebAppData({{iwa_url.spec(), 1000}});
+
+ base::Value::List usage_args;
+ usage_args.Append(iwa_url.spec());
+ handler()->HandleFetchUsageTotal(usage_args);
+ handler()->ServicePendingRequests();
+
+ ValidateUsageInfo(
+ /*expected_usage_origin=*/iwa_url.spec(),
+ /*expected_usage_string=*/"1,000 B",
+ /*expected_cookie_string=*/"",
+ /*expected_fps_member_count_string=*/"", /*expected_fps_policy=*/false);
+
+ base::Value::List clear_args;
+ clear_args.Append(iwa_url.spec());
+ handler()->HandleClearUnpartitionedUsage(clear_args);
+
+ handler()->HandleFetchUsageTotal(usage_args);
+ handler()->ServicePendingRequests();
+
+ ValidateUsageInfo(
+ /*expected_usage_origin=*/iwa_url.spec(),
+ /*expected_usage_string=*/"",
+ /*expected_cookie_string=*/"",
+ /*expected_fps_member_count_string=*/"", /*expected_fps_policy=*/false);
+}
+
} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chromium/chrome/browser/ui/webui/settings/site_settings_helper.cc
index 0bc74c27f99..9bc1f5089a7 100644
--- a/chromium/chrome/browser/ui/webui/settings/site_settings_helper.cc
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_helper.cc
@@ -21,13 +21,11 @@
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/bluetooth/bluetooth_chooser_context_factory.h"
-#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/file_system_access/chrome_file_system_access_permission_context.h"
#include "chrome/browser/file_system_access/file_system_access_permission_context_factory.h"
#include "chrome/browser/hid/hid_chooser_context.h"
#include "chrome/browser/hid/hid_chooser_context_factory.h"
-#include "chrome/browser/permissions/notification_permission_review_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/serial/serial_chooser_context.h"
#include "chrome/browser/serial/serial_chooser_context_factory.h"
@@ -39,7 +37,6 @@
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
-#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
@@ -50,15 +47,15 @@
#include "components/permissions/contexts/bluetooth_chooser_context.h"
#include "components/permissions/object_permission_context_base.h"
#include "components/permissions/permission_decision_auto_blocker.h"
-#include "components/permissions/permission_result.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
-#include "components/site_engagement/content/site_engagement_service.h"
+#include "components/strings/grit/components_strings.h"
#include "components/subresource_filter/content/browser/subresource_filter_content_settings_manager.h"
#include "components/subresource_filter/content/browser/subresource_filter_profile_context.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
+#include "components/url_formatter/elide_url.h"
#include "components/url_formatter/url_formatter.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_result.h"
@@ -80,6 +77,8 @@ constexpr char kAppId[] = "appId";
namespace {
+using PermissionStatus = blink::mojom::PermissionStatus;
+
// Chooser data group names.
const char kUsbChooserDataGroupType[] = "usb-devices-data";
const char kSerialChooserDataGroupType[] = "serial-ports-data";
@@ -135,6 +134,7 @@ const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
"private-network-devices-data"},
{ContentSettingsType::ANTI_ABUSE, "anti-abuse"},
{ContentSettingsType::STORAGE_ACCESS, "storage-access"},
+ {ContentSettingsType::AUTO_PICTURE_IN_PICTURE, "auto-picture-in-picture"},
// Add new content settings here if a corresponding Javascript string
// representation for it is not required, for example if the content setting
@@ -166,7 +166,7 @@ const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
{ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, nullptr},
{ContentSettingsType::FILE_SYSTEM_READ_GUARD, nullptr},
{ContentSettingsType::CAMERA_PAN_TILT_ZOOM, nullptr},
- {ContentSettingsType::INSECURE_LOCAL_NETWORK, nullptr},
+ {ContentSettingsType::INSECURE_PRIVATE_NETWORK, nullptr},
{ContentSettingsType::PERMISSION_AUTOREVOCATION_DATA, nullptr},
{ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY, nullptr},
{ContentSettingsType::DISPLAY_CAPTURE, nullptr},
@@ -195,6 +195,9 @@ const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
nullptr},
{ContentSettingsType::THIRD_PARTY_STORAGE_PARTITIONING, nullptr},
{ContentSettingsType::ALL_SCREEN_CAPTURE, nullptr},
+ {ContentSettingsType::COOKIE_CONTROLS_METADATA, nullptr},
+ {ContentSettingsType::TPCD_SUPPORT, nullptr},
+ {ContentSettingsType::TPCD_METADATA_GRANTS, nullptr},
};
static_assert(std::size(kContentSettingsTypeGroupNames) ==
@@ -243,15 +246,17 @@ SiteSettingSource CalculateSiteSettingSource(
const ContentSettingsType content_type,
const GURL& origin,
const content_settings::SettingInfo& info,
- const permissions::PermissionResult result) {
+ const content::PermissionResult result) {
if (info.source == content_settings::SETTING_SOURCE_ALLOWLIST)
return SiteSettingSource::kAllowlist; // Source #1.
- if (result.source == permissions::PermissionStatusSource::KILL_SWITCH)
+ if (result.source == content::PermissionStatusSource::KILL_SWITCH) {
return SiteSettingSource::kKillSwitch; // Source #2.
+ }
- if (result.source == permissions::PermissionStatusSource::INSECURE_ORIGIN)
+ if (result.source == content::PermissionStatusSource::INSECURE_ORIGIN) {
return SiteSettingSource::kInsecureOrigin; // Source #3.
+ }
if (info.source == content_settings::SETTING_SOURCE_POLICY ||
info.source == content_settings::SETTING_SOURCE_SUPERVISED) {
@@ -276,10 +281,8 @@ SiteSettingSource CalculateSiteSettingSource(
DCHECK_NE(content_settings::SETTING_SOURCE_NONE, info.source);
if (info.source == content_settings::SETTING_SOURCE_USER) {
- if (result.source ==
- permissions::PermissionStatusSource::MULTIPLE_DISMISSALS ||
- result.source ==
- permissions::PermissionStatusSource::MULTIPLE_IGNORES) {
+ if (result.source == content::PermissionStatusSource::MULTIPLE_DISMISSALS ||
+ result.source == content::PermissionStatusSource::MULTIPLE_IGNORES) {
return SiteSettingSource::kEmbargo; // Source #8.
}
if (info.primary_pattern == ContentSettingsPattern::Wildcard() &&
@@ -314,8 +317,8 @@ bool PatternAppliesToWebUISchemes(const ContentSettingPatternSource& pattern) {
std::string GetDisplayNameForPattern(Profile* profile,
const ContentSettingsPattern& pattern) {
GURL url(pattern.ToString());
- if (url.SchemeIs(extensions::kExtensionScheme) ||
- url.SchemeIs(chrome::kIsolatedAppScheme)) {
+ if (url.is_valid() && (url.SchemeIs(extensions::kExtensionScheme) ||
+ url.SchemeIs(chrome::kIsolatedAppScheme))) {
return GetDisplayNameForGURL(profile, url, /*hostname_only=*/false);
}
return pattern.ToString();
@@ -363,7 +366,9 @@ void GetPolicyAllowedUrls(ContentSettingsType type,
exceptions->push_back(GetExceptionForPage(
type, profile, pattern, ContentSettingsPattern(), display_name,
CONTENT_SETTING_ALLOW,
- SiteSettingSourceToString(SiteSettingSource::kPolicy), incognito));
+ SiteSettingSourceToString(SiteSettingSource::kPolicy),
+ // Pass base::Time() to indicate the exceptions do not expire.
+ base::Time(), incognito));
}
}
@@ -380,9 +385,8 @@ std::string GetSourceStringForChooserException(
// Chooser exceptions do not use a PermissionContextBase for their
// permissions.
- permissions::PermissionResult permission_result(
- CONTENT_SETTING_DEFAULT,
- permissions::PermissionStatusSource::UNSPECIFIED);
+ content::PermissionResult permission_result(
+ PermissionStatus::ASK, content::PermissionStatusSource::UNSPECIFIED);
// The |origin| parameter is only used for |ContentSettingsType::ADS| with
// the |kSafeBrowsingSubresourceFilter| feature flag enabled, so an empty GURL
@@ -444,34 +448,6 @@ constexpr UrlIdentity::TypeSet kUrlIdentityAllowedTypes = {
UrlIdentity::Type::kDefault, UrlIdentity::Type::kFile,
UrlIdentity::Type::kIsolatedWebApp, UrlIdentity::Type::kChromeExtension};
-bool ShouldAddToNotificationPermissionReviewList(
- site_engagement::SiteEngagementService* service,
- GURL url,
- int notification_count) {
- // The notification permission should be added to the list if one of the
- // criteria below holds:
- // - Site engagement level is NONE OR MINIMAL and average daily notification
- // count is more than 0.
- // - Site engamment level is LOW and average daily notification count is
- // more than 3. Otherwise, the notification permission should not be added
- // to review list.
- double score = service->GetScore(url);
- int low_engagement_notification_limit =
- features::kSafetyCheckNotificationPermissionsLowEnagementLimit.Get();
- bool is_low_engagement =
- !site_engagement::SiteEngagementService::IsEngagementAtLeast(
- score, blink::mojom::EngagementLevel::MEDIUM) &&
- notification_count > low_engagement_notification_limit;
- int min_engagement_notification_limit =
- features::kSafetyCheckNotificationPermissionsMinEnagementLimit.Get();
- bool is_minimal_engagement =
- !site_engagement::SiteEngagementService::IsEngagementAtLeast(
- score, blink::mojom::EngagementLevel::LOW) &&
- notification_count > min_engagement_notification_limit;
-
- return is_minimal_engagement || is_low_engagement;
-}
-
} // namespace
bool HasRegisteredGroupName(ContentSettingsType type) {
@@ -582,6 +558,11 @@ const std::vector<ContentSettingsType>& GetVisiblePermissionCategories() {
base_types->push_back(ContentSettingsType::PRIVATE_NETWORK_GUARD);
}
+ if (base::FeatureList::IsEnabled(
+ blink::features::kMediaSessionEnterPictureInPicture)) {
+ base_types->push_back(ContentSettingsType::AUTO_PICTURE_IN_PICTURE);
+ }
+
initialized = true;
}
@@ -642,6 +623,22 @@ base::Value::Dict GetFileSystemExceptionForPage(
return exception;
}
+std::u16string GetExpirationDescription(const base::Time& expiration) {
+ CHECK(!expiration.is_null());
+
+ const base::TimeDelta time_diff =
+ expiration.LocalMidnight() - base::Time::Now().LocalMidnight();
+
+ // Only exceptions that haven't expired should reach this function.
+ // However, there is an edge case where an exception could expire between
+ // being fetched and this calculation. So let's always return a valid
+ // number, zero.
+ int days = std::max(time_diff.InDays(), 0);
+
+ return l10n_util::GetPluralStringFUTF16(IDS_SETTINGS_EXPIRES_AFTER_TIME_LABEL,
+ days);
+}
+
// Create a base::Value::Dict that will act as a data source for a single row
// in a HostContentSettingsMap-controlled exceptions table (e.g., cookies).
base::Value::Dict GetExceptionForPage(
@@ -652,6 +649,7 @@ base::Value::Dict GetExceptionForPage(
const std::string& display_name,
const ContentSetting& setting,
const std::string& provider_name,
+ const base::Time& expiration,
bool incognito,
bool is_embargoed) {
base::Value::Dict exception;
@@ -667,12 +665,126 @@ base::Value::Dict GetExceptionForPage(
DCHECK(!setting_string.empty());
exception.Set(kSetting, setting_string);
+ // Cookie exception types may have an expiration that should be shown.
+ if (content_type == ContentSettingsType::COOKIES && !expiration.is_null() &&
+ !incognito) {
+ exception.Set(kDescription, GetExpirationDescription(expiration));
+ }
+
exception.Set(kSource, provider_name);
exception.Set(kIncognito, incognito);
exception.Set(kIsEmbargoed, is_embargoed);
return exception;
}
+std::u16string GetStorageAccessEmbeddingDescription(
+ StorageAccessEmbeddingException embedding_sa_exception) {
+ if (embedding_sa_exception.is_embargoed) {
+ return l10n_util::GetStringUTF16(
+ IDS_PAGE_INFO_PERMISSION_AUTOMATICALLY_BLOCKED);
+ }
+
+ if (embedding_sa_exception.expiration.is_null()) {
+ return std::u16string();
+ }
+
+ return GetExpirationDescription(embedding_sa_exception.expiration);
+}
+
+// If the given `pattern` represents an individual origin, Isolated Web App, or
+// extension, retrieve a string to display it as such. If not, return the
+// pattern without wildcards as a string.
+std::string GetStorageAccessDisplayNameForPattern(
+ Profile* profile,
+ ContentSettingsPattern pattern) {
+ GURL url(pattern.ToString());
+ if (url.is_valid() && (url.SchemeIs(extensions::kExtensionScheme) ||
+ url.SchemeIs(chrome::kIsolatedAppScheme))) {
+ return GetDisplayNameForGURL(profile, url, /*hostname_only=*/false);
+ }
+
+ GURL url2 = pattern.ToRepresentativeUrl();
+ if (url2.is_valid()) {
+ return base::UTF16ToUTF8(FormatUrlForSecurityDisplay(
+ url2, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
+ }
+
+ return pattern.ToString();
+}
+
+base::Value::Dict GetStorageAccessExceptionForPage(
+ Profile* profile,
+ const ContentSettingsPattern& pattern,
+ const std::string& display_name,
+ ContentSetting setting,
+ const std::vector<StorageAccessEmbeddingException>& exceptions) {
+ CHECK(!exceptions.empty());
+
+ base::Value::Dict exception;
+ exception.Set(kOrigin, pattern.ToString());
+ exception.Set(kDisplayName, display_name);
+ std::string setting_string =
+ content_settings::ContentSettingToString(setting);
+ DCHECK(!setting_string.empty());
+ exception.Set(kSetting, setting_string);
+
+ // If there is only one exception and that exception applies everywhere,
+ // i.e. `secondary_pattern` is empty, then don't return exceptions and a
+ // static row should be displayed. In practice, this only applies to embargoed
+ // sites.
+ if (exceptions.size() == 1 &&
+ exceptions[0].secondary_pattern == ContentSettingsPattern::Wildcard()) {
+ auto& embedding_sa_exception = exceptions[0];
+
+ std::u16string description =
+ GetStorageAccessEmbeddingDescription(embedding_sa_exception);
+ if (!description.empty()) {
+ exception.Set(kDescription, description);
+ }
+
+ exception.Set(kIncognito, embedding_sa_exception.is_incognito);
+ exception.Set(kExceptions, base::Value::List());
+ return exception;
+ }
+
+ exception.Set(kCloseDescription,
+ l10n_util::GetPluralStringFUTF16(IDS_DEL_SITE_SETTINGS_COUNTER,
+ exceptions.size()));
+ const int open_description_id =
+ (setting == ContentSetting::CONTENT_SETTING_ALLOW)
+ ? IDS_SETTINGS_STORAGE_ACCESS_ALLOWED_SITE_LABEL
+ : IDS_SETTINGS_STORAGE_ACCESS_BLOCKED_SITE_LABEL;
+ exception.Set(kOpenDescription,
+ l10n_util::GetStringUTF16(open_description_id));
+
+ base::Value::List embedding_origins;
+ for (auto& embedding_sa_exception : exceptions) {
+ ContentSettingsPattern secondary_pattern =
+ embedding_sa_exception.secondary_pattern;
+ base::Value::Dict embedding_exception;
+ embedding_exception.Set(
+ kEmbeddingOrigin,
+ secondary_pattern == ContentSettingsPattern::Wildcard()
+ ? std::string()
+ : secondary_pattern.ToString());
+ embedding_exception.Set(
+ kEmbeddingDisplayName,
+ GetStorageAccessDisplayNameForPattern(profile, secondary_pattern));
+
+ std::u16string description =
+ GetStorageAccessEmbeddingDescription(embedding_sa_exception);
+ if (!description.empty()) {
+ embedding_exception.Set(kDescription, description);
+ }
+ embedding_exception.Set(kIncognito, embedding_sa_exception.is_incognito);
+ embedding_origins.Append(std::move(embedding_exception));
+ }
+
+ exception.Set(kExceptions, std::move(embedding_origins));
+
+ return exception;
+}
+
UrlIdentity GetUrlIdentityForGURL(Profile* profile,
const GURL& url,
bool hostname_only) {
@@ -695,20 +807,17 @@ std::string GetDisplayNameForGURL(Profile* profile,
GetUrlIdentityForGURL(profile, url, hostname_only).name);
}
-void GetExceptionsForContentType(ContentSettingsType type,
- Profile* profile,
- content::WebUI* web_ui,
- bool incognito,
- base::Value::List* exceptions) {
- ContentSettingsForOneType all_settings;
+// Fills in `all_patterns_settings` with site exceptions information for the
+// given `type` from `profile`.
+void GetRawExceptionsForContentSettingsType(
+ ContentSettingsType type,
+ Profile* profile,
+ content::WebUI* web_ui,
+ AllPatternsSettings& all_patterns_settings) {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile);
- map->GetSettingsForOneType(type, &all_settings);
-
- // Group settings by primary_pattern.
- AllPatternsSettings all_patterns_settings;
- for (const auto& setting : all_settings) {
+ for (const auto& setting : map->GetSettingsForOneType(type)) {
// Don't add default settings.
if (setting.primary_pattern == ContentSettingsPattern::Wildcard() &&
setting.secondary_pattern == ContentSettingsPattern::Wildcard() &&
@@ -728,6 +837,13 @@ void GetExceptionsForContentType(ContentSettingsType type,
continue;
}
+ // Don't add auto granted permissions for storage access exceptions.
+ if (type == ContentSettingsType::STORAGE_ACCESS &&
+ setting.metadata.session_model() ==
+ content_settings::SessionModel::NonRestorableUserSession) {
+ continue;
+ }
+
auto content_setting = setting.GetContentSetting();
if (type == ContentSettingsType::COOKIES &&
@@ -745,22 +861,17 @@ void GetExceptionsForContentType(ContentSettingsType type,
}
}
- all_patterns_settings[std::make_pair(
- setting.primary_pattern, setting.source)][setting.secondary_pattern] =
- content_setting;
+ all_patterns_settings[{setting.primary_pattern, setting.source}][{
+ setting.secondary_pattern, setting.incognito}] = {
+ content_setting, /*is_embargoed=*/false, setting.metadata.expiration()};
}
- ContentSettingsForOneType embargo_settings;
- map->GetSettingsForOneType(ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
- &embargo_settings);
-
permissions::PermissionDecisionAutoBlocker* auto_blocker =
permissions::PermissionsClient::Get()->GetPermissionDecisionAutoBlocker(
profile);
- std::set<ContentSettingsPattern> origins_under_embargo;
-
- for (const auto& setting : embargo_settings) {
+ for (const auto& setting : map->GetSettingsForOneType(
+ ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA)) {
// Off-the-record HostContentSettingsMap contains incognito content
// settings as well as normal content settings. Here, we use the
// incognito settings only.
@@ -774,24 +885,33 @@ void GetExceptionsForContentType(ContentSettingsType type,
if (auto_blocker->IsEmbargoed(GURL(setting.primary_pattern.ToString()),
type)) {
- origins_under_embargo.insert(setting.primary_pattern);
- all_patterns_settings[std::make_pair(
- setting.primary_pattern, setting.source)][setting.secondary_pattern] =
- CONTENT_SETTING_BLOCK;
+ all_patterns_settings[{setting.primary_pattern, setting.source}]
+ [{setting.secondary_pattern, setting.incognito}] = {
+ CONTENT_SETTING_BLOCK, /*is_embargoed=*/true,
+ setting.metadata.expiration()};
+ ;
}
}
+}
+
+void GetExceptionsForContentType(ContentSettingsType type,
+ Profile* profile,
+ content::WebUI* web_ui,
+ bool incognito,
+ base::Value::List* exceptions) {
+ // Group settings by primary_pattern.
+ AllPatternsSettings all_patterns_settings;
+
+ GetRawExceptionsForContentSettingsType(type, profile, web_ui,
+ all_patterns_settings);
// Keep the exceptions sorted by provider so they will be displayed in
// precedence order.
std::vector<base::Value::Dict>
all_provider_exceptions[HostContentSettingsMap::NUM_PROVIDER_TYPES];
- // |all_patterns_settings| is sorted from the lowest precedence pattern to
- // the highest (see operator< in ContentSettingsPattern), so traverse it in
- // reverse to show the patterns with the highest precedence (the more specific
- // ones) on the top.
for (const auto& [primary_pattern_and_source, one_settings] :
- base::Reversed(all_patterns_settings)) {
+ all_patterns_settings) {
const auto& [primary_pattern, source] = primary_pattern_and_source;
const std::string display_name =
GetDisplayNameForPattern(profile, primary_pattern);
@@ -799,12 +919,14 @@ void GetExceptionsForContentType(ContentSettingsType type,
auto& this_provider_exceptions = all_provider_exceptions
[HostContentSettingsMap::GetProviderTypeFromSource(source)];
- for (auto j = one_settings.begin(); j != one_settings.end(); ++j) {
- ContentSetting content_setting = j->second;
+ for (const auto& secondary_setting : one_settings) {
+ const SiteExceptionInfo& site_exception_info = secondary_setting.second;
+ const auto& [secondary_pattern, is_incognito] = secondary_setting.first;
this_provider_exceptions.push_back(GetExceptionForPage(
- type, profile, primary_pattern, j->first, display_name,
- content_setting, source, incognito,
- base::Contains(origins_under_embargo, primary_pattern)));
+ type, profile, primary_pattern, secondary_pattern,
+ std::move(display_name), site_exception_info.content_setting, source,
+ site_exception_info.expiration, is_incognito,
+ site_exception_info.is_embargoed));
}
}
@@ -837,6 +959,57 @@ void GetExceptionsForContentType(ContentSettingsType type,
}
}
+void GetStorageAccessExceptions(ContentSetting content_setting,
+ Profile* profile,
+ Profile* incognito_profile,
+ content::WebUI* web_ui,
+ base::Value::List* exceptions) {
+ ContentSettingsType type = ContentSettingsType::STORAGE_ACCESS;
+
+ // Group settings by primary_pattern.
+ AllPatternsSettings all_patterns_settings;
+
+ GetRawExceptionsForContentSettingsType(type, profile, web_ui,
+ all_patterns_settings);
+
+ if (incognito_profile) {
+ GetRawExceptionsForContentSettingsType(type, incognito_profile, web_ui,
+ all_patterns_settings);
+ }
+
+ for (const auto& [primary_pattern_and_source, one_settings] :
+ all_patterns_settings) {
+ const auto& [primary_pattern, source] = primary_pattern_and_source;
+
+ std::vector<StorageAccessEmbeddingException> sa_exceptions;
+
+ for (const auto& secondary_setting : one_settings) {
+ const SiteExceptionInfo& site_exception_info = secondary_setting.second;
+ const auto& [secondary_pattern, is_incognito] = secondary_setting.first;
+
+ if (site_exception_info.content_setting != content_setting) {
+ continue;
+ }
+
+ sa_exceptions.push_back({secondary_pattern, is_incognito,
+ site_exception_info.is_embargoed,
+ site_exception_info.expiration});
+ }
+
+ if (sa_exceptions.empty()) {
+ continue;
+ }
+
+ // TODO(http://b/289788055): Remove wildcards.
+ const std::string display_name =
+ GetStorageAccessDisplayNameForPattern(profile, primary_pattern);
+
+ exceptions->Append(GetStorageAccessExceptionForPage(
+ profile, primary_pattern, std::move(display_name), content_setting,
+ sa_exceptions));
+ }
+}
+
void GetContentCategorySetting(const HostContentSettingsMap* map,
ContentSettingsType content_type,
base::Value::Dict* object) {
@@ -863,27 +1036,25 @@ ContentSetting GetContentSettingForOrigin(Profile* profile,
map->GetContentSetting(origin, origin, content_type, &info);
// Retrieve the content setting.
- permissions::PermissionResult result(
- setting, permissions::PermissionStatusSource::UNSPECIFIED);
+ content::PermissionResult result(
+ permissions::PermissionUtil::ContentSettingToPermissionStatus(setting),
+ content::PermissionStatusSource::UNSPECIFIED);
if (permissions::PermissionDecisionAutoBlocker::IsEnabledForContentSetting(
content_type)) {
if (permissions::PermissionUtil::IsPermission(content_type)) {
- content::PermissionResult permission_result =
- profile->GetPermissionController()
- ->GetPermissionResultForOriginWithoutContext(
- permissions::PermissionUtil::
- ContentSettingTypeToPermissionType(content_type),
- url::Origin::Create(origin));
- result =
- permissions::PermissionUtil::ToPermissionResult(permission_result);
+ result = profile->GetPermissionController()
+ ->GetPermissionResultForOriginWithoutContext(
+ permissions::PermissionUtil::
+ ContentSettingTypeToPermissionType(content_type),
+ url::Origin::Create(origin));
} else {
permissions::PermissionDecisionAutoBlocker* auto_blocker =
permissions::PermissionsClient::Get()
->GetPermissionDecisionAutoBlocker(profile);
- absl::optional<permissions::PermissionResult> embargo_result =
+ absl::optional<content::PermissionResult> embargo_result =
auto_blocker->GetEmbargoResult(origin, content_type);
if (embargo_result)
- result = *embargo_result;
+ result = embargo_result.value();
}
}
@@ -895,17 +1066,17 @@ ContentSetting GetContentSettingForOrigin(Profile* profile,
content_settings::SessionModel::OneTime) {
DCHECK(
permissions::PermissionUtil::CanPermissionBeAllowedOnce(content_type));
- DCHECK_EQ(result.content_setting, CONTENT_SETTING_ALLOW);
+ DCHECK_EQ(result.status, PermissionStatus::ASK);
return CONTENT_SETTING_DEFAULT;
}
- return result.content_setting;
+ return permissions::PermissionUtil::PermissionStatusToContentSetting(
+ result.status);
}
std::vector<ContentSettingPatternSource>
GetSingleOriginExceptionsForContentType(HostContentSettingsMap* map,
ContentSettingsType content_type) {
- ContentSettingsForOneType entries;
- map->GetSettingsForOneType(content_type, &entries);
+ ContentSettingsForOneType entries = map->GetSettingsForOneType(content_type);
// Exclude any entries that don't represent a single webby top-frame origin.
base::EraseIf(entries, [](const ContentSettingPatternSource& e) {
return !content_settings::PatternAppliesToSingleOrigin(
@@ -1096,61 +1267,4 @@ std::vector<web_app::IsolatedWebAppUrlInfo> GetInstalledIsolatedWebApps(
return iwas;
}
-// TODO(crbug.com/1444024): Migrate to NotificationPermissionsReviewService.
-base::Value::List PopulateNotificationPermissionReviewData(Profile* profile) {
- base::Value::List result;
- if (!base::FeatureList::IsEnabled(
- features::kSafetyCheckNotificationPermissions)) {
- return result;
- }
-
- auto* service =
- NotificationPermissionsReviewServiceFactory::GetForProfile(profile);
- if (!service) {
- return result;
- }
-
- auto notification_permissions = service->GetNotificationSiteListForReview();
-
- site_engagement::SiteEngagementService* engagement_service =
- site_engagement::SiteEngagementService::Get(profile);
-
- // Sort notification permissions by their priority for surfacing to the user.
- auto notification_permission_ordering =
- [](const permissions::NotificationPermissions& left,
- const permissions::NotificationPermissions& right) {
- return left.notification_count > right.notification_count;
- };
- std::sort(notification_permissions.begin(), notification_permissions.end(),
- notification_permission_ordering);
-
- for (const auto& notification_permission : notification_permissions) {
- // Converting primary pattern to GURL should always be valid, since
- // Notification Permission Review list only contains single origins. Those
- // are filtered in
- // NotificationPermissionsReviewService::GetNotificationSiteListForReview.
- GURL url = GURL(notification_permission.primary_pattern.ToString());
- DCHECK(url.is_valid());
- if (!ShouldAddToNotificationPermissionReviewList(
- engagement_service, url,
- notification_permission.notification_count)) {
- continue;
- }
-
- base::Value::Dict permission;
- permission.Set(site_settings::kOrigin,
- notification_permission.primary_pattern.ToString());
-
- std::string notification_info_string =
- base::UTF16ToUTF8(l10n_util::GetPluralStringFUTF16(
- IDS_SETTINGS_SAFETY_CHECK_REVIEW_NOTIFICATION_PERMISSIONS_COUNT_LABEL,
- notification_permission.notification_count));
- permission.Set(site_settings::kNotificationInfoString,
- notification_info_string);
- result.Append(std::move(permission));
- }
-
- return result;
-}
-
} // namespace site_settings
diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_helper.h b/chromium/chrome/browser/ui/webui/settings/site_settings_helper.h
index d05b9dde71d..591e039fa02 100644
--- a/chromium/chrome/browser/ui/webui/settings/site_settings_helper.h
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_helper.h
@@ -38,34 +38,59 @@ class IsolatedWebAppUrlInfo;
namespace site_settings {
-// Maps from a secondary pattern to a setting.
-typedef std::map<ContentSettingsPattern, ContentSetting> OnePatternSettings;
-// Maps from a primary pattern/source pair to a OnePatternSettings. All the
+struct SiteExceptionInfo {
+ ContentSetting content_setting;
+ bool is_embargoed;
+ base::Time expiration;
+};
+
+struct StorageAccessEmbeddingException {
+ ContentSettingsPattern secondary_pattern;
+ bool is_incognito;
+ bool is_embargoed;
+ base::Time expiration;
+};
+
+// Maps from a pair(secondary pattern, incognito) to a setting and if it's
+// embargoed.
+typedef std::map<std::pair<ContentSettingsPattern, bool>, SiteExceptionInfo>
+ OnePatternSettings;
+
+// Maps from a pair (primary pattern, source) to a OnePatternSettings. All the
// mappings in OnePatternSettings share the given primary pattern and source.
+//
+// The operator< in ContentSettingsPattern, determines that by default the
+// preferences are saved in lowest precedence pattern to the highest. However,
+// we want to show the patterns with the highest precedence (the more specific
+// ones) on the top, hence `std::greater<>`.
typedef std::map<std::pair<ContentSettingsPattern, std::string>,
- OnePatternSettings>
+ OnePatternSettings,
+ std::greater<>>
AllPatternsSettings;
// A set of <origin, source, incognito> tuple for organizing granted permission
// objects that belong to the same device.
using ChooserExceptionDetails = std::set<std::tuple<GURL, std::string, bool>>;
+// TODO(crbug.com/1373962): Prefix the types related to the File System Access
+// API so that their relation to the file system is more apparent.
constexpr char kChooserType[] = "chooserType";
-constexpr char kDirectoryReadGrants[] = "directoryReadGrants";
-constexpr char kDirectoryWriteGrants[] = "directoryWriteGrants";
+constexpr char kCloseDescription[] = "closeDescription";
constexpr char kDisabled[] = "disabled";
constexpr char kDisplayName[] = "displayName";
+constexpr char kDescription[] = "description";
+constexpr char kEditGrants[] = "editGrants";
constexpr char kEmbeddingOrigin[] = "embeddingOrigin";
+constexpr char kEmbeddingDisplayName[] = "embeddingDisplayName";
+constexpr char kExceptions[] = "exceptions";
constexpr char kFilePath[] = "filePath";
-constexpr char kFileReadGrants[] = "fileReadGrants";
-constexpr char kFileWriteGrants[] = "fileWriteGrants";
constexpr char kHostOrSpec[] = "hostOrSpec";
constexpr char kIncognito[] = "incognito";
constexpr char kIsDirectory[] = "isDirectory";
constexpr char kIsEmbargoed[] = "isEmbargoed";
constexpr char kIsWritable[] = "isWritable";
-constexpr char kNotificationInfoString[] = "notificationInfoString";
constexpr char kObject[] = "object";
+constexpr char kOpenDescription[] = "openDescription";
constexpr char kOrigin[] = "origin";
constexpr char kOriginForFavicon[] = "originForFavicon";
constexpr char kPermissions[] = "permissions";
@@ -77,6 +102,7 @@ constexpr char kSource[] = "source";
constexpr char kType[] = "type";
constexpr char kNotificationPermissionsReviewListMaybeChangedEvent[] =
"notification-permission-review-list-maybe-changed";
+constexpr char kViewGrants[] = "viewGrants";
enum class SiteSettingSource {
kAllowlist,
@@ -118,6 +144,27 @@ base::Value::Dict GetFileSystemExceptionForPage(
bool incognito,
bool is_embargoed = false);
+// Calculates the number of days between now and `expiration_timestamp`,
+// timestamp of when a setting is going to expire, and returns the appropriate
+// string for display in site settings. Only looks at the date between now and
+// `expiration_timestamp` i.e. doesn't take into account time.
+
+// E.g. current time 03/07 18:00. If expiration is in:
+// 03/07 01:00 then, time diff is 17h, and returns 0.
+// 04/07 19:00 then, time diff is 23h, but returns 1.
+// 05/07 19:00 then, time diff is 47h, and returns 2.
+// 05/07 17:00 then, time diff is 49h, and returns 2.
+std::u16string GetExpirationDescription(const base::Time& expiration_timestamp);
+
+// Helper function to construct a dictionary for a storage access exceptions
+// grouped by origin.
+base::Value::Dict GetStorageAccessExceptionForPage(
+ Profile* profile,
+ const ContentSettingsPattern& pattern,
+ const std::string& display_name,
+ ContentSetting setting,
+ const std::vector<StorageAccessEmbeddingException>& exceptions);
+
// Helper function to construct a dictionary for an exception.
base::Value::Dict GetExceptionForPage(
ContentSettingsType content_type,
@@ -127,6 +174,7 @@ base::Value::Dict GetExceptionForPage(
const std::string& display_name,
const ContentSetting& setting,
const std::string& provider_name,
+ const base::Time& expiration,
bool incognito,
bool is_embargoed = false);
@@ -142,6 +190,15 @@ void GetExceptionsForContentType(ContentSettingsType type,
bool incognito,
base::Value::List* exceptions);
+// Fills in |exceptions| with Values for the Storage Access exception for the
+// given content setting (such as enabled or blocked) from a |profile| and its
+// |incognito_profile|, if applicable.
+void GetStorageAccessExceptions(ContentSetting content_setting,
+ Profile* profile,
+ Profile* incognito_profile,
+ content::WebUI* web_ui,
+ base::Value::List* exceptions);
+
// Fills in object saying what the current settings is for the category (such as
// enabled or blocked) and the source of that setting (such preference, policy,
// or extension).
@@ -220,11 +277,6 @@ std::string GetDisplayNameForGURL(Profile* profile,
std::vector<web_app::IsolatedWebAppUrlInfo> GetInstalledIsolatedWebApps(
Profile* profile);
-// Returns a list of domains to be shown on the 'Review Notification
-// Permissions' module in site settings notification page. Those domains send
-// a lot of notifications, but have low site engagement.
-base::Value::List PopulateNotificationPermissionReviewData(Profile* profile);
-
} // namespace site_settings
#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SITE_SETTINGS_HELPER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_helper_unittest.cc b/chromium/chrome/browser/ui/webui/settings/site_settings_helper_unittest.cc
index 1723406b7b3..6bfd2f8099c 100644
--- a/chromium/chrome/browser/ui/webui/settings/site_settings_helper_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_helper_unittest.cc
@@ -25,6 +25,7 @@
#include "chrome/browser/web_applications/test/web_app_test_utils.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -44,6 +45,7 @@
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/device/public/cpp/test/fake_usb_device_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -58,6 +60,9 @@
namespace site_settings {
namespace {
+
+using PermissionStatus = blink::mojom::PermissionStatus;
+
constexpr ContentSettingsType kContentType = ContentSettingsType::GEOLOCATION;
constexpr ContentSettingsType kContentTypeCookies =
ContentSettingsType::COOKIES;
@@ -65,7 +70,7 @@ constexpr ContentSettingsType kContentTypeFileSystem =
ContentSettingsType::FILE_SYSTEM_WRITE_GUARD;
constexpr ContentSettingsType kContentTypeNotifications =
ContentSettingsType::NOTIFICATIONS;
-}
+} // namespace
class SiteSettingsHelperTest : public testing::Test {
public:
@@ -97,17 +102,9 @@ class SiteSettingsHelperTest : public testing::Test {
ContentSettingsPattern::Wildcard(), kContentType, setting);
}
- void RecordNotification(permissions::NotificationsEngagementService* service,
- GURL url,
- int daily_average_count) {
- // This many notifications were recorded during the past week in total.
- int total_count = daily_average_count * 7;
- service->RecordNotificationDisplayed(url, total_count);
- }
-
- base::Time GetReferenceTime() {
+ static base::Time GetReferenceTime() {
base::Time time;
- EXPECT_TRUE(base::Time::FromString("Sat, 1 Sep 2018 11:00:00 GMT", &time));
+ EXPECT_TRUE(base::Time::FromString("Sat, 1 Sep 2018 11:00:00", &time));
return time;
}
@@ -171,11 +168,11 @@ TEST_F(SiteSettingsHelperTest, ExceptionListShowsIncognitoEmbargoed) {
}
// Check that origin is under embargo.
- ASSERT_EQ(CONTENT_SETTING_BLOCK,
+ ASSERT_EQ(PermissionStatus::DENIED,
auto_blocker
->GetEmbargoResult(GURL(kOriginToEmbargo),
kContentTypeNotifications)
- ->content_setting);
+ ->status);
}
// Check there is 1 embargoed origin for a non-incognito profile.
@@ -229,11 +226,11 @@ TEST_F(SiteSettingsHelperTest, ExceptionListShowsIncognitoEmbargoed) {
incognito_auto_blocker->RecordDismissAndEmbargo(
GURL(kOriginToEmbargoIncognito), kContentTypeNotifications, false);
}
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
+ EXPECT_EQ(PermissionStatus::DENIED,
incognito_auto_blocker
->GetEmbargoResult(GURL(kOriginToEmbargoIncognito),
kContentTypeNotifications)
- ->content_setting);
+ ->status);
}
// Check there are 2 blocked or embargoed origins for an incognito profile.
@@ -286,10 +283,10 @@ TEST_F(SiteSettingsHelperTest, ExceptionListShowsEmbargoed) {
}
// Check that origin is under embargo.
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
+ EXPECT_EQ(PermissionStatus::DENIED,
auto_blocker
->GetEmbargoResult(origin_to_embargo, kContentTypeNotifications)
- ->content_setting);
+ ->status);
// Check there are 2 blocked origins.
{
@@ -601,6 +598,66 @@ TEST_F(SiteSettingsHelperTest, CookieExceptions) {
}
}
+TEST_F(SiteSettingsHelperTest, GetExpirationDescription) {
+ base::subtle::ScopedTimeClockOverrides time_override(
+ &SiteSettingsHelperTest::GetReferenceTime,
+ /*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr);
+
+ auto description =
+ GetExpirationDescription(GetReferenceTime() + base::Days(0));
+
+ EXPECT_EQ(description, l10n_util::GetPluralStringFUTF16(
+ IDS_SETTINGS_EXPIRES_AFTER_TIME_LABEL, 0));
+}
+
+TEST_F(SiteSettingsHelperTest, GetExpirationDescription_Tomorrow) {
+ base::subtle::ScopedTimeClockOverrides time_override(
+ &SiteSettingsHelperTest::GetReferenceTime,
+ /*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr);
+
+ auto description =
+ GetExpirationDescription(GetReferenceTime() + base::Days(1));
+
+ EXPECT_EQ(description, l10n_util::GetPluralStringFUTF16(
+ IDS_SETTINGS_EXPIRES_AFTER_TIME_LABEL, 1));
+}
+
+TEST_F(SiteSettingsHelperTest,
+ GetExpirationDescription_Tomorrow_LessThan24_AfterMidnight) {
+ base::subtle::ScopedTimeClockOverrides time_override(
+ &SiteSettingsHelperTest::GetReferenceTime,
+ /*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr);
+
+ auto description =
+ GetExpirationDescription(GetReferenceTime() + base::Hours(14));
+
+ EXPECT_EQ(description, l10n_util::GetPluralStringFUTF16(
+ IDS_SETTINGS_EXPIRES_AFTER_TIME_LABEL, 1));
+}
+
+TEST_F(SiteSettingsHelperTest,
+ GetExpirationDescription_Tomorrow_LessThan24_BeforeMidnight) {
+ base::subtle::ScopedTimeClockOverrides time_override(
+ &SiteSettingsHelperTest::GetReferenceTime,
+ /*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr);
+
+ auto description =
+ GetExpirationDescription(GetReferenceTime() + base::Hours(12));
+ EXPECT_EQ(description, l10n_util::GetPluralStringFUTF16(
+ IDS_SETTINGS_EXPIRES_AFTER_TIME_LABEL, 0));
+}
+
+TEST_F(SiteSettingsHelperTest, GetExpirationDescription_Expired) {
+ base::subtle::ScopedTimeClockOverrides time_override(
+ &SiteSettingsHelperTest::GetReferenceTime,
+ /*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr);
+
+ auto description =
+ GetExpirationDescription(GetReferenceTime() - base::Days(4));
+ EXPECT_EQ(description, l10n_util::GetPluralStringFUTF16(
+ IDS_SETTINGS_EXPIRES_AFTER_TIME_LABEL, 0));
+}
+
namespace {
void ExpectValidChooserExceptionObject(
@@ -774,89 +831,6 @@ TEST_F(SiteSettingsHelperTest, CreateChooserExceptionObject) {
}
}
-TEST_F(SiteSettingsHelperTest, PopulateNotificationPermissionReviewData) {
- TestingProfile profile;
- base::test::ScopedFeatureList scoped_feature;
- scoped_feature.InitAndEnableFeature(
- ::features::kSafetyCheckNotificationPermissions);
-
- // Add a couple of notification permission and check they appear in review
- // list.
- HostContentSettingsMap* map =
- HostContentSettingsMapFactory::GetForProfile(&profile);
- GURL urls[] = {GURL("https://google.com:443"),
- GURL("https://www.youtube.com:443"),
- GURL("https://www.example.com:443")};
-
- map->SetContentSettingDefaultScope(urls[0], GURL(),
- ContentSettingsType::NOTIFICATIONS,
- CONTENT_SETTING_ALLOW);
- map->SetContentSettingDefaultScope(urls[1], GURL(),
- ContentSettingsType::NOTIFICATIONS,
- CONTENT_SETTING_ALLOW);
- map->SetContentSettingDefaultScope(urls[2], GURL(),
- ContentSettingsType::NOTIFICATIONS,
- CONTENT_SETTING_ALLOW);
-
- // Record initial display date to enable comparing dictionaries for
- // NotificationEngagementService.
- auto* notification_engagement_service =
- NotificationsEngagementServiceFactory::GetForProfile(&profile);
- std::string displayedDate =
- notification_engagement_service->GetBucketLabel(base::Time::Now());
-
- auto* site_engagement_service =
- site_engagement::SiteEngagementServiceFactory::GetForProfile(&profile);
-
- // Set a host to have minimum engagement. This should be in review list.
- RecordNotification(notification_engagement_service, urls[0], 1);
- site_engagement::SiteEngagementScore score =
- site_engagement_service->CreateEngagementScore(urls[0]);
- score.Reset(0.5, GetReferenceTime());
- score.Commit();
- EXPECT_EQ(blink::mojom::EngagementLevel::MINIMAL,
- site_engagement_service->GetEngagementLevel(urls[0]));
-
- // Set a host to have large number of notifications, but low engagement. This
- // should be in review list.
- RecordNotification(notification_engagement_service, urls[1], 5);
- site_engagement_service->AddPointsForTesting(urls[1], 1.0);
- EXPECT_EQ(blink::mojom::EngagementLevel::LOW,
- site_engagement_service->GetEngagementLevel(urls[1]));
-
- // Set a host to have medium engagement and high notification count. This
- // should not be in review list.
- RecordNotification(notification_engagement_service, urls[2], 5);
- site_engagement_service->AddPointsForTesting(urls[2], 50.0);
- EXPECT_EQ(blink::mojom::EngagementLevel::MEDIUM,
- site_engagement_service->GetEngagementLevel(urls[2]));
-
- const auto& notification_permissions =
- PopulateNotificationPermissionReviewData(&profile);
- // Check if resulting list contains only the expected URLs. They should be in
- // descending order of notification count.
- EXPECT_EQ(2UL, notification_permissions.size());
- EXPECT_EQ("https://www.youtube.com:443",
- *notification_permissions[0].GetDict().FindString(
- site_settings::kOrigin));
- EXPECT_EQ("https://google.com:443",
- *notification_permissions[1].GetDict().FindString(
- site_settings::kOrigin));
-
- // Increasing notification count also promotes host in the list.
- RecordNotification(notification_engagement_service,
- GURL("https://google.com:443"), 10);
- const auto& updated_notification_permissions =
- PopulateNotificationPermissionReviewData(&profile);
- EXPECT_EQ(2UL, updated_notification_permissions.size());
- EXPECT_EQ("https://google.com:443",
- *updated_notification_permissions[0].GetDict().FindString(
- site_settings::kOrigin));
- EXPECT_EQ("https://www.youtube.com:443",
- *updated_notification_permissions[1].GetDict().FindString(
- site_settings::kOrigin));
-}
-
namespace {
constexpr char kUsbPolicySetting[] = R"(
@@ -1081,9 +1055,12 @@ TEST_F(PersistentPermissionsSiteSettingsHelperTest,
// Initialize and populate the `grants` object with permissions.
ChromeFileSystemAccessPermissionContext* context =
FileSystemAccessPermissionContextFactory::GetForProfile(&profile);
- auto empty_grants = context->GetPermissionGrants(kTestOrigin);
+ auto empty_grants =
+ context->ConvertObjectsToGrants(context->GetGrantedObjects(kTestOrigin));
EXPECT_TRUE(empty_grants.file_write_grants.empty());
+ context->SetOriginHasExtendedPermissionForTesting(kTestOrigin);
+
auto file_write_grant = context->GetWritePermissionGrant(
kTestOrigin, kTestPath,
ChromeFileSystemAccessPermissionContext::HandleType::kFile,
@@ -1092,7 +1069,9 @@ TEST_F(PersistentPermissionsSiteSettingsHelperTest,
kTestOrigin, kTestPath2,
ChromeFileSystemAccessPermissionContext::HandleType::kFile,
ChromeFileSystemAccessPermissionContext::UserAction::kSave);
- auto populated_grants = context->GetPermissionGrants(kTestOrigin);
+
+ auto populated_grants =
+ context->ConvertObjectsToGrants(context->GetGrantedObjects(kTestOrigin));
EXPECT_FALSE(populated_grants.file_write_grants.empty());
base::Value::List exceptions;
diff --git a/chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc b/chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
index 77899f0d491..46b241f5284 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
@@ -40,6 +40,7 @@
#include "components/page_image_service/features.h"
#include "components/page_image_service/image_service.h"
#include "components/page_image_service/image_service_handler.h"
+#include "components/policy/core/common/policy_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
@@ -156,6 +157,8 @@ BookmarksSidePanelUI::BookmarksSidePanelUI(content::WebUI* web_ui)
{"checkboxA11yLabel", IDS_BOOKMARKS_CHECKBOX_LABEL},
{"editInvalidUrl", IDS_BOOKMARK_MANAGER_INVALID_URL},
{"bookmarkFolderChildCount", IDS_BOOKMARK_FOLDER_CHILD_COUNT},
+ {"primaryFilterHeading", IDS_BOOKMARKS_PRIMARY_FILTER_HEADING},
+ {"secondaryFilterHeading", IDS_BOOKMARKS_SECONDARY_FILTER_HEADING},
};
for (const auto& str : kLocalizedStrings)
webui::AddLocalizedString(source, str.name, str.id);
@@ -177,6 +180,7 @@ BookmarksSidePanelUI::BookmarksSidePanelUI(content::WebUI* web_ui)
source->AddBoolean("guestMode", profile->IsGuestSession());
source->AddBoolean("incognitoMode", profile->IsIncognitoProfile());
+ source->AddBoolean("isIncognitoModeAvailable", IsIncognitoModeAvailable());
source->AddInteger(
"sortOrder",
prefs->GetInteger(bookmarks_webui::prefs::kBookmarksSortOrder));
@@ -299,3 +303,9 @@ void BookmarksSidePanelUI::CreateShoppingListHandler(
std::make_unique<commerce::ShoppingListContextMenuController>(
bookmark_model, shopping_service, shopping_list_handler_.get());
}
+
+bool BookmarksSidePanelUI::IsIncognitoModeAvailable() {
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ return prefs->GetInteger(policy::policy_prefs::kIncognitoModeAvailability) ==
+ static_cast<int>(policy::IncognitoModeAvailability::kEnabled);
+}
diff --git a/chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.h b/chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.h
index 1b3cc420a67..44d290c4d81 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.h
+++ b/chromium/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.h
@@ -75,6 +75,8 @@ class BookmarksSidePanelUI
mojo::PendingReceiver<shopping_list::mojom::ShoppingListHandler> receiver)
override;
+ bool IsIncognitoModeAvailable();
+
std::unique_ptr<BookmarksPageHandler> bookmarks_page_handler_;
mojo::Receiver<side_panel::mojom::BookmarksPageHandlerFactory>
bookmarks_page_factory_receiver_{this};
diff --git a/chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.cc b/chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.cc
index e29c34dd1a3..b729e2babcf 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.cc
@@ -41,6 +41,7 @@
#include "components/unified_consent/unified_consent_service.h"
#include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/base/url_util.h"
@@ -68,6 +69,7 @@ CompanionPageHandler::CompanionPageHandler(
GetProfile()->GetPrefs())) {
identity_manager_observation_.Observe(
IdentityManagerFactory::GetForProfile(GetProfile()));
+ // TODO(crbug.com/1476887): Observe PCO similarly.
consent_helper_observation_.Observe(consent_helper_.get());
if (base::FeatureList::IsEnabled(
visual_search::features::kVisualSearchSuggestions)) {
@@ -170,11 +172,11 @@ void CompanionPageHandler::DidFinishLoad(
}
void CompanionPageHandler::SendVisualSearchResult(
- const std::vector<std::string>& results) {
+ const visual_search::VisualSuggestionsResults& results) {
std::vector<side_panel::mojom::VisualSearchResultPtr> final_results;
for (const auto& result : results) {
- final_results.emplace_back(
- side_panel::mojom::VisualSearchResult::New(result));
+ final_results.emplace_back(side_panel::mojom::VisualSearchResult::New(
+ result.base64_img, result.alt_text));
}
page_->OnDeviceVisualClassificationResult(std::move(final_results));
base::UmaHistogramTimes(
@@ -186,7 +188,7 @@ void CompanionPageHandler::SendVisualSearchResult(
}
void CompanionPageHandler::HandleVisualSearchResult(
- const std::vector<std::string> results,
+ const visual_search::VisualSuggestionsResults results,
const VisualSuggestionsMetrics metrics) {
// This is the only place where we log UKM metrics for the visual
// classification pipeline. We record the metrics even when the UI is not
@@ -220,7 +222,7 @@ void CompanionPageHandler::OnLoadingState(
if (loading_state == side_panel::mojom::LoadingState::kStartedLoading) {
ui_loading_start_time_ = base::TimeTicks::Now();
if (visual_result) {
- SendVisualSearchResult(visual_result.value().second);
+ SendVisualSearchResult(visual_result.value());
} else {
visual_search::VisualSearchClassifierHost::ResultCallback callback =
base::BindOnce(&CompanionPageHandler::HandleVisualSearchResult,
@@ -252,6 +254,7 @@ void CompanionPageHandler::ShowUI() {
// page.
auto* browser = GetBrowser();
if (!browser) {
+ base::UmaHistogramBoolean("Companion.SidePanel.ShowUiSuccess", false);
return;
}
@@ -272,15 +275,18 @@ void CompanionPageHandler::ShowUI() {
// requested from the feedback UI.
RegisterModalDialogManager(browser);
+ base::UmaHistogramBoolean("Companion.SidePanel.ShowUiSuccess", true);
+
// If searching the text query succeeds, then early return.
if (OnSearchTextQuery()) {
return;
}
- std::unique_ptr<side_panel::mojom::ImageQuery> image_query =
- helper->GetImageQuery();
- if (image_query) {
- OnImageQuery(*image_query);
+ if (helper->HasImageQuery()) {
+ // If there is an image query to run, we need to wait until the side panel
+ // view has bounds in order for us to issue the request to Lens properly.
+ // This is called in the CompanionSidePanelController once it detects a
+ // change in the Companion's WebContents bounds.
return;
}
@@ -293,18 +299,19 @@ bool CompanionPageHandler::OnSearchTextQuery() {
auto* helper = companion::CompanionTabHelper::FromWebContents(web_contents());
CHECK(helper);
const std::string query = helper->GetTextQuery();
+ std::unique_ptr<base::Time> query_start_time =
+ helper->GetTextQueryStartTime();
if (query.empty()) {
return false;
}
- // Only notify the companion UI the page changed if we can share
- // information about the page by user consent.
GURL page_url;
if (IsUserPermittedToSharePageInfoWithCompanion(GetProfile()->GetPrefs())) {
page_url = web_contents()->GetVisibleURL();
}
- GURL companion_url = url_builder_->BuildCompanionURL(page_url, query);
+ GURL companion_url = url_builder_->BuildCompanionURL(
+ page_url, query, std::move(query_start_time));
page_->LoadCompanionPage(companion_url);
return true;
}
@@ -387,6 +394,9 @@ void CompanionPageHandler::OnExpsOptInStatusAvailable(bool is_exps_opted_in) {
void CompanionPageHandler::OnOpenInNewTabButtonURLChanged(
const GURL& url_to_open) {
+ if (!web_contents()) {
+ return;
+ }
auto* companion_helper =
companion::CompanionTabHelper::FromWebContents(web_contents());
DCHECK(companion_helper);
@@ -457,6 +467,10 @@ void CompanionPageHandler::OpenUrlInBrowser(
signin_delegate_->OpenUrlInBrowser(url_to_open.value(), use_new_tab);
}
+void CompanionPageHandler::RefreshCompanionPage() {
+ NotifyURLChanged(/*is_full_reload*/ true);
+}
+
void CompanionPageHandler::OnNavigationError() {
page_->OnNavigationError();
}
diff --git a/chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.h b/chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.h
index 703c010eb5b..656eae96b41 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.h
+++ b/chromium/chrome/browser/ui/webui/side_panel/companion/companion_page_handler.h
@@ -68,6 +68,7 @@ class CompanionPageHandler
void OpenUrlInBrowser(const absl::optional<GURL>& url_to_open,
bool use_new_tab) override;
void OnLoadingState(side_panel::mojom::LoadingState loading_state) override;
+ void RefreshCompanionPage() override;
// content::WebContentsObserver overrides.
void DidFinishNavigation(
@@ -126,11 +127,13 @@ class CompanionPageHandler
// This method is used as the callback that handles visual search results.
// Its role is to perform some checks and do a mojom IPC to side panel.
- void HandleVisualSearchResult(const std::vector<std::string> results,
- const VisualSuggestionsMetrics stats);
+ void HandleVisualSearchResult(
+ const visual_search::VisualSuggestionsResults results,
+ const VisualSuggestionsMetrics stats);
// Method responsible for binding and sending VQS results to panel.
- void SendVisualSearchResult(const std::vector<std::string>& results);
+ void SendVisualSearchResult(
+ const visual_search::VisualSuggestionsResults& results);
mojo::Receiver<side_panel::mojom::CompanionPageHandler> receiver_;
mojo::Remote<side_panel::mojom::CompanionPage> page_;
diff --git a/chromium/chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.cc b/chromium/chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.cc
index 6d08bd5d97e..bf89717e29b 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/companion/companion_side_panel_untrusted_ui.cc
@@ -43,8 +43,9 @@ CompanionSidePanelUntrustedUI::CompanionSidePanelUntrustedUI(
: companion::GetHomepageURLForCompanion();
// Allow iframing accounts page due to potential redirects.
std::string frameSrcDirective =
- std::string("frame-src https://accounts.google.com ") + frameSrcString +
- ";";
+ std::string(
+ "frame-src https://accounts.google.com https://consent.google.com ") +
+ frameSrcString + ";";
std::string formActionDirective =
std::string("form-action ") + frameSrcString + ";";
html_source->OverrideContentSecurityPolicy(
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/OWNERS b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/OWNERS
index 611a17fda5f..0a7ba8594f6 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/OWNERS
+++ b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/OWNERS
@@ -1,4 +1,6 @@
file://chrome/browser/resources/new_tab_page/OWNERS
per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS \ No newline at end of file
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS \ No newline at end of file
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
index ce5e172f899..ba26a631647 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
+++ b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
@@ -17,8 +17,6 @@ struct BackgroundImage {
bool is_uploaded_image;
// Title of the background image.
string title;
- // The main color of the background image.
- skia.mojom.SkColor? main_color;
// Collection id of the background image.
string collection_id;
// Whether daily refresh is enabled.
@@ -39,34 +37,14 @@ struct Theme {
BackgroundImage? background_image;
// User's third party theme info.
ThirdPartyThemeInfo? third_party_theme_info;
- // Whether the browser theming is dark mode.
- bool is_dark_mode;
- // The color this theme was calculated from.
- skia.mojom.SkColor seed_color;
// The current theme background color.
skia.mojom.SkColor background_color;
// The current theme foreground color. If not set, we use the default theme.
skia.mojom.SkColor? foreground_color;
- // The color of the color picker icon.
- skia.mojom.SkColor color_picker_icon_color;
- // True if the colors are managed by a policy.
- bool colors_managed_by_policy;
// True if the background is managed by a policy.
bool background_managed_by_policy;
-};
-
-struct ChromeColor {
- // Localized string of the color name.
- string name;
- // The color this Chrome color was calculated from.
- skia.mojom.SkColor seed;
- // The foreground color.
- skia.mojom.SkColor background;
- // The background color.
- skia.mojom.SkColor foreground;
- // The base color.
- // TODO(crbug/1454868): Make base no longer optional once GM2 is removed.
- skia.mojom.SkColor? base;
+ // True if we are following the OS theme.
+ bool follow_device_theme;
};
// A collection of background images.
@@ -125,12 +103,6 @@ interface CustomizeChromePageHandler {
// Triggers a call to |CustomizeChromePage.SetPageMostVisitedSettings|.
UpdateMostVisitedSettings();
- // Returns the chrome colors used in the customize chrome side panel overview.
- GetOverviewChromeColors(bool is_dark_mode) => (array<ChromeColor> colors);
-
- // Returns the chrome colors used in the customize chrome side panel themes.
- GetChromeColors() => (array<ChromeColor> colors);
-
// Returns the collections of background images.
GetBackgroundCollections() => (array<BackgroundCollection> collections);
@@ -146,8 +118,8 @@ interface CustomizeChromePageHandler {
// Sets Chrome's theme according to the default color.
SetDefaultColor();
- // Sets a Chrome theme generated from |seed_color|.
- SetSeedColor(skia.mojom.SkColor seed_color);
+ // Set that the theme should follow the device's.
+ SetFollowDeviceTheme(bool follow);
// Removes background image.
RemoveBackgroundImage();
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_browsertest.cc b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_browsertest.cc
index 073707fd323..2a56a4cda99 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_browsertest.cc
@@ -11,16 +11,16 @@
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
-#include "components/search/ntp_features.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
+#include "ui/base/ui_base_features.h"
class CustomizeChromeSidePanelBrowserTest : public InProcessBrowserTest {
protected:
// InProcessBrowserTest:
void SetUp() override {
- scoped_feature_list_.InitWithFeatures(
- {ntp_features::kCustomizeChromeSidePanel}, {});
+ scoped_feature_list_.InitWithFeatures({features::kCustomizeChromeSidePanel},
+ {});
InProcessBrowserTest::SetUp();
}
// Activates the browser tab at `index`.
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.cc b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.cc
deleted file mode 100644
index 383c9e56946..00000000000
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.h"
-
-#include <array>
-#include <utility>
-
-#include "chrome/browser/new_tab_page/chrome_colors/generated_colors_info.h"
-#include "chrome/browser/new_tab_page/chrome_colors/selected_colors_info.h"
-
-namespace {
-
-// Returns chrome color with ID |kCustomizeChromeColorIds[I]|.
-template <size_t I>
-constexpr chrome_colors::ColorInfo GetChromeColor() {
- constexpr int id = kCustomizeChromeColorIds[I];
- // We assume that chrome colors are stored sequentially ordered by their ID
- // starting at ID 1.
- constexpr auto chrome_color = chrome_colors::kGeneratedColorsInfo[id - 1];
- static_assert(chrome_color.id == id);
- return chrome_color;
-}
-
-template <std::size_t... I>
-constexpr auto MakeCustomizeChromeColors(std::index_sequence<I...>) {
- return std::array<chrome_colors::ColorInfo, sizeof...(I)>{
- GetChromeColor<I>()...};
-}
-
-} // namespace
-
-const decltype(kCustomizeChromeColors) kCustomizeChromeColors =
- MakeCustomizeChromeColors(
- std::make_index_sequence<std::size(kCustomizeChromeColorIds)>{});
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
index 158a2022158..ec886ca4121 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
@@ -7,9 +7,9 @@
#include "base/containers/contains.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
-#include "chrome/browser/new_tab_page/chrome_colors/chrome_colors_factory.h"
-#include "chrome/browser/new_tab_page/chrome_colors/generated_colors_info.h"
-#include "chrome/browser/new_tab_page/chrome_colors/selected_colors_info.h"
+#include "chrome/browser/browser_features.h"
+#include "chrome/browser/manta/manta_service_factory.h"
+#include "chrome/browser/manta/snapper_provider.h"
#include "chrome/browser/new_tab_page/modules/new_tab_page_modules.h"
#include "chrome/browser/new_tab_page/new_tab_page_util.h"
#include "chrome/browser/profiles/profile.h"
@@ -23,12 +23,13 @@
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/search/ntp_user_data_types.h"
#include "chrome/browser/ui/webui/new_tab_page/ntp_pref_names.h"
-#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.h"
#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
#include "chrome/common/pref_names.h"
-#include "chrome/common/themes/autogenerated_theme_util.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/endpoint_fetcher/endpoint_fetcher.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
+#include "components/search/ntp_features.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
@@ -36,42 +37,8 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_provider.h"
-#include "ui/color/color_provider_manager.h"
-#include "ui/color/dynamic_color/palette_factory.h"
#include "ui/native_theme/native_theme.h"
-namespace {
-// Create a GM2 chrome color.
-side_panel::mojom::ChromeColorPtr CreateChromeColor(
- chrome_colors::ColorInfo color_info) {
- auto theme_colors = GetAutogeneratedThemeColors(color_info.color);
- auto color = side_panel::mojom::ChromeColor::New();
- color->name = l10n_util::GetStringUTF8(color_info.label_id);
- color->seed = color_info.color;
- color->background = theme_colors.active_tab_color;
- color->foreground = theme_colors.frame_color;
- return color;
-}
-
-// Create a GM3 chrome color that takes color scheme into account.
-side_panel::mojom::ChromeColorPtr CreateDynamicChromeColor(
- chrome_colors::ColorInfo color_info,
- bool is_dark_mode) {
- auto color = side_panel::mojom::ChromeColor::New();
- auto color_palette = ui::GeneratePalette(
- color_info.color, ui::ColorProviderManager::SchemeVariant::kTonalSpot);
- color->name = l10n_util::GetStringUTF8(color_info.label_id);
- color->seed = color_info.color;
- color->background = is_dark_mode ? color_palette->primary().get(80)
- : color_palette->primary().get(40);
- color->foreground = is_dark_mode ? color_palette->primary().get(30)
- : color_palette->primary().get(90);
- color->base = is_dark_mode ? color_palette->secondary().get(50)
- : color_palette->primary().get(80);
- return color;
-}
-} // namespace
-
CustomizeChromePageHandler::CustomizeChromePageHandler(
mojo::PendingReceiver<side_panel::mojom::CustomizeChromePageHandler>
pending_page_handler,
@@ -123,6 +90,13 @@ CustomizeChromePageHandler::CustomizeChromePageHandler(
ntp_custom_background_service_observation_.Observe(
ntp_custom_background_service_.get());
+
+ if (base::FeatureList::IsEnabled(
+ ntp_features::kCustomizeChromeWallpaperSearch) &&
+ base::FeatureList::IsEnabled(features::kMantaService)) {
+ manta_service_ = manta::MantaServiceFactory::GetForProfile(profile_);
+ snapper_provider_ = manta_service_->CreateSnapperProvider();
+ }
}
CustomizeChromePageHandler::~CustomizeChromePageHandler() {
@@ -154,37 +128,12 @@ void CustomizeChromePageHandler::ScrollToSection(
}
void CustomizeChromePageHandler::SetDefaultColor() {
+ theme_service_->UseDeviceTheme(false);
theme_service_->UseDefaultTheme();
}
-void CustomizeChromePageHandler::SetSeedColor(SkColor seed_color) {
- theme_service_->BuildAutogeneratedThemeFromColor(seed_color);
-}
-
-void CustomizeChromePageHandler::GetOverviewChromeColors(
- bool is_dark_mode,
- GetOverviewChromeColorsCallback callback) {
- std::vector<side_panel::mojom::ChromeColorPtr> colors;
- if (features::IsChromeWebuiRefresh2023()) {
- // TODO(crbug/1430277): Loop through new list of seed colors for GM3.
- for (const auto& color_info : kCustomizeChromeColors) {
- colors.push_back(CreateDynamicChromeColor(color_info, is_dark_mode));
- }
- } else {
- for (const auto& color_info : kCustomizeChromeColors) {
- colors.push_back(CreateChromeColor(color_info));
- }
- }
- std::move(callback).Run(std::move(colors));
-}
-
-void CustomizeChromePageHandler::GetChromeColors(
- GetChromeColorsCallback callback) {
- std::vector<side_panel::mojom::ChromeColorPtr> colors;
- for (const auto& color_info : chrome_colors::kGeneratedColorsInfo) {
- colors.push_back(CreateChromeColor(color_info));
- }
- std::move(callback).Run(std::move(colors));
+void CustomizeChromePageHandler::SetFollowDeviceTheme(bool follow) {
+ theme_service_->UseDeviceTheme(follow);
}
void CustomizeChromePageHandler::SetBackgroundImage(
@@ -273,6 +222,23 @@ void CustomizeChromePageHandler::RemoveBackgroundImage() {
}
}
+void CustomizeChromePageHandler::WallpaperSearchCallback(
+ std::unique_ptr<EndpointResponse> response) {
+ return;
+}
+
+void CustomizeChromePageHandler::GetWallpaperSearchBackground() {
+ if (base::FeatureList::IsEnabled(
+ ntp_features::kCustomizeChromeWallpaperSearch) &&
+ base::FeatureList::IsEnabled(features::kMantaService)) {
+ const std::string json = R"({"data": ""})";
+ snapper_provider_->Call(
+ json,
+ base::BindOnce(&CustomizeChromePageHandler::WallpaperSearchCallback,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
void CustomizeChromePageHandler::UpdateTheme() {
if (ntp_custom_background_service_) {
ntp_custom_background_service_->RefreshBackgroundIfNeeded();
@@ -293,24 +259,32 @@ void CustomizeChromePageHandler::UpdateTheme() {
background_image->collection_id = custom_background->collection_id;
background_image->daily_refresh_enabled =
custom_background->daily_refresh_enabled;
- if (custom_background->custom_background_main_color.has_value()) {
- background_image->main_color =
- *custom_background->custom_background_main_color;
- }
} else {
background_image = nullptr;
}
theme->background_image = std::move(background_image);
- theme->seed_color = theme_service_->GetAutogeneratedThemeColor();
- theme->background_color =
- web_contents_->GetColorProvider().GetColor(kColorNewTabPageBackground);
- if (!theme_service_->UsingDefaultTheme() &&
- !theme_service_->UsingSystemTheme()) {
- theme->foreground_color =
- web_contents_->GetColorProvider().GetColor(ui::kColorFrameActive);
- theme->colors_managed_by_policy = theme_service_->UsingPolicyTheme();
+ theme->follow_device_theme = theme_service_->UsingDeviceTheme();
+
+ auto user_color = theme_service_->GetUserColor();
+ // If a user has the GM3 flag enabled but a GM2 theme set they are in a limbo
+ // state between the 2 theme types. We need to get the color of their theme
+ // with GetAutogeneratedThemeColor still until they set a GM3 theme, use the
+ // old way of detecting default, and use the old color tokens to keep an
+ // accurate representation of what the user is seeing.
+ if (features::IsChromeWebuiRefresh2023() && user_color.has_value()) {
+ theme->background_color =
+ web_contents_->GetColorProvider().GetColor(ui::kColorSysInversePrimary);
+ if (user_color.value() != SK_ColorTRANSPARENT) {
+ theme->foreground_color = theme->background_color;
+ }
} else {
- theme->colors_managed_by_policy = false;
+ theme->background_color =
+ web_contents_->GetColorProvider().GetColor(kColorNewTabPageBackground);
+ if (!theme_service_->UsingDefaultTheme() &&
+ !theme_service_->UsingSystemTheme()) {
+ theme->foreground_color =
+ web_contents_->GetColorProvider().GetColor(ui::kColorFrameActive);
+ }
}
theme->background_managed_by_policy =
ntp_custom_background_service_->IsCustomBackgroundDisabledByPolicy();
@@ -327,22 +301,6 @@ void CustomizeChromePageHandler::UpdateTheme() {
theme->third_party_theme_info = std::move(third_party_theme_info);
}
}
- theme->color_picker_icon_color =
- web_contents_->GetColorProvider().GetColor(kColorNewTabPageText);
- auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
- CHECK(native_theme);
- // If Chrome WebUI Refresh 2023 flag is enabled and BrowserColorScheme is not
- // set to follow the system, use BrowserColorScheme for deciding dark mode
- // boolean. Otherwise, use the system value.
- ThemeService::BrowserColorScheme colorScheme =
- theme_service_->GetBrowserColorScheme();
- if (features::IsChromeWebuiRefresh2023() &&
- colorScheme != ThemeService::BrowserColorScheme::kSystem) {
- theme->is_dark_mode =
- colorScheme == ThemeService::BrowserColorScheme::kDark;
- } else {
- theme->is_dark_mode = native_theme->ShouldUseDarkColors();
- }
page_->SetTheme(std::move(theme));
}
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
index 1d2d61e714e..da079dce24a 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
+++ b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
@@ -6,7 +6,10 @@
#define CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_PAGE_HANDLER_H_
#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
+#include "chrome/browser/manta/manta_service.h"
+#include "chrome/browser/manta/snapper_provider.h"
#include "chrome/browser/search/background/ntp_background_service.h"
#include "chrome/browser/search/background/ntp_background_service_observer.h"
#include "chrome/browser/search/background/ntp_custom_background_service.h"
@@ -16,6 +19,7 @@
#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom.h"
#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
#include "chrome/common/search/ntp_logging_events.h"
+#include "components/endpoint_fetcher/endpoint_fetcher.h"
#include "components/prefs/pref_change_registrar.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
@@ -68,11 +72,7 @@ class CustomizeChromePageHandler
// side_panel::mojom::CustomizeChromePageHandler:
void SetDefaultColor() override;
- void SetSeedColor(SkColor seed_color) override;
- void GetOverviewChromeColors(
- bool is_dark_mode,
- GetOverviewChromeColorsCallback callback) override;
- void GetChromeColors(GetChromeColorsCallback callback) override;
+ void SetFollowDeviceTheme(bool follow) override;
void SetBackgroundImage(const std::string& attribution_1,
const std::string& attribution_2,
const GURL& attribution_url,
@@ -97,8 +97,11 @@ class CustomizeChromePageHandler
void UpdateModulesSettings() override;
void UpdateScrollToSection() override;
+ void GetWallpaperSearchBackground();
+
private:
void LogEvent(NTPLoggingEventType event);
+ void WallpaperSearchCallback(std::unique_ptr<EndpointResponse> response);
bool IsCustomLinksEnabled() const;
bool IsShortcutsVisible() const;
@@ -131,6 +134,8 @@ class CustomizeChromePageHandler
scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
raw_ptr<content::WebContents> web_contents_;
raw_ptr<NtpBackgroundService> ntp_background_service_;
+ raw_ptr<manta::MantaService> manta_service_;
+ std::unique_ptr<manta::SnapperProvider> snapper_provider_;
GetBackgroundCollectionsCallback background_collections_callback_;
base::TimeTicks background_collections_request_start_time_;
std::string images_request_collection_id_;
@@ -154,6 +159,8 @@ class CustomizeChromePageHandler
mojo::Remote<side_panel::mojom::CustomizeChromePage> page_;
mojo::Receiver<side_panel::mojom::CustomizeChromePageHandler> receiver_;
+
+ base::WeakPtrFactory<CustomizeChromePageHandler> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_PAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
index 220d263c030..fc672aa811c 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
@@ -15,10 +15,6 @@
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/new_tab_page/chrome_colors/chrome_colors_factory.h"
-#include "chrome/browser/new_tab_page/chrome_colors/chrome_colors_service.h"
-#include "chrome/browser/new_tab_page/chrome_colors/generated_colors_info.h"
-#include "chrome/browser/new_tab_page/chrome_colors/selected_colors_info.h"
#include "chrome/browser/search/background/ntp_background_data.h"
#include "chrome/browser/search/background/ntp_background_service_factory.h"
#include "chrome/browser/search/background/ntp_custom_background_service.h"
@@ -31,10 +27,8 @@
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/webui/new_tab_page/ntp_pref_names.h"
#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom.h"
-#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_colors.h"
#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
#include "chrome/common/pref_names.h"
-#include "chrome/common/themes/autogenerated_theme_util.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/test_browser_window.h"
#include "chrome/test/base/testing_profile.h"
@@ -56,8 +50,6 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_provider.h"
-#include "ui/color/color_provider_manager.h"
-#include "ui/color/dynamic_color/palette_factory.h"
#include "ui/native_theme/native_theme.h"
#include "ui/shell_dialogs/select_file_dialog_factory.h"
@@ -104,7 +96,7 @@ class TestSelectFileDialog : public ui::SelectFileDialog {
bool IsRunning(gfx::NativeWindow owning_window) const override {
return false;
}
- void ListenerDestroyed() override {}
+ void ListenerDestroyed() override { listener_ = nullptr; }
bool HasMultipleFileTypeChoicesImpl() override { return false; }
private:
@@ -204,15 +196,14 @@ class MockThemeService : public ThemeService {
public:
MockThemeService() : ThemeService(nullptr, theme_helper_) { set_ready(); }
using ThemeService::NotifyThemeChanged;
+ MOCK_METHOD(void, UseDefaultTheme, ());
+ MOCK_METHOD(void, UseDeviceTheme, (bool));
MOCK_CONST_METHOD0(UsingDefaultTheme, bool());
- MOCK_CONST_METHOD0(UsingExtensionTheme, bool());
MOCK_CONST_METHOD0(UsingSystemTheme, bool());
- MOCK_CONST_METHOD0(UsingPolicyTheme, bool());
- MOCK_CONST_METHOD0(GetAutogeneratedThemeColor, SkColor());
+ MOCK_CONST_METHOD0(UsingExtensionTheme, bool());
MOCK_CONST_METHOD0(GetThemeID, std::string());
- MOCK_CONST_METHOD0(GetBrowserColorScheme, ThemeService::BrowserColorScheme());
- MOCK_METHOD(void, UseDefaultTheme, ());
- MOCK_METHOD(void, BuildAutogeneratedThemeFromColor, (SkColor));
+ MOCK_CONST_METHOD0(GetUserColor, absl::optional<SkColor>());
+ MOCK_CONST_METHOD0(UsingDeviceTheme, bool());
private:
ThemeHelper theme_helper_;
@@ -367,109 +358,6 @@ TEST_F(CustomizeChromePageHandlerTest, SetMostVisitedSettings) {
histogram_tester().ExpectTotalCount("NewTabPage.CustomizeShortcutAction", 2);
}
-TEST_F(CustomizeChromePageHandlerTest, GetOverviewChromeColors) {
- std::vector<side_panel::mojom::ChromeColorPtr> colors;
- base::MockCallback<CustomizeChromePageHandler::GetChromeColorsCallback>
- callback;
- EXPECT_CALL(callback, Run(testing::_))
- .Times(1)
- .WillOnce(testing::Invoke(
- [&colors](std::vector<side_panel::mojom::ChromeColorPtr> colors_arg) {
- colors = std::move(colors_arg);
- }));
- handler().GetOverviewChromeColors(false, callback.Get());
-
- ASSERT_EQ(kCustomizeChromeColors.size(), colors.size());
- for (size_t i = 0; i < kCustomizeChromeColors.size(); i++) {
- EXPECT_EQ(l10n_util::GetStringUTF8(kCustomizeChromeColors[i].label_id),
- colors[i]->name);
- EXPECT_EQ(kCustomizeChromeColors[i].color, colors[i]->seed);
- EXPECT_EQ(GetAutogeneratedThemeColors(kCustomizeChromeColors[i].color)
- .active_tab_color,
- colors[i]->background);
- EXPECT_EQ(GetAutogeneratedThemeColors(kCustomizeChromeColors[i].color)
- .frame_color,
- colors[i]->foreground);
- }
-}
-
-TEST_F(CustomizeChromePageHandlerTest, GetOverviewChromeColorsGM3) {
- scoped_feature_list_.InitWithFeatures(
- {features::kChromeRefresh2023, features::kChromeWebuiRefresh2023}, {});
- std::vector<side_panel::mojom::ChromeColorPtr> colors;
- base::MockCallback<CustomizeChromePageHandler::GetChromeColorsCallback>
- callback;
- EXPECT_CALL(callback, Run(testing::_))
- .Times(2)
- .WillRepeatedly(testing::Invoke(
- [&colors](std::vector<side_panel::mojom::ChromeColorPtr> colors_arg) {
- colors = std::move(colors_arg);
- }));
-
- // Test with Dark Mode on.
- handler().GetOverviewChromeColors(true, callback.Get());
-
- ASSERT_EQ(kCustomizeChromeColors.size(), colors.size());
- for (size_t i = 0; i < kCustomizeChromeColors.size(); i++) {
- auto palette = ui::GeneratePalette(
- kCustomizeChromeColors[i].color,
- ui::ColorProviderManager::SchemeVariant::kTonalSpot);
- EXPECT_EQ(l10n_util::GetStringUTF8(kCustomizeChromeColors[i].label_id),
- colors[i]->name);
- EXPECT_EQ(kCustomizeChromeColors[i].color, colors[i]->seed);
- EXPECT_EQ(palette->primary().get(80), colors[i]->background);
- EXPECT_EQ(palette->primary().get(30), colors[i]->foreground);
- EXPECT_EQ(palette->secondary().get(50), colors[i]->base);
- }
-
- // Test with Dark Mode off.
- handler().GetOverviewChromeColors(false, callback.Get());
-
- ASSERT_EQ(kCustomizeChromeColors.size(), colors.size());
- for (size_t i = 0; i < kCustomizeChromeColors.size(); i++) {
- auto palette = ui::GeneratePalette(
- kCustomizeChromeColors[i].color,
- ui::ColorProviderManager::SchemeVariant::kTonalSpot);
- EXPECT_EQ(l10n_util::GetStringUTF8(kCustomizeChromeColors[i].label_id),
- colors[i]->name);
- EXPECT_EQ(kCustomizeChromeColors[i].color, colors[i]->seed);
- EXPECT_EQ(palette->primary().get(40), colors[i]->background);
- EXPECT_EQ(palette->primary().get(90), colors[i]->foreground);
- EXPECT_EQ(palette->primary().get(80), colors[i]->base);
- }
-}
-
-TEST_F(CustomizeChromePageHandlerTest, GetChromeColors) {
- std::vector<side_panel::mojom::ChromeColorPtr> colors;
- base::MockCallback<CustomizeChromePageHandler::GetChromeColorsCallback>
- callback;
- EXPECT_CALL(callback, Run(testing::_))
- .Times(1)
- .WillOnce(testing::Invoke(
- [&colors](std::vector<side_panel::mojom::ChromeColorPtr> colors_arg) {
- colors = std::move(colors_arg);
- }));
- handler().GetChromeColors(callback.Get());
-
- auto num_colors = sizeof(chrome_colors::kGeneratedColorsInfo) /
- sizeof(chrome_colors::kGeneratedColorsInfo[0]);
- ASSERT_EQ(num_colors, colors.size());
- for (size_t i = 0; i < num_colors; i++) {
- EXPECT_EQ(l10n_util::GetStringUTF8(
- chrome_colors::kGeneratedColorsInfo[i].label_id),
- colors[i]->name);
- EXPECT_EQ(chrome_colors::kGeneratedColorsInfo[i].color, colors[i]->seed);
- EXPECT_EQ(GetAutogeneratedThemeColors(
- chrome_colors::kGeneratedColorsInfo[i].color)
- .active_tab_color,
- colors[i]->background);
- EXPECT_EQ(GetAutogeneratedThemeColors(
- chrome_colors::kGeneratedColorsInfo[i].color)
- .frame_color,
- colors[i]->foreground);
- }
-}
-
enum class ThemeUpdateSource {
kMojo,
kThemeService,
@@ -511,19 +399,18 @@ TEST_P(CustomizeChromePageHandlerSetThemeTest, SetTheme) {
custom_background.custom_background_url = GURL("https://foo.com/img.png");
custom_background.custom_background_attribution_line_1 = "foo line";
custom_background.is_uploaded_image = false;
- custom_background.custom_background_main_color = SK_ColorGREEN;
custom_background.collection_id = "test_collection";
custom_background.daily_refresh_enabled = false;
ON_CALL(mock_ntp_custom_background_service_, GetCustomBackground())
.WillByDefault(testing::Return(absl::make_optional(custom_background)));
- ON_CALL(mock_theme_service(), GetAutogeneratedThemeColor())
- .WillByDefault(testing::Return(SK_ColorBLUE));
+ ON_CALL(mock_theme_service(), GetUserColor())
+ .WillByDefault(testing::Return(absl::optional<SkColor>()));
ON_CALL(mock_theme_service(), UsingDefaultTheme())
.WillByDefault(testing::Return(false));
ON_CALL(mock_theme_service(), UsingSystemTheme())
.WillByDefault(testing::Return(false));
- ON_CALL(mock_theme_service(), UsingPolicyTheme())
- .WillByDefault(testing::Return(true));
+ ON_CALL(mock_theme_service(), UsingDeviceTheme())
+ .WillByDefault(testing::Return(false));
ON_CALL(mock_ntp_custom_background_service_,
IsCustomBackgroundDisabledByPolicy())
.WillByDefault(testing::Return(true));
@@ -537,76 +424,15 @@ TEST_P(CustomizeChromePageHandlerSetThemeTest, SetTheme) {
EXPECT_EQ("https://foo.com/img.png", theme->background_image->url);
EXPECT_FALSE(theme->background_image->is_uploaded_image);
EXPECT_FALSE(theme->background_image->daily_refresh_enabled);
- EXPECT_EQ(SK_ColorGREEN,
- theme->background_image->main_color.value_or(SK_ColorWHITE));
EXPECT_EQ("foo line", theme->background_image->title);
EXPECT_EQ("test_collection", theme->background_image->collection_id);
- EXPECT_TRUE(theme->is_dark_mode);
- EXPECT_EQ(SK_ColorBLUE, theme->seed_color);
EXPECT_EQ(
web_contents().GetColorProvider().GetColor(kColorNewTabPageBackground),
theme->background_color);
EXPECT_EQ(web_contents().GetColorProvider().GetColor(ui::kColorFrameActive),
theme->foreground_color);
- EXPECT_EQ(web_contents().GetColorProvider().GetColor(kColorNewTabPageText),
- theme->color_picker_icon_color);
- EXPECT_TRUE(theme->colors_managed_by_policy);
EXPECT_TRUE(theme->background_managed_by_policy);
-}
-
-TEST_P(CustomizeChromePageHandlerSetThemeTest, SetThemeColorSchemeGM3) {
- scoped_feature_list_.InitWithFeatures(
- {features::kChromeRefresh2023, features::kChromeWebuiRefresh2023}, {});
- side_panel::mojom::ThemePtr theme;
- EXPECT_CALL(mock_page_, SetTheme)
- .Times(2)
- .WillRepeatedly(
- testing::Invoke([&theme](side_panel::mojom::ThemePtr arg) {
- theme = std::move(arg);
- }));
-
- // Set mocks needed for UpdateTheme.
- CustomBackground custom_background;
- custom_background.custom_background_url = GURL("https://foo.com/img.png");
- custom_background.custom_background_attribution_line_1 = "foo line";
- custom_background.is_uploaded_image = false;
- custom_background.custom_background_main_color = SK_ColorGREEN;
- custom_background.collection_id = "test_collection";
- custom_background.daily_refresh_enabled = false;
- ON_CALL(mock_ntp_custom_background_service_, GetCustomBackground())
- .WillByDefault(testing::Return(absl::make_optional(custom_background)));
- ON_CALL(mock_theme_service(), GetAutogeneratedThemeColor())
- .WillByDefault(testing::Return(SK_ColorBLUE));
- ON_CALL(mock_theme_service(), UsingDefaultTheme())
- .WillByDefault(testing::Return(false));
- ON_CALL(mock_theme_service(), UsingSystemTheme())
- .WillByDefault(testing::Return(false));
- ON_CALL(mock_theme_service(), UsingPolicyTheme())
- .WillByDefault(testing::Return(true));
- ON_CALL(mock_ntp_custom_background_service_,
- IsCustomBackgroundDisabledByPolicy())
- .WillByDefault(testing::Return(true));
-
- // Set BrowserColorScheme to System and system to dark mode.
- ON_CALL(mock_theme_service(), GetBrowserColorScheme())
- .WillByDefault(
- testing::Return(ThemeService::BrowserColorScheme::kSystem));
- ui::NativeTheme::GetInstanceForNativeUi()->set_use_dark_colors(true);
-
- UpdateTheme();
- mock_page_.FlushForTesting();
-
- // // Theme should be dark to match the system.
- EXPECT_TRUE(theme->is_dark_mode);
-
- // Set BrowserColorScheme to Light and leave system still on dark mode.
- ON_CALL(mock_theme_service(), GetBrowserColorScheme())
- .WillByDefault(testing::Return(ThemeService::BrowserColorScheme::kLight));
-
- UpdateTheme();
- mock_page_.FlushForTesting();
-
- EXPECT_FALSE(theme->is_dark_mode);
+ EXPECT_FALSE(theme->follow_device_theme);
}
TEST_P(CustomizeChromePageHandlerSetThemeTest, SetThemeWithDailyRefresh) {
@@ -779,21 +605,11 @@ TEST_F(CustomizeChromePageHandlerTest, GetBackgroundImages) {
TEST_F(CustomizeChromePageHandlerTest, SetDefaultColor) {
EXPECT_CALL(mock_theme_service(), UseDefaultTheme).Times(1);
+ EXPECT_CALL(mock_theme_service(), UseDeviceTheme(false)).Times(1);
handler().SetDefaultColor();
}
-TEST_F(CustomizeChromePageHandlerTest, SetSeedColor) {
- SkColor color = SK_ColorWHITE;
- EXPECT_CALL(mock_theme_service(), BuildAutogeneratedThemeFromColor)
- .Times(1)
- .WillOnce(testing::SaveArg<0>(&color));
-
- handler().SetSeedColor(SK_ColorBLUE);
-
- EXPECT_EQ(SK_ColorBLUE, color);
-}
-
TEST_F(CustomizeChromePageHandlerTest, RemoveBackgroundImage) {
EXPECT_CALL(mock_ntp_custom_background_service_, ResetCustomBackgroundInfo)
.Times(1);
@@ -875,6 +691,18 @@ TEST_F(CustomizeChromePageHandlerTest, SetDailyRefreshCollectionId) {
handler().SetDailyRefreshCollectionId("test_id");
}
+TEST_F(CustomizeChromePageHandlerTest, SetFollowDeviceTheme_On) {
+ EXPECT_CALL(mock_theme_service(), UseDeviceTheme(true)).Times(1);
+
+ handler().SetFollowDeviceTheme(true);
+}
+
+TEST_F(CustomizeChromePageHandlerTest, SetUseDeviceTheme_Off) {
+ EXPECT_CALL(mock_theme_service(), UseDeviceTheme(false)).Times(1);
+
+ handler().SetFollowDeviceTheme(false);
+}
+
TEST_F(CustomizeChromePageHandlerTest, ScrollToSection) {
side_panel::mojom::CustomizeChromeSection section;
EXPECT_CALL(mock_page_, ScrollToSection)
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
index 1dc25f937ab..e037df1054f 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
@@ -7,6 +7,7 @@
#include <string>
#include <utility>
+#include "chrome/browser/browser_features.h"
#include "chrome/browser/cart/cart_handler.h"
#include "chrome/browser/new_tab_page/modules/new_tab_page_modules.h"
#include "chrome/browser/new_tab_page/new_tab_page_util.h"
@@ -14,6 +15,7 @@
#include "chrome/browser/search/background/ntp_custom_background_service_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/webui/cr_components/customize_color_scheme_mode/customize_color_scheme_mode_handler.h"
+#include "chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h"
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
#include "chrome/browser/ui/webui/sanitized_image_source.h"
#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h"
@@ -73,13 +75,17 @@ CustomizeChromeUI::CustomizeChromeUI(content::WebUI* web_ui)
{"colorPickerLabel", IDS_NTP_CUSTOMIZE_COLOR_PICKER_LABEL},
{"currentTheme", IDS_NTP_CUSTOMIZE_CHROME_CURRENT_THEME_LABEL},
{"defaultColorName", IDS_NTP_CUSTOMIZE_DEFAULT_LABEL},
+ {"greyDefaultColorName", IDS_NTP_CUSTOMIZE_GREY_DEFAULT_LABEL},
+ {"hueSliderTitle", IDS_NTP_CUSTOMIZE_COLOR_HUE_SLIDER_TITLE},
{"mainColorName", IDS_NTP_CUSTOMIZE_MAIN_COLOR_LABEL},
{"managedColorsTitle", IDS_NTP_THEME_MANAGED_DIALOG_TITLE},
{"managedColorsBody", IDS_NTP_THEME_MANAGED_DIALOG_BODY},
{"uploadImage", IDS_NTP_CUSTOM_BG_UPLOAD_AN_IMAGE},
{"uploadedImage", IDS_NTP_CUSTOMIZE_UPLOADED_IMAGE_LABEL},
+ {"yourUploadedImage", IDS_NTP_CUSTOMIZE_YOUR_UPLOADED_IMAGE_LABEL},
{"resetToClassicChrome",
IDS_NTP_CUSTOMIZE_CHROME_RESET_TO_CLASSIC_CHROME_LABEL},
+ {"followThemeToggle", IDS_NTP_CUSTOMIZE_CHROME_FOLLOW_THEME_LABEL},
{"refreshDaily", IDS_NTP_CUSTOM_BG_DAILY_REFRESH},
// Shortcut strings.
{"mostVisited", IDS_NTP_CUSTOMIZE_MOST_VISITED_LABEL},
@@ -114,6 +120,25 @@ CustomizeChromeUI::CustomizeChromeUI(content::WebUI* web_ui)
IsCartModuleEnabled() &&
base::FeatureList::IsEnabled(
ntp_features::kNtpChromeCartInHistoryClusterModule));
+
+ source->AddBoolean("showDeviceThemeToggle",
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
+ features::IsChromeWebuiRefresh2023());
+#else
+ false);
+#endif
+
+ source->AddBoolean(
+ "extensionsCardEnabled",
+ base::FeatureList::IsEnabled(
+ ntp_features::kCustomizeChromeSidePanelExtensionsCard) &&
+ features::IsChromeWebuiRefresh2023());
+
+ source->AddBoolean("wallpaperSearchEnabled",
+ base::FeatureList::IsEnabled(
+ ntp_features::kCustomizeChromeWallpaperSearch) &&
+ base::FeatureList::IsEnabled(features::kMantaService));
+
webui::SetupChromeRefresh2023(source);
webui::SetupWebUIDataSource(
@@ -181,6 +206,17 @@ void CustomizeChromeUI::BindInterface(
}
void CustomizeChromeUI::BindInterface(
+ mojo::PendingReceiver<
+ theme_color_picker::mojom::ThemeColorPickerHandlerFactory>
+ pending_receiver) {
+ if (theme_color_picker_handler_factory_receiver_.is_bound()) {
+ theme_color_picker_handler_factory_receiver_.reset();
+ }
+ theme_color_picker_handler_factory_receiver_.Bind(
+ std::move(pending_receiver));
+}
+
+void CustomizeChromeUI::BindInterface(
mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
pending_receiver) {
color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
@@ -226,3 +262,14 @@ void CustomizeChromeUI::CreateCustomizeColorSchemeModeHandler(
std::make_unique<CustomizeColorSchemeModeHandler>(
std::move(client), std::move(handler), profile_);
}
+
+void CustomizeChromeUI::CreateThemeColorPickerHandler(
+ mojo::PendingReceiver<theme_color_picker::mojom::ThemeColorPickerHandler>
+ handler,
+ mojo::PendingRemote<theme_color_picker::mojom::ThemeColorPickerClient>
+ client) {
+ theme_color_picker_handler_ = std::make_unique<ThemeColorPickerHandler>(
+ std::move(handler), std::move(client),
+ NtpCustomBackgroundServiceFactory::GetForProfile(profile_),
+ web_contents_);
+}
diff --git a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
index bcbd7e8c64a..3f8bcc10435 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
+++ b/chromium/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
@@ -21,6 +21,7 @@
#include "ui/webui/mojo_bubble_web_ui_controller.h"
#include "ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom.h"
#include "ui/webui/resources/cr_components/help_bubble/help_bubble.mojom.h"
+#include "ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.mojom.h"
namespace content {
class WebContents;
@@ -31,6 +32,7 @@ class CartHandler;
class Profile;
class HelpBubbleHandler;
class CustomizeColorSchemeModeHandler;
+class ThemeColorPickerHandler;
namespace ui {
class ColorChangeHandler;
@@ -42,6 +44,7 @@ class CustomizeChromeUI
public help_bubble::mojom::HelpBubbleHandlerFactory,
public customize_color_scheme_mode::mojom::
CustomizeColorSchemeModeHandlerFactory,
+ public theme_color_picker::mojom::ThemeColorPickerHandlerFactory,
public side_panel::mojom::CustomizeChromePageHandlerFactory {
public:
explicit CustomizeChromeUI(content::WebUI* web_ui);
@@ -86,6 +89,10 @@ class CustomizeChromeUI
CustomizeColorSchemeModeHandlerFactory>
pending_receiver);
+ void BindInterface(mojo::PendingReceiver<
+ theme_color_picker::mojom::ThemeColorPickerHandlerFactory>
+ pending_receiver);
+
private:
// side_panel::mojom::CustomizeChromePageHandlerFactory
void CreatePageHandler(
@@ -108,6 +115,13 @@ class CustomizeChromeUI
customize_color_scheme_mode::mojom::CustomizeColorSchemeModeHandler>
handler) override;
+ // theme_color_picker::mojom::ThemeColorPickerHandlerFactory:
+ void CreateThemeColorPickerHandler(
+ mojo::PendingReceiver<theme_color_picker::mojom::ThemeColorPickerHandler>
+ handler,
+ mojo::PendingRemote<theme_color_picker::mojom::ThemeColorPickerClient>
+ client) override;
+
std::unique_ptr<CustomizeChromePageHandler> customize_chrome_page_handler_;
std::unique_ptr<CartHandler> cart_handler_;
raw_ptr<Profile> profile_;
@@ -128,6 +142,9 @@ class CustomizeChromeUI
mojo::Receiver<customize_color_scheme_mode::mojom::
CustomizeColorSchemeModeHandlerFactory>
customize_color_scheme_mode_handler_factory_receiver_{this};
+ std::unique_ptr<ThemeColorPickerHandler> theme_color_picker_handler_;
+ mojo::Receiver<theme_color_picker::mojom::ThemeColorPickerHandlerFactory>
+ theme_color_picker_handler_factory_receiver_{this};
base::WeakPtrFactory<CustomizeChromeUI> weak_ptr_factory_{this};
diff --git a/chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc b/chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc
index 88edf3a337a..c64e8a05a30 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.cc
@@ -17,6 +17,8 @@
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/side_panel_history_clusters_resources.h"
#include "chrome/grit/side_panel_history_clusters_resources_map.h"
+#include "chrome/grit/side_panel_shared_resources.h"
+#include "chrome/grit/side_panel_shared_resources_map.h"
#include "components/favicon_base/favicon_url_parser.h"
#include "components/page_image_service/image_service.h"
#include "components/page_image_service/image_service_handler.h"
@@ -24,6 +26,7 @@
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
+#include "ui/webui/color_change_listener/color_change_handler.h"
HistoryClustersSidePanelUI::HistoryClustersSidePanelUI(content::WebUI* web_ui)
: ui::MojoBubbleWebUIController(web_ui),
@@ -43,6 +46,8 @@ HistoryClustersSidePanelUI::HistoryClustersSidePanelUI(content::WebUI* web_ui)
base::make_span(kSidePanelHistoryClustersResources,
kSidePanelHistoryClustersResourcesSize),
IDR_SIDE_PANEL_HISTORY_CLUSTERS_HISTORY_CLUSTERS_HTML);
+ source->AddResourcePaths(base::make_span(kSidePanelSharedResources,
+ kSidePanelSharedResourcesSize));
}
HistoryClustersSidePanelUI::~HistoryClustersSidePanelUI() = default;
@@ -50,6 +55,13 @@ HistoryClustersSidePanelUI::~HistoryClustersSidePanelUI() = default;
WEB_UI_CONTROLLER_TYPE_IMPL(HistoryClustersSidePanelUI)
void HistoryClustersSidePanelUI::BindInterface(
+ mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+ pending_receiver) {
+ color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
+ web_ui()->GetWebContents(), std::move(pending_receiver));
+}
+
+void HistoryClustersSidePanelUI::BindInterface(
mojo::PendingReceiver<history_clusters::mojom::PageHandler>
pending_page_handler) {
history_clusters_handler_ =
diff --git a/chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h b/chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h
index ce50b4ea827..6f205d24f61 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h
+++ b/chromium/chrome/browser/ui/webui/side_panel/history_clusters/history_clusters_side_panel_ui.h
@@ -13,8 +13,13 @@
#include "content/public/browser/web_contents_observer.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "ui/webui/mojo_bubble_web_ui_controller.h"
+#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
#include "ui/webui/resources/cr_components/history_clusters/history_clusters.mojom-forward.h"
+namespace ui {
+class ColorChangeHandler;
+}
+
namespace history_clusters {
class HistoryClustersHandler;
}
@@ -32,6 +37,10 @@ class HistoryClustersSidePanelUI : public ui::MojoBubbleWebUIController,
delete;
~HistoryClustersSidePanelUI() override;
+ void BindInterface(
+ mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+ pending_receiver);
+
// Instantiates the implementor of the mojom::PageHandlerFactory mojo
// interface passing the pending receiver that will be internally bound.
void BindInterface(mojo::PendingReceiver<history_clusters::mojom::PageHandler>
@@ -62,6 +71,7 @@ class HistoryClustersSidePanelUI : public ui::MojoBubbleWebUIController,
content::NavigationHandle* navigation_handle) override;
private:
+ std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
std::unique_ptr<history_clusters::HistoryClustersHandler>
history_clusters_handler_;
std::unique_ptr<page_image_service::ImageServiceHandler>
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/DEPS b/chromium/chrome/browser/ui/webui/side_panel/read_anything/DEPS
index 0d08619c1aa..626639e46b5 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/read_anything/DEPS
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/DEPS
@@ -3,7 +3,7 @@ include_rules = [
]
specific_include_rules = {
- "read_anything_page_handler\.*": [
+ "read_anything_untrusted_page_handler\.*": [
"+chrome/browser/ui/views/side_panel/read_anything",
"+chrome/browser/ui/views/frame/browser_view.h",
],
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_browsertest.cc b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_browsertest.cc
index 3e6bbf65a7e..efad2f248cb 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_browsertest.cc
@@ -211,3 +211,9 @@ IN_PROC_BROWSER_TEST_F(ReadAnythingAppTest, UpdateContent_NoContentNodes) {
IN_PROC_BROWSER_TEST_F(ReadAnythingAppTest, UpdateContent_InteractiveElement) {
ASSERT_TRUE(RunTest("update_content_interactive_element.js"));
}
+
+// TODO(crbug.com/1474951): Remove this test once the ReadAnythingWebUiToolbar
+// flag is removed.
+IN_PROC_BROWSER_TEST_F(ReadAnythingAppTest, ReadAnythingToolbar_Hidden) {
+ ASSERT_TRUE(RunTest("toolbar_hidden_without_flag.js"));
+}
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_read_aloud_browsertest.cc b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_read_aloud_browsertest.cc
new file mode 100644
index 00000000000..0aafb832e2e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_read_aloud_browsertest.cc
@@ -0,0 +1,75 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Test chrome/browser/resources/side_panel/read_anything/app.ts here. Add a new
+// test script to chrome/test/data/webui/side_panel/read_anything and add a new
+// pass the file name to RunTest in this file.
+
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/browser_test.h"
+#include "ui/accessibility/accessibility_features.h"
+
+class ReadAnythingAppReadAloudTest : public InProcessBrowserTest {
+ public:
+ ReadAnythingAppReadAloudTest() {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kReadAnything, features::kReadAnythingWebUIToolbar,
+ features::kReadAnythingReadAloud},
+ {});
+ }
+ ~ReadAnythingAppReadAloudTest() override = default;
+ ReadAnythingAppReadAloudTest(const ReadAnythingAppReadAloudTest&) = delete;
+ ReadAnythingAppReadAloudTest& operator=(const ReadAnythingAppReadAloudTest&) =
+ delete;
+
+ testing::AssertionResult RunTest(const char* name) {
+ std::string script;
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ // Tests are located in
+ // chrome/test/data/webui/side_panel/read_anything/$(name).
+ base::FilePath path;
+ base::PathService::Get(chrome::DIR_TEST_DATA, &path);
+ path = path.AppendASCII("webui")
+ .AppendASCII("side_panel")
+ .AppendASCII("read_anything")
+ .AppendASCII(name);
+
+ // Read the test.
+ if (!base::PathExists(path)) {
+ return testing::AssertionFailure() << "Couldn't find " << path.value();
+ }
+ base::ReadFileToString(path, &script);
+ script = "'use strict';" + script;
+ }
+
+ // Run the test.
+ EXPECT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), GURL(chrome::kChromeUIUntrustedReadAnythingSidePanelURL)));
+ content::RenderFrameHost* webui = browser()
+ ->tab_strip_model()
+ ->GetActiveWebContents()
+ ->GetPrimaryMainFrame();
+ if (!webui) {
+ return testing::AssertionFailure() << "Failed to navigate to WebUI";
+ }
+
+ bool result = content::EvalJs(webui, script).ExtractBool();
+ return result ? testing::AssertionSuccess()
+ : (testing::AssertionFailure() << "Check console output");
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(ReadAnythingAppReadAloudTest, ReadAloud_Visible) {
+ ASSERT_TRUE(RunTest("read_aloud_visible_with_flag.js"));
+}
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc
new file mode 100644
index 00000000000..33c3e378686
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc
@@ -0,0 +1,79 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Test chrome/browser/resources/side_panel/read_anything/app.ts here. Add a new
+// test script to chrome/test/data/webui/side_panel/read_anything and add a new
+// pass the file name to RunTest in this file.
+
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/browser_test.h"
+#include "ui/accessibility/accessibility_features.h"
+
+class ReadAnythingAppToolbarTest : public InProcessBrowserTest {
+ public:
+ ReadAnythingAppToolbarTest() {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kReadAnything, features::kReadAnythingWebUIToolbar}, {});
+ }
+ ~ReadAnythingAppToolbarTest() override = default;
+ ReadAnythingAppToolbarTest(const ReadAnythingAppToolbarTest&) = delete;
+ ReadAnythingAppToolbarTest& operator=(const ReadAnythingAppToolbarTest&) =
+ delete;
+
+ testing::AssertionResult RunTest(const char* name) {
+ std::string script;
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ // Tests are located in
+ // chrome/test/data/webui/side_panel/read_anything/$(name).
+ base::FilePath path;
+ base::PathService::Get(chrome::DIR_TEST_DATA, &path);
+ path = path.AppendASCII("webui")
+ .AppendASCII("side_panel")
+ .AppendASCII("read_anything")
+ .AppendASCII(name);
+
+ // Read the test.
+ if (!base::PathExists(path)) {
+ return testing::AssertionFailure() << "Couldn't find " << path.value();
+ }
+ base::ReadFileToString(path, &script);
+ script = "'use strict';" + script;
+ }
+
+ // Run the test.
+ EXPECT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), GURL(chrome::kChromeUIUntrustedReadAnythingSidePanelURL)));
+ content::RenderFrameHost* webui = browser()
+ ->tab_strip_model()
+ ->GetActiveWebContents()
+ ->GetPrimaryMainFrame();
+ if (!webui) {
+ return testing::AssertionFailure() << "Failed to navigate to WebUI";
+ }
+
+ bool result = content::EvalJs(webui, script).ExtractBool();
+ return result ? testing::AssertionSuccess()
+ : (testing::AssertionFailure() << "Check console output");
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(ReadAnythingAppToolbarTest,
+ ReadAnythingToolbar_Visible) {
+ ASSERT_TRUE(RunTest("toolbar_visible_with_flag.js"));
+}
+
+// TODO(crbug.com/1474951): Remove this test once Read Aloud flag is removed.
+IN_PROC_BROWSER_TEST_F(ReadAnythingAppToolbarTest, ReadAloud_Hidden) {
+ ASSERT_TRUE(RunTest("toolbar_without_flag_hides_read_aloud.js"));
+}
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.cc b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.cc
index 5b66ee13d3f..29ffbbefa95 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.cc
@@ -10,33 +10,10 @@
#if !BUILDFLAG(IS_ANDROID)
-namespace prefs {
-// String to represent the user's preferred font name for the read anything UI.
-const char kAccessibilityReadAnythingFontName[] =
- "settings.a11y.read_anything.font_name";
-
-// Double to represent the user's preferred font size scaling factor.
-const char kAccessibilityReadAnythingFontScale[] =
- "settings.a11y.read_anything.font_scale";
-
-// Int value to represent the user's preferred color settings.
-const char kAccessibilityReadAnythingColorInfo[] =
- "settings.a11y.read_anything.color_info";
-
-// Int value to represent the user's preferred line spacing setting.
-const char kAccessibilityReadAnythingLineSpacing[] =
- "settings.a11y.read_anything.line_spacing";
-
-// Int value to represent the user's preferred letter spacing setting.
-const char kAccessibilityReadAnythingLetterSpacing[] =
- "settings.a11y.read_anything.letter_spacing";
-
-} // namespace prefs
-
void RegisterReadAnythingProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterStringPref(prefs::kAccessibilityReadAnythingFontName,
- string_constants::kReadAnythingDefaultFontName,
+ string_constants::kReadAnythingPlaceholderFontName,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterDoublePref(prefs::kAccessibilityReadAnythingFontScale,
kReadAnythingDefaultFontScale,
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.h b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.h
index d7272d121bd..0aa652c525a 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.h
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.h
@@ -16,11 +16,25 @@ class PrefRegistrySyncable;
namespace prefs {
-extern const char kAccessibilityReadAnythingFontName[];
-extern const char kAccessibilityReadAnythingFontScale[];
-extern const char kAccessibilityReadAnythingColorInfo[];
-extern const char kAccessibilityReadAnythingLineSpacing[];
-extern const char kAccessibilityReadAnythingLetterSpacing[];
+// String to represent the user's preferred font name for the read anything UI.
+inline constexpr char kAccessibilityReadAnythingFontName[] =
+ "settings.a11y.read_anything.font_name";
+
+// Double to represent the user's preferred font size scaling factor.
+inline constexpr char kAccessibilityReadAnythingFontScale[] =
+ "settings.a11y.read_anything.font_scale";
+
+// Int value to represent the user's preferred color settings.
+inline constexpr char kAccessibilityReadAnythingColorInfo[] =
+ "settings.a11y.read_anything.color_info";
+
+// Int value to represent the user's preferred line spacing setting.
+inline constexpr char kAccessibilityReadAnythingLineSpacing[] =
+ "settings.a11y.read_anything.line_spacing";
+
+// Int value to represent the user's preferred letter spacing setting.
+inline constexpr char kAccessibilityReadAnythingLetterSpacing[] =
+ "settings.a11y.read_anything.letter_spacing";
} // namespace prefs
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.cc b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
index c1dbcccb786..0565f5c038f 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
@@ -2,16 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.h"
+#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h"
+#include <algorithm>
#include <string>
#include <utility>
#include <vector>
+#include "base/metrics/histogram_functions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h"
+#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.h"
+#include "chrome/common/accessibility/read_anything_constants.h"
+#include "components/prefs/pref_service.h"
#include "content/public/browser/web_ui.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/ax_action_data.h"
@@ -27,7 +32,18 @@ using read_anything::mojom::ReadAnythingTheme;
using read_anything::mojom::UntrustedPage;
using read_anything::mojom::UntrustedPageHandler;
-ReadAnythingPageHandler::ReadAnythingPageHandler(
+namespace {
+
+int GetNormalizedFontScale(double font_scale) {
+ DCHECK(font_scale >= kReadAnythingMinimumFontScale &&
+ font_scale <= kReadAnythingMaximumFontScale);
+ return (font_scale - kReadAnythingMinimumFontScale) *
+ (1 / kReadAnythingFontScaleIncrement);
+}
+
+} // namespace
+
+ReadAnythingUntrustedPageHandler::ReadAnythingUntrustedPageHandler(
mojo::PendingRemote<UntrustedPage> page,
mojo::PendingReceiver<UntrustedPageHandler> receiver,
content::WebUI* web_ui)
@@ -46,6 +62,19 @@ ReadAnythingPageHandler::ReadAnythingPageHandler(
coordinator_->AddModelObserver(this);
}
+ if (features::IsReadAnythingWebUIToolbarEnabled()) {
+ PrefService* prefs = browser_->profile()->GetPrefs();
+ page_->OnSettingsRestoredFromPrefs(
+ static_cast<read_anything::mojom::LineSpacing>(
+ prefs->GetInteger(prefs::kAccessibilityReadAnythingLineSpacing)),
+ static_cast<read_anything::mojom::LetterSpacing>(
+ prefs->GetInteger(prefs::kAccessibilityReadAnythingLetterSpacing)),
+ prefs->GetString(prefs::kAccessibilityReadAnythingFontName),
+ prefs->GetDouble(prefs::kAccessibilityReadAnythingFontScale),
+ static_cast<read_anything::mojom::Colors>(
+ prefs->GetInteger(prefs::kAccessibilityReadAnythingColorInfo)));
+ }
+
#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
if (features::IsReadAnythingWithScreen2xEnabled()) {
if (screen_ai::ScreenAIInstallState::GetInstance()->get_state() ==
@@ -62,12 +91,14 @@ ReadAnythingPageHandler::ReadAnythingPageHandler(
OnActiveWebContentsChanged();
}
-ReadAnythingPageHandler::~ReadAnythingPageHandler() {
+ReadAnythingUntrustedPageHandler::~ReadAnythingUntrustedPageHandler() {
TabStripModelObserver::StopObservingAll(this);
Observe(nullptr);
+ LogTextStyle();
- if (!coordinator_)
+ if (!coordinator_) {
return;
+ }
// If |this| is destroyed before the |ReadAnythingCoordinator|, then remove
// |this| from the observer lists. In the cases where the coordinator is
@@ -80,7 +111,7 @@ ReadAnythingPageHandler::~ReadAnythingPageHandler() {
// ui::AXActionHandlerObserver:
///////////////////////////////////////////////////////////////////////////////
-void ReadAnythingPageHandler::TreeRemoved(ui::AXTreeID ax_tree_id) {
+void ReadAnythingUntrustedPageHandler::TreeRemoved(ui::AXTreeID ax_tree_id) {
page_->OnAXTreeDestroyed(ax_tree_id);
}
@@ -88,12 +119,41 @@ void ReadAnythingPageHandler::TreeRemoved(ui::AXTreeID ax_tree_id) {
// read_anything::mojom::UntrustedPageHandler:
///////////////////////////////////////////////////////////////////////////////
-void ReadAnythingPageHandler::OnCopy() {
+void ReadAnythingUntrustedPageHandler::OnCopy() {
web_contents()->Copy();
}
-void ReadAnythingPageHandler::OnLinkClicked(const ui::AXTreeID& target_tree_id,
- ui::AXNodeID target_node_id) {
+void ReadAnythingUntrustedPageHandler::OnLineSpaceChange(
+ read_anything::mojom::LineSpacing line_spacing) {
+ browser_->profile()->GetPrefs()->SetInteger(
+ prefs::kAccessibilityReadAnythingLineSpacing,
+ static_cast<size_t>(line_spacing));
+}
+
+void ReadAnythingUntrustedPageHandler::OnLetterSpaceChange(
+ read_anything::mojom::LetterSpacing letter_spacing) {
+ browser_->profile()->GetPrefs()->SetInteger(
+ prefs::kAccessibilityReadAnythingLetterSpacing,
+ static_cast<size_t>(letter_spacing));
+}
+void ReadAnythingUntrustedPageHandler::OnFontChange(const std::string& font) {
+ browser_->profile()->GetPrefs()->SetString(
+ prefs::kAccessibilityReadAnythingFontName, font);
+}
+void ReadAnythingUntrustedPageHandler::OnFontSizeChange(double font_size) {
+ double saved_font_size = std::min(font_size, kReadAnythingMaximumFontScale);
+ browser_->profile()->GetPrefs()->SetDouble(
+ prefs::kAccessibilityReadAnythingFontScale, saved_font_size);
+}
+void ReadAnythingUntrustedPageHandler::OnColorChange(
+ read_anything::mojom::Colors color) {
+ browser_->profile()->GetPrefs()->SetInteger(
+ prefs::kAccessibilityReadAnythingColorInfo, static_cast<size_t>(color));
+}
+
+void ReadAnythingUntrustedPageHandler::OnLinkClicked(
+ const ui::AXTreeID& target_tree_id,
+ ui::AXNodeID target_node_id) {
ui::AXActionData action_data;
action_data.target_tree_id = target_tree_id;
action_data.action = ax::mojom::Action::kDoDefault;
@@ -107,7 +167,7 @@ void ReadAnythingPageHandler::OnLinkClicked(const ui::AXTreeID& target_tree_id,
handler->PerformAction(action_data);
}
-void ReadAnythingPageHandler::OnSelectionChange(
+void ReadAnythingUntrustedPageHandler::OnSelectionChange(
const ui::AXTreeID& target_tree_id,
ui::AXNodeID anchor_node_id,
int anchor_offset,
@@ -129,11 +189,15 @@ void ReadAnythingPageHandler::OnSelectionChange(
handler->PerformAction(action_data);
}
+void ReadAnythingUntrustedPageHandler::OnCollapseSelection() {
+ web_contents()->CollapseSelection();
+}
+
///////////////////////////////////////////////////////////////////////////////
// ReadAnythingModel::Observer:
///////////////////////////////////////////////////////////////////////////////
-void ReadAnythingPageHandler::OnReadAnythingThemeChanged(
+void ReadAnythingUntrustedPageHandler::OnReadAnythingThemeChanged(
const std::string& font_name,
double font_scale,
ui::ColorId foreground_color_id,
@@ -162,12 +226,12 @@ void ReadAnythingPageHandler::OnReadAnythingThemeChanged(
// ReadAnythingCoordinator::Observer:
///////////////////////////////////////////////////////////////////////////////
-void ReadAnythingPageHandler::Activate(bool active) {
+void ReadAnythingUntrustedPageHandler::Activate(bool active) {
active_ = active;
OnActiveWebContentsChanged();
}
-void ReadAnythingPageHandler::OnCoordinatorDestroyed() {
+void ReadAnythingUntrustedPageHandler::OnCoordinatorDestroyed() {
coordinator_ = nullptr;
}
@@ -176,7 +240,7 @@ void ReadAnythingPageHandler::OnCoordinatorDestroyed() {
///////////////////////////////////////////////////////////////////////////////
#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
-void ReadAnythingPageHandler::StateChanged(
+void ReadAnythingUntrustedPageHandler::StateChanged(
screen_ai::ScreenAIInstallState::State state) {
DCHECK(features::IsReadAnythingWithScreen2xEnabled());
// If Screen AI library is downloaded but not initialized yet, ensure it is
@@ -197,7 +261,7 @@ void ReadAnythingPageHandler::StateChanged(
// TabStripModelObserver:
///////////////////////////////////////////////////////////////////////////////
-void ReadAnythingPageHandler::OnTabStripModelChanged(
+void ReadAnythingUntrustedPageHandler::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
@@ -206,7 +270,7 @@ void ReadAnythingPageHandler::OnTabStripModelChanged(
}
}
-void ReadAnythingPageHandler::OnTabStripModelDestroyed(
+void ReadAnythingUntrustedPageHandler::OnTabStripModelDestroyed(
TabStripModel* tab_strip_model) {
// If the TabStripModel is destroyed before |this|, remove |this| as an
// observer.
@@ -218,11 +282,11 @@ void ReadAnythingPageHandler::OnTabStripModelDestroyed(
// content::WebContentsObserver:
///////////////////////////////////////////////////////////////////////////////
-void ReadAnythingPageHandler::PrimaryPageChanged(content::Page& page) {
+void ReadAnythingUntrustedPageHandler::PrimaryPageChanged(content::Page& page) {
OnActiveAXTreeIDChanged();
}
-void ReadAnythingPageHandler::AccessibilityEventReceived(
+void ReadAnythingUntrustedPageHandler::AccessibilityEventReceived(
const content::AXEventNotificationDetails& details) {
page_->AccessibilityEventReceived(details.ax_tree_id, details.updates,
details.events);
@@ -230,7 +294,7 @@ void ReadAnythingPageHandler::AccessibilityEventReceived(
///////////////////////////////////////////////////////////////////////////////
-void ReadAnythingPageHandler::OnActiveWebContentsChanged() {
+void ReadAnythingUntrustedPageHandler::OnActiveWebContentsChanged() {
// TODO(crbug.com/1266555): Disable accessibility.and stop observing events
// on the now inactive tab. But make sure that we don't disable it for
// assistive technology users. Some options here are:
@@ -255,7 +319,7 @@ void ReadAnythingPageHandler::OnActiveWebContentsChanged() {
OnActiveAXTreeIDChanged();
}
-void ReadAnythingPageHandler::OnActiveAXTreeIDChanged() {
+void ReadAnythingUntrustedPageHandler::OnActiveAXTreeIDChanged() {
ui::AXTreeID tree_id = ui::AXTreeIDUnknown();
ukm::SourceId ukm_source_id = ukm::kInvalidSourceId;
GURL visible_url;
@@ -271,3 +335,33 @@ void ReadAnythingPageHandler::OnActiveAXTreeIDChanged() {
page_->OnActiveAXTreeIDChanged(tree_id, ukm_source_id, visible_url);
}
+
+void ReadAnythingUntrustedPageHandler::LogTextStyle() {
+ // This is called when the side panel closes, so retrieving the values from
+ // preferences won't happen very often.
+ PrefService* prefs = browser_->profile()->GetPrefs();
+ int maximum_font_scale_logging =
+ GetNormalizedFontScale(kReadAnythingMaximumFontScale);
+ double font_scale =
+ prefs->GetDouble(prefs::kAccessibilityReadAnythingFontScale);
+ base::UmaHistogramExactLinear(string_constants::kFontScaleHistogramName,
+ GetNormalizedFontScale(font_scale),
+ maximum_font_scale_logging + 1);
+ ReadAnythingFont font =
+ font_map_.at(prefs->GetString(prefs::kAccessibilityReadAnythingFontName));
+ base::UmaHistogramEnumeration(string_constants::kFontNameHistogramName, font);
+ read_anything::mojom::Colors color =
+ static_cast<read_anything::mojom::Colors>(
+ prefs->GetInteger(prefs::kAccessibilityReadAnythingColorInfo));
+ base::UmaHistogramEnumeration(string_constants::kColorHistogramName, color);
+ read_anything::mojom::LineSpacing line_spacing =
+ static_cast<read_anything::mojom::LineSpacing>(
+ prefs->GetInteger(prefs::kAccessibilityReadAnythingLineSpacing));
+ base::UmaHistogramEnumeration(string_constants::kLineSpacingHistogramName,
+ line_spacing);
+ read_anything::mojom::LetterSpacing letter_spacing =
+ static_cast<read_anything::mojom::LetterSpacing>(
+ prefs->GetInteger(prefs::kAccessibilityReadAnythingLetterSpacing));
+ base::UmaHistogramEnumeration(string_constants::kLetterSpacingHistogramName,
+ letter_spacing);
+}
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.h b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
index 610a25bda6c..93f0a3f8734 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.h
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_PAGE_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_PAGE_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_UNTRUSTED_PAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_UNTRUSTED_PAGE_HANDLER_H_
#include <string>
@@ -25,14 +25,14 @@
#endif
///////////////////////////////////////////////////////////////////////////////
-// ReadAnythingPageHandler
+// ReadAnythingUntrustedPageHandler
//
// A handler of the Read Anything app
// (chrome/browser/resources/side_panel/read_anything/app.ts).
-// This class is created and owned by ReadAnythingUI and has the same lifetime
-// as the Side Panel view.
+// This class is created and owned by ReadAnythingUntrustedUI and has the same
+// lifetime as the Side Panel view.
//
-class ReadAnythingPageHandler
+class ReadAnythingUntrustedPageHandler
: public ui::AXActionHandlerObserver,
public read_anything::mojom::UntrustedPageHandler,
public ReadAnythingModel::Observer,
@@ -43,14 +43,16 @@ class ReadAnythingPageHandler
public TabStripModelObserver,
public content::WebContentsObserver {
public:
- ReadAnythingPageHandler(
+ ReadAnythingUntrustedPageHandler(
mojo::PendingRemote<read_anything::mojom::UntrustedPage> page,
mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler>
receiver,
content::WebUI* web_ui);
- ReadAnythingPageHandler(const ReadAnythingPageHandler&) = delete;
- ReadAnythingPageHandler& operator=(const ReadAnythingPageHandler&) = delete;
- ~ReadAnythingPageHandler() override;
+ ReadAnythingUntrustedPageHandler(const ReadAnythingUntrustedPageHandler&) =
+ delete;
+ ReadAnythingUntrustedPageHandler& operator=(
+ const ReadAnythingUntrustedPageHandler&) = delete;
+ ~ReadAnythingUntrustedPageHandler() override;
private:
// ui::AXActionHandlerObserver:
@@ -58,6 +60,13 @@ class ReadAnythingPageHandler
// read_anything::mojom::UntrustedPageHandler:
void OnCopy() override;
+ void OnLineSpaceChange(
+ read_anything::mojom::LineSpacing line_spacing) override;
+ void OnLetterSpaceChange(
+ read_anything::mojom::LetterSpacing letter_spacing) override;
+ void OnFontChange(const std::string& font) override;
+ void OnFontSizeChange(double font_size) override;
+ void OnColorChange(read_anything::mojom::Colors color) override;
void OnLinkClicked(const ui::AXTreeID& target_tree_id,
ui::AXNodeID target_node_id) override;
void OnSelectionChange(const ui::AXTreeID& target_tree_id,
@@ -65,6 +74,7 @@ class ReadAnythingPageHandler
int anchor_offset,
ui::AXNodeID focus_node_id,
int focus_offset) override;
+ void OnCollapseSelection() override;
// ReadAnythingModel::Observer:
void OnReadAnythingThemeChanged(
@@ -107,9 +117,21 @@ class ReadAnythingPageHandler
// Notifies the model that the AXTreeID has changed.
void OnActiveAXTreeIDChanged();
+ // Logs the current visual settings values.
+ void LogTextStyle();
+
raw_ptr<ReadAnythingCoordinator> coordinator_;
const raw_ptr<Browser> browser_;
const raw_ptr<content::WebUI> web_ui_;
+ const std::map<std::string, ReadAnythingFont> font_map_ = {
+ {"Poppins", ReadAnythingFont::kPoppins},
+ {"Sans-serif", ReadAnythingFont::kSansSerif},
+ {"Serif", ReadAnythingFont::kSerif},
+ {"Comic Neue", ReadAnythingFont::kComicNeue},
+ {"Lexend Deca", ReadAnythingFont::kLexendDeca},
+ {"EB Garamond", ReadAnythingFont::kEbGaramond},
+ {"STIX Two Text", ReadAnythingFont::kStixTwoText},
+ };
const mojo::Receiver<read_anything::mojom::UntrustedPageHandler> receiver_;
const mojo::Remote<read_anything::mojom::UntrustedPage> page_;
@@ -135,4 +157,4 @@ class ReadAnythingPageHandler
#endif
};
-#endif // CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_PAGE_HANDLER_H_
+#endif // CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_UNTRUSTED_PAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.cc b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
index 02ce120d1bf..00eb3c22168 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.h"
+#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.h"
#include <string>
#include <utility>
#include "chrome/browser/ui/ui_features.h"
-#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_page_handler.h"
+#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
@@ -33,7 +33,7 @@ ReadAnythingUIUntrustedConfig::~ReadAnythingUIUntrustedConfig() = default;
std::unique_ptr<content::WebUIController>
ReadAnythingUIUntrustedConfig::CreateWebUIController(content::WebUI* web_ui,
const GURL& url) {
- return std::make_unique<ReadAnythingUI>(web_ui);
+ return std::make_unique<ReadAnythingUntrustedUI>(web_ui);
}
bool ReadAnythingUIUntrustedConfig::IsWebUIEnabled(
@@ -41,7 +41,7 @@ bool ReadAnythingUIUntrustedConfig::IsWebUIEnabled(
return features::IsReadAnythingEnabled();
}
-ReadAnythingUI::ReadAnythingUI(content::WebUI* web_ui)
+ReadAnythingUntrustedUI::ReadAnythingUntrustedUI(content::WebUI* web_ui)
: ui::UntrustedBubbleWebUIController(web_ui) {
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
web_ui->GetWebContents()->GetBrowserContext(),
@@ -52,16 +52,39 @@ ReadAnythingUI::ReadAnythingUI(content::WebUI* web_ui)
{"emptyStateHeader", IDS_READING_MODE_EMPTY_STATE_HEADER},
{"emptyStateSubheader", IDS_READING_MODE_EMPTY_STATE_SUBHEADER},
{"readAnythingLoadingMessage", IDS_READ_ANYTHING_LOADING},
+ {"lineSpacingTitle", IDS_READING_MODE_LINE_SPACING_COMBOBOX_LABEL},
+ {"fontNameTitle", IDS_READING_MODE_FONT_NAME_COMBOBOX_LABEL},
+ {"themeTitle", IDS_READING_MODE_COLORS_COMBOBOX_LABEL},
+ {"letterSpacingTitle", IDS_READING_MODE_LETTER_SPACING_COMBOBOX_LABEL},
+ {"fontSizeTitle", IDS_READING_MODE_FONT_SIZE},
+ {"displaySectionTitle", IDS_READING_MODE_DISPLAY_SETTINGS},
+ {"defaultColorTitle", IDS_READING_MODE_DEFAULT_COLOR_LABEL},
+ {"lightColorTitle", IDS_READING_MODE_LIGHT_COLOR_LABEL},
+ {"darkColorTitle", IDS_READING_MODE_DARK_COLOR_LABEL},
+ {"yellowColorTitle", IDS_READING_MODE_YELLOW_COLOR_LABEL},
+ {"blueColorTitle", IDS_READING_MODE_BLUE_COLOR_LABEL},
+ {"back", IDS_READING_MODE_BACK},
+ {"lineSpacingStandardTitle", IDS_READING_MODE_SPACING_COMBOBOX_STANDARD},
+ {"lineSpacingLooseTitle", IDS_READING_MODE_SPACING_COMBOBOX_LOOSE},
+ {"lineSpacingVeryLooseTitle",
+ IDS_READING_MODE_SPACING_COMBOBOX_VERY_LOOSE},
+ {"letterSpacingStandardTitle",
+ IDS_READING_MODE_SPACING_COMBOBOX_STANDARD},
+ {"letterSpacingWideTitle", IDS_READING_MODE_SPACING_COMBOBOX_WIDE},
+ {"letterSpacingVeryWideTitle",
+ IDS_READING_MODE_SPACING_COMBOBOX_VERY_WIDE},
};
- for (const auto& str : kLocalizedStrings)
+ for (const auto& str : kLocalizedStrings) {
webui::AddLocalizedString(source, str.name, str.id);
+ }
// Rather than call `webui::SetupWebUIDataSource`, manually set up source
// here. This ensures that if CSPs change in a way that is safe for chrome://
- // but not chrome-untrusted://, ReadAnythingUI does not inherit them.
+ // but not chrome-untrusted://, ReadAnythingUntrustedUI does not inherit them.
source->UseStringsJs();
source->EnableReplaceI18nInJS();
webui::EnableTrustedTypesCSP(source);
+ webui::SetupChromeRefresh2023(source);
source->AddResourcePaths(base::make_span(
kSidePanelReadAnythingResources, kSidePanelReadAnythingResourcesSize));
source->AddResourcePath("", IDR_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_HTML);
@@ -72,7 +95,7 @@ ReadAnythingUI::ReadAnythingUI(content::WebUI* web_ui)
"script-src 'self' chrome-untrusted://resources;");
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::StyleSrc,
- "style-src 'self' chrome-untrusted://resources "
+ "style-src 'self' chrome-untrusted://resources chrome-untrusted://theme "
"https://fonts.googleapis.com 'unsafe-inline';");
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::FontSrc,
@@ -83,24 +106,35 @@ ReadAnythingUI::ReadAnythingUI(content::WebUI* web_ui)
"img-src 'self' chrome-untrusted://resources;");
}
-ReadAnythingUI::~ReadAnythingUI() = default;
+ReadAnythingUntrustedUI::~ReadAnythingUntrustedUI() = default;
-WEB_UI_CONTROLLER_TYPE_IMPL(ReadAnythingUI)
+WEB_UI_CONTROLLER_TYPE_IMPL(ReadAnythingUntrustedUI)
-void ReadAnythingUI::BindInterface(
+void ReadAnythingUntrustedUI::BindInterface(
+ mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+ pending_receiver) {
+ if (features::IsReadAnythingWebUIToolbarEnabled()) {
+ color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
+ web_ui()->GetWebContents(), std::move(pending_receiver));
+ }
+}
+
+void ReadAnythingUntrustedUI::BindInterface(
mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandlerFactory>
receiver) {
read_anything_page_factory_receiver_.reset();
read_anything_page_factory_receiver_.Bind(std::move(receiver));
}
-void ReadAnythingUI::CreateUntrustedPageHandler(
+void ReadAnythingUntrustedUI::CreateUntrustedPageHandler(
mojo::PendingRemote<read_anything::mojom::UntrustedPage> page,
mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler>
receiver) {
DCHECK(page);
- read_anything_page_handler_ = std::make_unique<ReadAnythingPageHandler>(
- std::move(page), std::move(receiver), web_ui());
- if (embedder())
+ read_anything_untrusted_page_handler_ =
+ std::make_unique<ReadAnythingUntrustedPageHandler>(
+ std::move(page), std::move(receiver), web_ui());
+ if (embedder()) {
embedder()->ShowUI();
+ }
}
diff --git a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.h b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.h
index 4e04bd644b2..f1a39bed8f6 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.h
+++ b/chromium/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_UI_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_UNTRUSTED_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_UNTRUSTED_UI_H_
#include <memory>
@@ -12,9 +12,11 @@
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
+#include "ui/webui/color_change_listener/color_change_handler.h"
+#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
#include "ui/webui/untrusted_bubble_web_ui_controller.h"
-class ReadAnythingPageHandler;
+class ReadAnythingUntrustedPageHandler;
class ReadAnythingUIUntrustedConfig : public content::WebUIConfig {
public:
@@ -29,19 +31,23 @@ class ReadAnythingUIUntrustedConfig : public content::WebUIConfig {
};
///////////////////////////////////////////////////////////////////////////////
-// ReadAnythingUI
+// ReadAnythingUntrustedUI
//
// A WebUI that holds distilled page contents for the Read Anything feature.
// This class has the same lifetime as the Side Panel view.
//
-class ReadAnythingUI
+class ReadAnythingUntrustedUI
: public ui::UntrustedBubbleWebUIController,
public read_anything::mojom::UntrustedPageHandlerFactory {
public:
- explicit ReadAnythingUI(content::WebUI* web_ui);
- ReadAnythingUI(const ReadAnythingUI&) = delete;
- ReadAnythingUI& operator=(const ReadAnythingUI&) = delete;
- ~ReadAnythingUI() override;
+ explicit ReadAnythingUntrustedUI(content::WebUI* web_ui);
+ ReadAnythingUntrustedUI(const ReadAnythingUntrustedUI&) = delete;
+ ReadAnythingUntrustedUI& operator=(const ReadAnythingUntrustedUI&) = delete;
+ ~ReadAnythingUntrustedUI() override;
+
+ void BindInterface(
+ mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+ pending_receiver);
// Instantiates the implementor of the mojom::UntrustedPageHandlerFactory mojo
// interface passing the pending receiver that will be internally bound.
@@ -56,11 +62,13 @@ class ReadAnythingUI
mojo::PendingReceiver<read_anything::mojom::UntrustedPageHandler>
receiver) override;
- std::unique_ptr<ReadAnythingPageHandler> read_anything_page_handler_;
+ std::unique_ptr<ReadAnythingUntrustedPageHandler>
+ read_anything_untrusted_page_handler_;
+ std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
mojo::Receiver<read_anything::mojom::UntrustedPageHandlerFactory>
read_anything_page_factory_receiver_{this};
WEB_UI_CONTROLLER_TYPE_DECL();
};
-#endif // CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_UI_H_
+#endif // CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_UNTRUSTED_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler_unittest.cc b/chromium/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler_unittest.cc
index 9e1ccab7097..b4b503b303c 100644
--- a/chromium/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler_unittest.cc
@@ -9,8 +9,9 @@
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
-#include "chrome/browser/ui/webui/side_panel/user_notes/user_notes.mojom-test-utils.h"
+#include "chrome/browser/ui/webui/side_panel/user_notes/user_notes.mojom.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/test_browser_window.h"
#include "components/bookmarks/browser/bookmark_model.h"
@@ -81,6 +82,51 @@ class UserNotesPageHandlerTest : public BrowserWithTestWindowTest {
TestUserNotesPageHandler* handler() { return handler_.get(); }
+ bool NewNoteFinished(const std::string& note) {
+ base::test::TestFuture<bool> success_future;
+ handler_->NewNoteFinished(note, success_future.GetCallback());
+ return success_future.Take();
+ }
+
+ std::vector<side_panel::mojom::NotePtr> GetNotesForCurrentTab() {
+ base::test::TestFuture<std::vector<side_panel::mojom::NotePtr>>
+ notes_future;
+ handler_->GetNotesForCurrentTab(notes_future.GetCallback());
+ return notes_future.Take();
+ }
+
+ bool HasNotesInAnyPages() {
+ base::test::TestFuture<bool> has_notes_future;
+ handler_->HasNotesInAnyPages(has_notes_future.GetCallback());
+ return has_notes_future.Take();
+ }
+
+ std::vector<side_panel::mojom::NoteOverviewPtr> GetNoteOverviews(
+ const std::string& user_input) {
+ base::test::TestFuture<std::vector<side_panel::mojom::NoteOverviewPtr>>
+ note_overviews_future;
+ handler_->GetNoteOverviews(user_input, note_overviews_future.GetCallback());
+ return note_overviews_future.Take();
+ }
+
+ bool UpdateNote(const std::string& guid, const std::string& text) {
+ base::test::TestFuture<bool> update_note_future;
+ handler_->UpdateNote(guid, text, update_note_future.GetCallback());
+ return update_note_future.Take();
+ }
+
+ bool DeleteNotesForUrl(const GURL& url) {
+ base::test::TestFuture<bool> delete_notes_future;
+ handler_->DeleteNotesForUrl(url, delete_notes_future.GetCallback());
+ return delete_notes_future.Take();
+ }
+
+ bool DeleteNote(const std::string& guid) {
+ base::test::TestFuture<bool> delete_note_future;
+ handler_->DeleteNote(guid, delete_note_future.GetCallback());
+ return delete_note_future.Take();
+ }
+
protected:
MockUserNotesPage page_;
raw_ptr<bookmarks::BookmarkModel, DanglingUntriaged> bookmark_model_;
@@ -91,152 +137,141 @@ class UserNotesPageHandlerTest : public BrowserWithTestWindowTest {
};
TEST_F(UserNotesPageHandlerTest, GetNotes) {
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note11"));
- auto notes = waiter.GetNotesForCurrentTab();
+ ASSERT_TRUE(NewNoteFinished("note1"));
+ ASSERT_TRUE(NewNoteFinished("note11"));
+ auto notes = GetNotesForCurrentTab();
ASSERT_EQ(2u, notes.size());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url2"));
- ASSERT_TRUE(waiter.NewNoteFinished("note2"));
- auto notes2 = waiter.GetNotesForCurrentTab();
+ ASSERT_TRUE(NewNoteFinished("note2"));
+ auto notes2 = GetNotesForCurrentTab();
ASSERT_EQ(1u, notes2.size());
ASSERT_EQ("note2", notes2[0]->text);
}
TEST_F(UserNotesPageHandlerTest, GetNoteOverviews) {
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note1"));
+ ASSERT_TRUE(NewNoteFinished("note1"));
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url2"));
- ASSERT_TRUE(waiter.NewNoteFinished("note2"));
+ ASSERT_TRUE(NewNoteFinished("note2"));
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url3"));
- ASSERT_TRUE(waiter.NewNoteFinished("note3"));
- auto note_overviews = waiter.GetNoteOverviews("");
+ ASSERT_TRUE(NewNoteFinished("note3"));
+ auto note_overviews = GetNoteOverviews("");
ASSERT_EQ(3u, note_overviews.size());
}
TEST_F(UserNotesPageHandlerTest, GetNoteOverviewIsCurrentTab) {
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note1"));
+ ASSERT_TRUE(NewNoteFinished("note1"));
// Note overview is for current tab.
- auto note_overviews = waiter.GetNoteOverviews("");
+ auto note_overviews = GetNoteOverviews("");
ASSERT_EQ(1u, note_overviews.size());
ASSERT_TRUE(note_overviews[0]->is_current_tab);
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url2"));
// Note overview is not for current tab.
- note_overviews = waiter.GetNoteOverviews("");
+ note_overviews = GetNoteOverviews("");
ASSERT_EQ(1u, note_overviews.size());
ASSERT_FALSE(note_overviews[0]->is_current_tab);
}
TEST_F(UserNotesPageHandlerTest, GetNoteOverviewWithBookmarkTitle) {
// Add a new bookmark with url.
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
const bookmarks::BookmarkNode* bb_node = bookmark_model_->bookmark_bar_node();
GURL url("https://google.com");
bookmark_model_->AddNewURL(bb_node, 0, u"title", url);
// Make sure the note overview title is read from the bookmark.
handler()->SetCurrentTabUrlForTesting(url);
- ASSERT_TRUE(waiter.NewNoteFinished("note1"));
- auto note_overviews = waiter.GetNoteOverviews("");
+ ASSERT_TRUE(NewNoteFinished("note1"));
+ auto note_overviews = GetNoteOverviews("");
ASSERT_EQ(1u, note_overviews.size());
ASSERT_EQ("title", note_overviews[0]->title);
}
TEST_F(UserNotesPageHandlerTest, GetNoteOverviewsReturnMatchedText) {
// Searching notes should match case insensitive.
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("Note1"));
- auto note_overviews = waiter.GetNoteOverviews("");
+ ASSERT_TRUE(NewNoteFinished("Note1"));
+ auto note_overviews = GetNoteOverviews("");
ASSERT_EQ(1u, note_overviews.size());
ASSERT_EQ("Note1", note_overviews[0]->text);
}
TEST_F(UserNotesPageHandlerTest, FindNoteOverviewsSearchOnlyText) {
// Searching notes should only match texts, not URL.
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://new_url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note1"));
+ ASSERT_TRUE(NewNoteFinished("note1"));
handler()->SetCurrentTabUrlForTesting(GURL(u"https://new_url2"));
- ASSERT_TRUE(waiter.NewNoteFinished("note2"));
+ ASSERT_TRUE(NewNoteFinished("note2"));
handler()->SetCurrentTabUrlForTesting(GURL(u"https://new_url3"));
- ASSERT_TRUE(waiter.NewNoteFinished("3"));
- auto note_overviews = waiter.GetNoteOverviews("n");
+ ASSERT_TRUE(NewNoteFinished("3"));
+ auto note_overviews = GetNoteOverviews("n");
ASSERT_EQ(2u, note_overviews.size());
}
TEST_F(UserNotesPageHandlerTest, FindNoteOverviewsCaseInsensitive) {
// Searching notes should match case insensitive.
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("Note1"));
+ ASSERT_TRUE(NewNoteFinished("Note1"));
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url2"));
- ASSERT_TRUE(waiter.NewNoteFinished("Note2"));
+ ASSERT_TRUE(NewNoteFinished("Note2"));
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url3"));
- ASSERT_TRUE(waiter.NewNoteFinished("3"));
- auto note_overviews = waiter.GetNoteOverviews("n");
+ ASSERT_TRUE(NewNoteFinished("3"));
+ auto note_overviews = GetNoteOverviews("n");
ASSERT_EQ(2u, note_overviews.size());
}
TEST_F(UserNotesPageHandlerTest, FindNoteOverviewsReturnMatchedText) {
// Searching notes should match case insensitive.
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("Note1"));
+ ASSERT_TRUE(NewNoteFinished("Note1"));
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url2"));
- ASSERT_TRUE(waiter.NewNoteFinished("foo"));
- auto note_overviews = waiter.GetNoteOverviews("Note");
+ ASSERT_TRUE(NewNoteFinished("foo"));
+ auto note_overviews = GetNoteOverviews("Note");
ASSERT_EQ(1u, note_overviews.size());
ASSERT_EQ("Note1", note_overviews[0]->text);
}
TEST_F(UserNotesPageHandlerTest, CreateAndDeleteNote) {
EXPECT_CALL(page_, NotesChanged()).Times(2);
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note1"));
+ ASSERT_TRUE(NewNoteFinished("note1"));
- auto notes = waiter.GetNotesForCurrentTab();
+ auto notes = GetNotesForCurrentTab();
ASSERT_EQ(1u, notes.size());
std::string guid = notes[0]->guid;
- ASSERT_TRUE(waiter.DeleteNote(guid));
+ ASSERT_TRUE(DeleteNote(guid));
- auto notes2 = waiter.GetNotesForCurrentTab();
+ auto notes2 = GetNotesForCurrentTab();
ASSERT_EQ(0u, notes2.size());
}
TEST_F(UserNotesPageHandlerTest, ShouldNotCreateNoteWithEmptyURL) {
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL::EmptyGURL());
- ASSERT_FALSE(waiter.NewNoteFinished("note5"));
+ ASSERT_FALSE(NewNoteFinished("note5"));
- auto notes = waiter.GetNotesForCurrentTab();
+ auto notes = GetNotesForCurrentTab();
ASSERT_EQ(0u, notes.size());
}
TEST_F(UserNotesPageHandlerTest, UpdateNote) {
EXPECT_CALL(page_, NotesChanged()).Times(2);
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note1"));
+ ASSERT_TRUE(NewNoteFinished("note1"));
- auto notes = waiter.GetNotesForCurrentTab();
+ auto notes = GetNotesForCurrentTab();
ASSERT_EQ(1u, notes.size());
ASSERT_EQ("note1", notes[0]->text);
std::string guid = notes[0]->guid;
- ASSERT_TRUE(waiter.UpdateNote(guid, "note2"));
+ ASSERT_TRUE(UpdateNote(guid, "note2"));
- auto notes2 = waiter.GetNotesForCurrentTab();
+ auto notes2 = GetNotesForCurrentTab();
ASSERT_EQ(1u, notes.size());
ASSERT_EQ(guid, notes2[0]->guid);
ASSERT_EQ("note2", notes2[0]->text);
@@ -244,13 +279,12 @@ TEST_F(UserNotesPageHandlerTest, UpdateNote) {
TEST_F(UserNotesPageHandlerTest, DeleteNotesForUrl) {
EXPECT_CALL(page_, NotesChanged()).Times(3);
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note2"));
- ASSERT_TRUE(waiter.DeleteNotesForUrl(GURL(u"https://url1")));
+ ASSERT_TRUE(NewNoteFinished("note1"));
+ ASSERT_TRUE(NewNoteFinished("note2"));
+ ASSERT_TRUE(DeleteNotesForUrl(GURL(u"https://url1")));
- auto notes = waiter.GetNotesForCurrentTab();
+ auto notes = GetNotesForCurrentTab();
ASSERT_EQ(0u, notes.size());
}
@@ -278,11 +312,10 @@ TEST_F(UserNotesPageHandlerTest, CurrentTabUrlChangedWithNavigation) {
}
TEST_F(UserNotesPageHandlerTest, HasNotesOnAnyPages) {
- side_panel::mojom::UserNotesPageHandlerAsyncWaiter waiter(handler());
- ASSERT_EQ(false, waiter.HasNotesInAnyPages());
+ ASSERT_EQ(false, HasNotesInAnyPages());
handler()->SetCurrentTabUrlForTesting(GURL(u"https://url1"));
- ASSERT_TRUE(waiter.NewNoteFinished("note1"));
- ASSERT_EQ(true, waiter.HasNotesInAnyPages());
+ ASSERT_TRUE(NewNoteFinished("note1"));
+ ASSERT_EQ(true, HasNotesInAnyPages());
}
} // namespace
diff --git a/chromium/chrome/browser/ui/webui/signin/OWNERS b/chromium/chrome/browser/ui/webui/signin/OWNERS
index 570c5760e8a..b5241c7b2af 100644
--- a/chromium/chrome/browser/ui/webui/signin/OWNERS
+++ b/chromium/chrome/browser/ui/webui/signin/OWNERS
@@ -9,3 +9,7 @@ per-file inline_login_handler.*=file://chrome/credential_provider/OWNERS
# For ChromeOS changes
per-file inline_login_*_chromeos*=file://chromeos/ash/components/account_manager/OWNERS
per-file signin_helper_chromeos.*=file://chromeos/ash/components/account_manager/OWNERS
+
+per-file dice_web_signin_intercept_*=gabolvr@google.com
+per-file profile_customization_*=gabolvr@google.com
+per-file sync_confirmation_*=gabolvr@google.com
diff --git a/chromium/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc b/chromium/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc
index c32824f1cb4..9522f59bf78 100644
--- a/chromium/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc
@@ -17,12 +17,15 @@
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/ash/components/standalone_browser/feature_refs.h"
#include "components/account_manager_core/account_manager_facade.h"
#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
#include "components/account_manager_core/mock_account_manager_facade.h"
@@ -359,12 +362,21 @@ class InlineLoginHandlerTestWithArcRestrictions
: public InlineLoginHandlerTest {
public:
InlineLoginHandlerTestWithArcRestrictions() {
- feature_list_.InitAndEnableFeature(ash::features::kLacrosSupport);
+ auto lacros = ash::standalone_browser::GetFeatureRefs();
+ lacros.push_back(crosapi::browser_util::kLacrosForSupervisedUsers);
+ feature_list_.InitWithFeatures(/*enabled=*/lacros, /*disabled=*/{});
}
~InlineLoginHandlerTestWithArcRestrictions() override = default;
void SetUpOnMainThread() override {
+ if (browser() == nullptr) {
+ // Create a new Ash browser window so test code using browser() can work
+ // even when Lacros is the only browser.
+ // TODO(crbug.com/1450158): Remove uses of browser() from such tests.
+ chrome::NewEmptyWindow(ProfileManager::GetActiveUserProfile());
+ SelectFirstBrowser();
+ }
InlineLoginHandlerTest::SetUpOnMainThread();
// In-session account addition happens when `AccountAppsAvailability` is
// already initialized.
diff --git a/chromium/chrome/browser/ui/webui/signin/ash/signin_helper_browsertest.cc b/chromium/chrome/browser/ui/webui/signin/ash/signin_helper_browsertest.cc
index 997a4c621a3..f07e145b5f2 100644
--- a/chromium/chrome/browser/ui/webui/signin/ash/signin_helper_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/signin/ash/signin_helper_browsertest.cc
@@ -21,9 +21,12 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/ash/components/account_manager/account_manager_factory.h"
+#include "chromeos/ash/components/standalone_browser/feature_refs.h"
#include "components/account_manager_core/account.h"
#include "components/account_manager_core/chromeos/account_manager.h"
#include "components/account_manager_core/chromeos/account_manager_mojo_service.h"
@@ -251,9 +254,10 @@ class SigninHelperTest : public InProcessBrowserTest,
void OnAccountRemoved(const account_manager::Account& account) override {}
- raw_ptr<account_manager::AccountManager, ExperimentalAsh> account_manager_ =
- nullptr;
- raw_ptr<crosapi::AccountManagerMojoService, ExperimentalAsh>
+ raw_ptr<account_manager::AccountManager, DanglingUntriaged | ExperimentalAsh>
+ account_manager_ = nullptr;
+ raw_ptr<crosapi::AccountManagerMojoService,
+ DanglingUntriaged | ExperimentalAsh>
account_manager_mojo_service_ = nullptr;
int signin_helper_created_count_ = 0;
int signin_helper_deleted_count_ = 0;
@@ -303,12 +307,21 @@ class SigninHelperTestWithArcAccountRestrictions
public ::ash::AccountAppsAvailability::Observer {
public:
SigninHelperTestWithArcAccountRestrictions() {
- feature_list_.InitAndEnableFeature(ash::features::kLacrosSupport);
+ feature_list_.InitWithFeatures(ash::standalone_browser::GetFeatureRefs(),
+ {});
}
~SigninHelperTestWithArcAccountRestrictions() override = default;
void SetUpOnMainThread() override {
+ if (browser() == nullptr) {
+ // Create a new Ash browser window so test code using browser() can work
+ // even when Lacros is the only browser.
+ // TODO(crbug.com/1450158): Remove uses of browser() from such tests.
+ chrome::NewEmptyWindow(ProfileManager::GetActiveUserProfile());
+ SelectFirstBrowser();
+ }
+
SigninHelperTest::SetUpOnMainThread();
account_apps_availability_ =
ash::AccountAppsAvailabilityFactory::GetForProfile(
@@ -390,7 +403,7 @@ class SigninHelperTestWithArcAccountRestrictions
absl::optional<account_manager::Account> on_account_available_in_arc_account_;
absl::optional<account_manager::Account>
on_account_unavailable_in_arc_account_;
- raw_ptr<ash::AccountAppsAvailability, ExperimentalAsh>
+ raw_ptr<ash::AccountAppsAvailability, DanglingUntriaged | ExperimentalAsh>
account_apps_availability_;
base::test::ScopedFeatureList feature_list_;
};
@@ -499,7 +512,8 @@ IN_PROC_BROWSER_TEST_F(SigninHelperTestWithArcAccountRestrictions,
class SigninHelperTestSecondaryGoogleAccountUsage : public SigninHelperTest {
public:
SigninHelperTestSecondaryGoogleAccountUsage() {
- feature_list_.InitAndDisableFeature(ash::features::kLacrosSupport);
+ feature_list_.InitWithFeatures({},
+ ash::standalone_browser::GetFeatureRefs());
}
~SigninHelperTestSecondaryGoogleAccountUsage() override = default;
diff --git a/chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc b/chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
index 4ad817728e1..7b4d0508d28 100644
--- a/chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
@@ -20,7 +20,7 @@
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_features.h"
#include "chrome/browser/ui/managed_ui.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
@@ -248,21 +248,10 @@ bool DiceWebSigninInterceptHandler::ShouldShowManagedDeviceVersion() {
}
std::string DiceWebSigninInterceptHandler::GetHeaderText() {
- if (bubble_parameters_.interception_type ==
- WebSigninInterceptor::SigninInterceptionType::kProfileSwitch) {
- return intercepted_account().given_name;
- }
-
- if (base::FeatureList::IsEnabled(kSigninInterceptBubbleV2))
- return std::string();
-
- if (bubble_parameters_.interception_type ==
- WebSigninInterceptor::SigninInterceptionType::kEnterprise &&
- IsManaged(intercepted_account())) {
- return intercepted_account().hosted_domain;
- }
-
- return intercepted_account().given_name;
+ return (bubble_parameters_.interception_type ==
+ WebSigninInterceptor::SigninInterceptionType::kProfileSwitch)
+ ? intercepted_account().given_name
+ : std::string();
}
std::string DiceWebSigninInterceptHandler::GetBodyTitle() {
@@ -367,10 +356,6 @@ std::string DiceWebSigninInterceptHandler::GetManagedDisclaimerText() {
}
bool DiceWebSigninInterceptHandler::GetShouldUseV2Design() {
- if (bubble_parameters_.interception_type ==
- WebSigninInterceptor::SigninInterceptionType::kProfileSwitch) {
- return false;
- }
-
- return base::FeatureList::IsEnabled(kSigninInterceptBubbleV2);
+ return bubble_parameters_.interception_type !=
+ WebSigninInterceptor::SigninInterceptionType::kProfileSwitch;
}
diff --git a/chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler_unittest.cc b/chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler_unittest.cc
index 7bf3cd302df..30401721c70 100644
--- a/chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler_unittest.cc
@@ -48,7 +48,6 @@ struct TestParam {
WebSigninInterceptor::SigninInterceptionType interception_type;
policy::EnterpriseManagementAuthority management_authority;
ExpectedStringGenerator expected_strings;
- ExpectedStringGenerator expected_strings_v2;
};
AccountInfo CreateAccount(std::string gaia_id,
@@ -79,34 +78,14 @@ const AccountInfo intercepted_account = CreateAccount(
/*email=*/"sam.sample@intercepted.com",
/*hosted_domain=*/kNoHostedDomainFound);
-const ExpectedStringGenerator common_v2_strings_generator =
- base::BindRepeating([] {
- return BubbleStrings{
- /*header_text=*/"",
- /*body_title=*/
- l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE),
- /*body_text=*/
- l10n_util::GetStringFUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_CONSUMER_BUBBLE_DESC,
- base::UTF8ToUTF16(primary_account.given_name)),
- /*confirm_button_label=*/
- l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_NEW_PROFILE_BUTTON_LABEL),
- /*cancel_button_label=*/
- l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CANCEL_BUTTON_LABEL),
- };
- });
-
// Permutations of supported bubbles.
const TestParam kTestParams[] = {
{
WebSigninInterceptor::SigninInterceptionType::kMultiUser,
policy::EnterpriseManagementAuthority::NONE,
- /*expected_strings=*/base::BindRepeating([]() {
+ /*expected_strings=*/base::BindRepeating([] {
return BubbleStrings{
- /*header_text=*/intercepted_account.given_name,
+ /*header_text=*/"",
/*body_title=*/
l10n_util::GetStringUTF8(
IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE),
@@ -122,31 +101,11 @@ const TestParam kTestParams[] = {
IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CANCEL_BUTTON_LABEL),
};
}),
- /*expected_strings_v2=*/common_v2_strings_generator,
},
{
WebSigninInterceptor::SigninInterceptionType::kMultiUser,
policy::EnterpriseManagementAuthority::CLOUD_DOMAIN,
- /*expected_strings=*/base::BindRepeating([]() {
- return BubbleStrings{
- /*header_text=*/intercepted_account.given_name,
- /*body_title=*/
- l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE),
- /*body_text=*/
- l10n_util::GetStringFUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_CONSUMER_BUBBLE_DESC_MANAGED_DEVICE,
- base::UTF8ToUTF16(primary_account.given_name),
- base::UTF8ToUTF16(intercepted_account.email)),
- /*confirm_button_label=*/
- l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_NEW_PROFILE_BUTTON_LABEL),
- /*cancel_button_label=*/
- l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CANCEL_BUTTON_LABEL),
- };
- }),
- /*expected_strings_v2=*/base::BindRepeating([] {
+ /*expected_strings=*/base::BindRepeating([] {
return BubbleStrings{
/*header_text=*/"",
/*body_title=*/
@@ -169,25 +128,7 @@ const TestParam kTestParams[] = {
{
WebSigninInterceptor::SigninInterceptionType::kEnterprise,
policy::EnterpriseManagementAuthority::NONE,
- /*expected_strings=*/base::BindRepeating([]() {
- return BubbleStrings{
- /*header_text=*/intercepted_account.given_name,
- /*body_title=*/
- l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE),
- /*body_text=*/
- l10n_util::GetStringFUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_BUBBLE_DESC,
- base::UTF8ToUTF16(primary_account.email)),
- /*confirm_button_label=*/
- l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_NEW_PROFILE_BUTTON_LABEL),
- /*cancel_button_label=*/
- l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CANCEL_BUTTON_LABEL),
- };
- }),
- /*expected_strings_v2=*/base::BindRepeating([] {
+ /*expected_strings=*/base::BindRepeating([] {
return BubbleStrings{
/*header_text=*/"",
/*body_title=*/
@@ -209,9 +150,9 @@ const TestParam kTestParams[] = {
{
WebSigninInterceptor::SigninInterceptionType::kEnterprise,
policy::EnterpriseManagementAuthority::CLOUD_DOMAIN,
- /*expected_strings=*/base::BindRepeating([]() {
+ /*expected_strings=*/base::BindRepeating([] {
return BubbleStrings{
- /*header_text=*/intercepted_account.given_name,
+ /*header_text=*/"",
/*body_title=*/
l10n_util::GetStringUTF8(
IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE),
@@ -227,22 +168,25 @@ const TestParam kTestParams[] = {
IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CANCEL_BUTTON_LABEL),
};
}),
- /*expected_strings_v2=*/base::BindRepeating([] {
+ },
+ {
+ WebSigninInterceptor::SigninInterceptionType::kProfileSwitch,
+ policy::EnterpriseManagementAuthority::NONE,
+ /*expected_strings=*/base::BindRepeating([] {
return BubbleStrings{
- /*header_text=*/"",
+ /*header_text=*/intercepted_account.given_name,
/*body_title=*/
l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE),
+ IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_TITLE),
/*body_text=*/
- l10n_util::GetStringFUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_BUBBLE_DESC_MANAGED_DEVICE,
- base::UTF8ToUTF16(intercepted_account.email)),
+ l10n_util::GetStringUTF8(
+ IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC),
/*confirm_button_label=*/
l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_NEW_PROFILE_BUTTON_LABEL),
+ IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CONFIRM_SWITCH_BUTTON_LABEL),
/*cancel_button_label=*/
l10n_util::GetStringUTF8(
- IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CANCEL_BUTTON_LABEL),
+ IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CANCEL_SWITCH_BUTTON_LABEL),
};
}),
},
@@ -303,22 +247,15 @@ class DiceWebSigninInterceptHandlerTest
};
TEST_P(DiceWebSigninInterceptHandlerTest, CheckStrings) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndDisableFeature(kSigninInterceptBubbleV2);
base::Value::Dict parameters = GetInterceptionParameters();
- EXPECT_FALSE(*parameters.FindBool("useV2Design"));
+ if (GetParam().interception_type !=
+ WebSigninInterceptor::SigninInterceptionType::kProfileSwitch) {
+ EXPECT_TRUE(*parameters.FindBool("useV2Design"));
+ }
ExpectStringsMatch(parameters, GetParam().expected_strings.Run());
}
-TEST_P(DiceWebSigninInterceptHandlerTest, CheckStrings_V2) {
- base::test::ScopedFeatureList feature_list{kSigninInterceptBubbleV2};
- base::Value::Dict parameters = GetInterceptionParameters();
-
- EXPECT_TRUE(*parameters.FindBool("useV2Design"));
- ExpectStringsMatch(parameters, GetParam().expected_strings_v2.Run());
-}
-
INSTANTIATE_TEST_SUITE_P(All,
DiceWebSigninInterceptHandlerTest,
testing::ValuesIn(kTestParams));
diff --git a/chromium/chrome/browser/ui/webui/signin/enterprise_profile_welcome_handler.cc b/chromium/chrome/browser/ui/webui/signin/enterprise_profile_welcome_handler.cc
index 4299f91d71c..968d48cc47d 100644
--- a/chromium/chrome/browser/ui/webui/signin/enterprise_profile_welcome_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/enterprise_profile_welcome_handler.cc
@@ -23,14 +23,14 @@
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/managed_ui.h"
-#include "chrome/browser/ui/signin_view_controller.h"
+#include "chrome/browser/ui/signin/signin_view_controller.h"
#include "chrome/browser/ui/webui/management/management_ui_handler.h"
#include "chrome/browser/ui/webui/signin/signin_utils.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
-#include "chrome/grit/google_chrome_strings.h"
+#include "components/policy/core/browser/signin/profile_separation_policies.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/strings/grit/components_strings.h"
@@ -70,9 +70,8 @@ std::string GetManagedAccountTitleWithEmail(
#if !BUILDFLAG(IS_CHROMEOS)
absl::optional<std::string> account_manager =
chrome::GetAccountManagerIdentity(profile);
- auto profile_separation_state =
- signin_util::GetProfileSeparationPolicyState(profile);
- if (profile_separation_state.Empty()) {
+
+ if (!signin_util::IsProfileSeparationEnforcedByProfile(profile)) {
// The profile is managed but does not enforce profile separation. The
// intercepted account requires it.
if (account_manager && !account_manager->empty()) {
@@ -86,9 +85,8 @@ std::string GetManagedAccountTitleWithEmail(
return l10n_util::GetStringFUTF8(
IDS_ENTERPRISE_PROFILE_WELCOME_ACCOUNT_EMAIL_MANAGED_BY, email,
base::UTF8ToUTF16(account_domain_name));
- } else if (profile_separation_state.Has(
- signin_util::ProfileSeparationPolicyState::
- kEnforcedOnMachineLevel)) {
+ } else if (profile->GetPrefs()->GetBoolean(
+ prefs::kManagedAccountsSigninRestrictionScopeMachine)) {
// The device is managed and requires profile separation.
absl::optional<std::string> device_manager =
chrome::GetDeviceManagerIdentity();
@@ -102,9 +100,6 @@ std::string GetManagedAccountTitleWithEmail(
email);
}
} else {
- DCHECK(profile_separation_state.Has(
- signin_util::ProfileSeparationPolicyState::kStrict));
- // The profile is managed and requires profile separation.
DCHECK(account_manager);
DCHECK(!account_manager->empty());
return l10n_util::GetStringFUTF8(
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_handler.cc b/chromium/chrome/browser/ui/webui/signin/inline_login_handler.cc
index c18e86a8f61..1fe3e2d9f94 100644
--- a/chromium/chrome/browser/ui/webui/signin/inline_login_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_handler.cc
@@ -23,7 +23,7 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
-#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/browser/ui/webui/signin/signin_utils.h"
#include "chrome/common/pref_names.h"
#include "components/metrics/metrics_pref_names.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
index 4ee84ce40f5..6810a7777ac 100644
--- a/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -47,9 +47,9 @@
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/profile_picker.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
-#include "chrome/browser/ui/signin/profile_customization_bubble_sync_controller.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_customization_bubble_sync_controller.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -231,7 +231,7 @@ void OnSigninComplete(Profile* profile,
if (reuse_manager) {
reuse_manager->SaveGaiaPasswordHash(
username, base::UTF8ToUTF16(password),
- /*is_primary_account_=*/true,
+ /*is_sync_password_for_metrics=*/true,
password_manager::metrics_util::GaiaPasswordHashChange::
SAVED_ON_CHROME_SIGNIN);
}
@@ -375,7 +375,7 @@ void InlineSigninHelper::OnClientOAuthSuccessAndBrowserOpened(
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_);
- std::string primary_email =
+ std::string sync_email =
identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSync)
.email;
@@ -384,7 +384,8 @@ void InlineSigninHelper::OnClientOAuthSuccessAndBrowserOpened(
PasswordReuseManagerFactory::GetForProfile(profile_);
if (reuse_manager) {
reuse_manager->SaveGaiaPasswordHash(
- primary_email, base::UTF8ToUTF16(password_), !primary_email.empty(),
+ sync_email, base::UTF8ToUTF16(password_),
+ /*is_sync_password_for_metrics=*/!sync_email.empty(),
password_manager::metrics_util::GaiaPasswordHashChange::
SAVED_ON_CHROME_SIGNIN);
}
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_ui.cc b/chromium/chrome/browser/ui/webui/signin/inline_login_ui.cc
index d6ba94c74f9..4875c099c1f 100644
--- a/chromium/chrome/browser/ui/webui/signin/inline_login_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_ui.cc
@@ -38,6 +38,7 @@
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "chrome/browser/ash/account_manager/account_apps_availability.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
@@ -45,7 +46,6 @@
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/webui/ash/edu_account_login_handler.h"
#include "chrome/browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/browser/ui/webui/signin/ash/inline_login_handler_impl.h"
#include "chrome/grit/arc_account_picker_resources.h"
#include "chrome/grit/arc_account_picker_resources_map.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_service.cc b/chromium/chrome/browser/ui/webui/signin/login_ui_service.cc
index b9b15687c55..143e26d8c4d 100644
--- a/chromium/chrome/browser/ui/webui/signin/login_ui_service.cc
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_service.cc
@@ -14,7 +14,7 @@
#include "chrome/common/url_constants.h"
#if !BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
LoginUIService::LoginUIService(Profile* profile)
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.cc b/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.cc
index 47e31511410..feace69e735 100644
--- a/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.cc
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.cc
@@ -36,9 +36,10 @@ LoginUIServiceFactory* LoginUIServiceFactory::GetInstance() {
return instance.get();
}
-KeyedService* LoginUIServiceFactory::BuildServiceInstanceFor(
- content::BrowserContext* profile) const {
- return new LoginUIService(static_cast<Profile*>(profile));
+std::unique_ptr<KeyedService>
+LoginUIServiceFactory::BuildServiceInstanceForBrowserContext(
+ content::BrowserContext* browser_context) const {
+ return std::make_unique<LoginUIService>(Profile::FromBrowserContext(browser_context));
}
bool LoginUIServiceFactory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.h b/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.h
index d67f8bdeab2..c8d4c9e0c46 100644
--- a/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.h
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.h
@@ -34,8 +34,8 @@ class LoginUIServiceFactory : public ProfileKeyedServiceFactory {
~LoginUIServiceFactory() override;
// BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
- content::BrowserContext* profile) const override;
+ std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+ content::BrowserContext* browser_context) const override;
bool ServiceIsCreatedWithBrowserContext() const override;
};
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.cc b/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
index 0c9ab8e43b5..565f3cf1660 100644
--- a/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -21,8 +21,8 @@
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/signin_modal_dialog.h"
-#include "chrome/browser/ui/signin_view_controller_delegate.h"
+#include "chrome/browser/ui/signin/signin_modal_dialog.h"
+#include "chrome/browser/ui/signin/signin_view_controller_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.cc b/chromium/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.cc
deleted file mode 100644
index 043268b8b06..00000000000
--- a/chromium/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h"
-
-#include "chrome/browser/new_tab_page/chrome_colors/chrome_colors_service.h"
-#include "chrome/browser/new_tab_page/chrome_colors/generated_colors_info.h"
-#include "chrome/common/themes/autogenerated_theme_util.h"
-#include "ui/base/l10n/l10n_util.h"
-
-ProfileCreationCustomizeThemesHandler::ProfileCreationCustomizeThemesHandler(
- mojo::PendingRemote<customize_themes::mojom::CustomizeThemesClient>
- pending_client,
- mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
- pending_handler)
- : remote_client_(std::move(pending_client)),
- receiver_(this, std::move(pending_handler)) {}
-
-ProfileCreationCustomizeThemesHandler::
- ~ProfileCreationCustomizeThemesHandler() = default;
-
-void ProfileCreationCustomizeThemesHandler::ApplyAutogeneratedTheme(
- SkColor frame_color) {
- auto theme = customize_themes::mojom::Theme::New();
- theme->type = customize_themes::mojom::ThemeType::kAutogenerated;
- auto theme_colors = customize_themes::mojom::ThemeColors::New();
- auto colors = GetAutogeneratedThemeColors(frame_color);
- theme_colors->frame = colors.frame_color;
- theme_colors->active_tab = colors.active_tab_color;
- theme_colors->active_tab_text = colors.active_tab_text_color;
- theme->info = customize_themes::mojom::ThemeInfo::NewAutogeneratedThemeColors(
- std::move(theme_colors));
- remote_client_->SetTheme(std::move(theme));
-}
-
-void ProfileCreationCustomizeThemesHandler::ApplyDefaultTheme() {
- auto theme = customize_themes::mojom::Theme::New();
- theme->type = customize_themes::mojom::ThemeType::kDefault;
- theme->info = customize_themes::mojom::ThemeInfo::NewChromeThemeId(
- chrome_colors::kDefaultColorId);
- remote_client_->SetTheme(std::move(theme));
-}
-
-void ProfileCreationCustomizeThemesHandler::ApplyChromeTheme(int32_t id) {
- auto theme = customize_themes::mojom::Theme::New();
- theme->type = customize_themes::mojom::ThemeType::kChrome;
- theme->info = customize_themes::mojom::ThemeInfo::NewChromeThemeId(id);
- remote_client_->SetTheme(std::move(theme));
-}
-
-void ProfileCreationCustomizeThemesHandler::InitializeTheme() {
- // Do nothing.
- // The profile picker initializes the theme from JavaScript.
-}
-
-void ProfileCreationCustomizeThemesHandler::GetChromeThemes(
- GetChromeThemesCallback callback) {
- std::vector<customize_themes::mojom::ChromeThemePtr> themes;
- for (const auto& color_info : chrome_colors::kGeneratedColorsInfo) {
- auto theme_colors = GetAutogeneratedThemeColors(color_info.color);
- auto theme = customize_themes::mojom::ChromeTheme::New();
- theme->id = color_info.id;
- theme->label = l10n_util::GetStringUTF8(color_info.label_id);
- auto colors = customize_themes::mojom::ThemeColors::New();
- colors->frame = theme_colors.frame_color;
- colors->active_tab = theme_colors.active_tab_color;
- colors->active_tab_text = theme_colors.active_tab_text_color;
- theme->colors = std::move(colors);
- themes.push_back(std::move(theme));
- }
- std::move(callback).Run(std::move(themes));
-}
-
-void ProfileCreationCustomizeThemesHandler::ConfirmThemeChanges() {}
-
-void ProfileCreationCustomizeThemesHandler::RevertThemeChanges() {}
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h b/chromium/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h
deleted file mode 100644
index 4d342a383d9..00000000000
--- a/chromium/chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_PROFILE_CREATION_CUSTOMIZE_THEMES_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_SIGNIN_PROFILE_CREATION_CUSTOMIZE_THEMES_HANDLER_H_
-
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
-
-class ProfileCreationCustomizeThemesHandler
- : public customize_themes::mojom::CustomizeThemesHandler {
- public:
- explicit ProfileCreationCustomizeThemesHandler(
- mojo::PendingRemote<customize_themes::mojom::CustomizeThemesClient>
- pending_client,
- mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
- pending_handler);
- ~ProfileCreationCustomizeThemesHandler() override;
-
- // customize_themes::mojom::CustomizeThemesHandler:
- void ApplyAutogeneratedTheme(SkColor frame_color) override;
- void ApplyDefaultTheme() override;
- void ApplyChromeTheme(int32_t id) override;
- void InitializeTheme() override;
- void GetChromeThemes(GetChromeThemesCallback callback) override;
- void ConfirmThemeChanges() override;
- void RevertThemeChanges() override;
-
- private:
- mojo::Remote<customize_themes::mojom::CustomizeThemesClient> remote_client_;
- mojo::Receiver<customize_themes::mojom::CustomizeThemesHandler> receiver_;
-};
-
-#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_PROFILE_CREATION_CUSTOMIZE_THEMES_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_customization_handler.cc b/chromium/chrome/browser/ui/webui/signin/profile_customization_handler.cc
index a8b09bfe8f8..7c1d2aaa85f 100644
--- a/chromium/chrome/browser/ui/webui/signin/profile_customization_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/profile_customization_handler.cc
@@ -18,9 +18,9 @@
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/profile_picker.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
-#include "chrome/browser/ui/signin/profile_customization_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_customization_util.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_customization_handler.h b/chromium/chrome/browser/ui/webui/signin/profile_customization_handler.h
index 7d1581f7b2a..a01bc2aebda 100644
--- a/chromium/chrome/browser/ui/webui/signin/profile_customization_handler.h
+++ b/chromium/chrome/browser/ui/webui/signin/profile_customization_handler.h
@@ -52,7 +52,7 @@ class ProfileCustomizationHandler : public content::WebUIMessageHandler,
const std::u16string& old_profile_name) override;
private:
- friend class ProfilePickerLocalProfileCreationDialogBrowserTest;
+ friend class ProfilePickerCreationFlowBrowserTest;
// Handlers for messages from javascript.
void HandleInitialized(const base::Value::List& args);
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_customization_ui.cc b/chromium/chrome/browser/ui/webui/signin/profile_customization_ui.cc
index 6d8ec69fa73..e148a370e22 100644
--- a/chromium/chrome/browser/ui/webui/signin/profile_customization_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin/profile_customization_ui.cc
@@ -12,7 +12,9 @@
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/search/background/ntp_custom_background_service_factory.h"
#include "chrome/browser/signin/signin_features.h"
+#include "chrome/browser/ui/webui/cr_components/theme_color_picker/theme_color_picker_handler.h"
#include "chrome/browser/ui/webui/customize_themes/chrome_customize_themes_handler.h"
#include "chrome/browser/ui/webui/signin/profile_customization_handler.h"
#include "chrome/browser/ui/webui/signin/signin_url_utils.h"
@@ -83,8 +85,17 @@ ProfileCustomizationUI::ProfileCustomizationUI(content::WebUI* web_ui)
IDS_PROFILE_CUSTOMIZATION_AVATAR_SELECTION_BACK_BUTTON_LABEL},
// Color picker strings:
+ {"close", IDS_CLOSE},
{"colorPickerLabel", IDS_NTP_CUSTOMIZE_COLOR_PICKER_LABEL},
+ {"colorsContainerLabel", IDS_NTP_THEMES_CONTAINER_LABEL},
+ {"currentTheme", IDS_NTP_CUSTOMIZE_CHROME_CURRENT_THEME_LABEL},
+ {"defaultColorName", IDS_NTP_CUSTOMIZE_DEFAULT_LABEL},
{"defaultThemeLabel", IDS_NTP_CUSTOMIZE_DEFAULT_LABEL},
+ {"hueSliderTitle", IDS_NTP_CUSTOMIZE_COLOR_HUE_SLIDER_TITLE},
+ {"greyDefaultColorName", IDS_NTP_CUSTOMIZE_GREY_DEFAULT_LABEL},
+ {"mainColorName", IDS_NTP_CUSTOMIZE_MAIN_COLOR_LABEL},
+ {"managedColorsBody", IDS_NTP_THEME_MANAGED_DIALOG_BODY},
+ {"managedColorsTitle", IDS_NTP_THEME_MANAGED_DIALOG_TITLE},
{"themesContainerLabel",
IDS_PROFILE_CUSTOMIZATION_THEMES_CONTAINER_LABEL},
{"thirdPartyThemeDescription", IDS_NTP_CUSTOMIZE_3PT_THEME_DESC},
@@ -99,9 +110,6 @@ ProfileCustomizationUI::ProfileCustomizationUI(content::WebUI* web_ui)
.GetProfileAttributesWithPath(profile->GetPath());
source->AddString("profileName",
base::UTF16ToUTF8(entry->GetLocalProfileName()));
- source->AddBoolean(
- "profileCustomizationInDialogDesign",
- base::FeatureList::IsEnabled(kSyncPromoAfterSigninIntercept));
const GURL& url = web_ui->GetWebContents()->GetVisibleURL();
source->AddBoolean("isLocalProfileCreation",
GetProfileCustomizationStyle(url) ==
@@ -136,6 +144,17 @@ void ProfileCustomizationUI::BindInterface(
customize_themes_factory_receiver_.Bind(std::move(pending_receiver));
}
+void ProfileCustomizationUI::BindInterface(
+ mojo::PendingReceiver<
+ theme_color_picker::mojom::ThemeColorPickerHandlerFactory>
+ pending_receiver) {
+ if (theme_color_picker_handler_factory_receiver_.is_bound()) {
+ theme_color_picker_handler_factory_receiver_.reset();
+ }
+ theme_color_picker_handler_factory_receiver_.Bind(
+ std::move(pending_receiver));
+}
+
ProfileCustomizationHandler*
ProfileCustomizationUI::GetProfileCustomizationHandlerForTesting() {
return profile_customization_handler_;
@@ -151,4 +170,16 @@ void ProfileCustomizationUI::CreateCustomizeThemesHandler(
web_ui()->GetWebContents(), Profile::FromWebUI(web_ui()));
}
+void ProfileCustomizationUI::CreateThemeColorPickerHandler(
+ mojo::PendingReceiver<theme_color_picker::mojom::ThemeColorPickerHandler>
+ handler,
+ mojo::PendingRemote<theme_color_picker::mojom::ThemeColorPickerClient>
+ client) {
+ theme_color_picker_handler_ = std::make_unique<ThemeColorPickerHandler>(
+ std::move(handler), std::move(client),
+ NtpCustomBackgroundServiceFactory::GetForProfile(
+ Profile::FromWebUI(web_ui())),
+ web_ui()->GetWebContents());
+}
+
WEB_UI_CONTROLLER_TYPE_IMPL(ProfileCustomizationUI)
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_customization_ui.h b/chromium/chrome/browser/ui/webui/signin/profile_customization_ui.h
index 0b7ce1dcb43..2b92ecbb4a0 100644
--- a/chromium/chrome/browser/ui/webui/signin/profile_customization_ui.h
+++ b/chromium/chrome/browser/ui/webui/signin/profile_customization_ui.h
@@ -12,17 +12,20 @@
#include "mojo/public/cpp/bindings/receiver.h"
#include "ui/webui/mojo_web_ui_controller.h"
#include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
+#include "ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.mojom.h"
namespace content {
class WebUI;
}
class ChromeCustomizeThemesHandler;
+class ThemeColorPickerHandler;
// This WebUI uses mojo for the color picker element.
class ProfileCustomizationUI
: public ui::MojoWebUIController,
- public customize_themes::mojom::CustomizeThemesHandlerFactory {
+ public customize_themes::mojom::CustomizeThemesHandlerFactory,
+ public theme_color_picker::mojom::ThemeColorPickerHandlerFactory {
public:
static constexpr int kPreferredHeight = 560;
static constexpr int kPreferredWidth = 512;
@@ -45,6 +48,13 @@ class ProfileCustomizationUI
customize_themes::mojom::CustomizeThemesHandlerFactory>
pending_receiver);
+ // Instantiates the implementor of the
+ // theme_color_picker::mojom::ThemeColorPickerHandlerFactory mojo interface
+ // passing the pending receiver that will be internally bound.
+ void BindInterface(mojo::PendingReceiver<
+ theme_color_picker::mojom::ThemeColorPickerHandlerFactory>
+ pending_receiver);
+
// Allows tests to trigger page events.
ProfileCustomizationHandler* GetProfileCustomizationHandlerForTesting();
@@ -56,9 +66,19 @@ class ProfileCustomizationUI
mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
pending_handler) override;
+ // theme_color_picker::mojom::ThemeColorPickerHandlerFactory:
+ void CreateThemeColorPickerHandler(
+ mojo::PendingReceiver<theme_color_picker::mojom::ThemeColorPickerHandler>
+ handler,
+ mojo::PendingRemote<theme_color_picker::mojom::ThemeColorPickerClient>
+ client) override;
+
std::unique_ptr<ChromeCustomizeThemesHandler> customize_themes_handler_;
mojo::Receiver<customize_themes::mojom::CustomizeThemesHandlerFactory>
customize_themes_factory_receiver_;
+ std::unique_ptr<ThemeColorPickerHandler> theme_color_picker_handler_;
+ mojo::Receiver<theme_color_picker::mojom::ThemeColorPickerHandlerFactory>
+ theme_color_picker_handler_factory_receiver_{this};
// Stored for tests.
raw_ptr<ProfileCustomizationHandler> profile_customization_handler_ = nullptr;
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chromium/chrome/browser/ui/webui/signin/profile_picker_handler.cc
index b696d05e2ef..1591702986d 100644
--- a/chromium/chrome/browser/ui/webui/signin/profile_picker_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/signin/profile_picker_handler.h"
#include "base/check.h"
+#include "base/check_op.h"
#include "base/containers/cxx20_erase.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
@@ -40,8 +41,8 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
-#include "chrome/browser/ui/profile_picker.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/webui/profile_helper.h"
@@ -59,6 +60,8 @@
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/mojom/themes.mojom.h"
+#include "ui/base/ui_base_features.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
@@ -66,11 +69,11 @@
#include "ui/gfx/image/image.h"
#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "chrome/browser/lacros/account_manager/account_manager_util.h"
#include "chrome/browser/lacros/account_manager/account_profile_mapper.h"
#include "chrome/browser/lacros/identity_manager_lacros.h"
#include "chrome/browser/lacros/lacros_url_handling.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chromeos/crosapi/mojom/login.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#include "components/account_manager_core/account.h"
@@ -416,10 +419,6 @@ void ProfilePickerHandler::RegisterMessages() {
base::BindRepeating(&ProfilePickerHandler::HandleGetAvailableIcons,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
- "createProfile",
- base::BindRepeating(&ProfilePickerHandler::HandleCreateProfile,
- base::Unretained(this)));
- web_ui()->RegisterMessageCallback(
"createProfileAndOpenCustomizationDialog",
base::BindRepeating(
&ProfilePickerHandler::HandleCreateProfileAndOpenCustomizationDialog,
@@ -445,6 +444,10 @@ void ProfilePickerHandler::RegisterMessages() {
base::BindRepeating(
&ProfilePickerHandler::HandleRecordSignInPromoImpression,
base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "updateProfileOrder",
+ base::BindRepeating(&ProfilePickerHandler::HandleUpdateProfileOrder,
+ base::Unretained(this)));
#if BUILDFLAG(IS_CHROMEOS_LACROS)
web_ui()->RegisterMessageCallback(
"getAvailableAccounts",
@@ -684,35 +687,9 @@ void ProfilePickerHandler::HandleGetAvailableIcons(
profiles::GetCustomProfileAvatarIconsAndLabels());
}
-void ProfilePickerHandler::HandleCreateProfile(const base::Value::List& args) {
- CHECK_EQ(4U, args.size());
- std::u16string profile_name = base::UTF8ToUTF16(args[0].GetString());
- // profileColor is undefined for the default theme.
- absl::optional<SkColor> profile_color;
- if (args[1].is_int())
- profile_color = args[1].GetInt();
- size_t avatar_index = args[2].GetInt();
- bool create_shortcut = args[3].GetBool();
- base::TrimWhitespace(profile_name, base::TRIM_ALL, &profile_name);
- CHECK(!profile_name.empty());
-
-#ifndef NDEBUG
- DCHECK(profiles::IsDefaultAvatarIconIndex(avatar_index));
-#endif
-
- ProfileMetrics::LogProfileAddNewUser(
- ProfileMetrics::ADD_NEW_PROFILE_PICKER_LOCAL);
- ProfileManager::CreateMultiProfileAsync(
- profile_name, avatar_index, /*is_hidden=*/false,
- base::BindOnce(&ProfilePickerHandler::OnProfileInitialized,
- weak_factory_.GetWeakPtr(), profile_color,
- create_shortcut));
-}
-
void ProfilePickerHandler::HandleCreateProfileAndOpenCustomizationDialog(
const base::Value::List& args) {
CHECK_EQ(1U, args.size());
- DCHECK(base::FeatureList::IsEnabled(kSyncPromoAfterSigninIntercept));
// profileColor is undefined for the default theme.
absl::optional<SkColor> profile_color;
@@ -773,51 +750,6 @@ void ProfilePickerHandler::HandleCancelProfileSwitch(
ProfilePicker::CancelSignedInFlow();
}
-void ProfilePickerHandler::OnProfileInitialized(
- absl::optional<SkColor> profile_color,
- bool create_shortcut,
- Profile* profile) {
- if (!profile) {
- NOTREACHED() << "Local fail in creating new profile";
- FireWebUIListener("create-profile-finished", base::Value());
- return;
- }
- DCHECK(!signin_util::IsForceSigninEnabled());
-
- // Apply a new color to the profile or use the default theme.
- auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
- if (profile_color.has_value())
- theme_service->BuildAutogeneratedThemeFromColor(*profile_color);
- else
- theme_service->UseDefaultTheme();
-
- // Create shortcut if needed.
- if (create_shortcut) {
- DCHECK(ProfileShortcutManager::IsFeatureEnabled());
- ProfileShortcutManager* shortcut_manager =
- g_browser_process->profile_manager()->profile_shortcut_manager();
- DCHECK(shortcut_manager);
- if (shortcut_manager)
- shortcut_manager->CreateProfileShortcut(profile->GetPath());
- }
-
- // Skip the FRE for this profile as sign-in was offered as part of the flow.
- profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, true);
-
- // Launch profile and close the picker.
- profiles::OpenBrowserWindowForProfile(
- base::BindOnce(&ProfilePickerHandler::OnSwitchToProfileComplete,
- weak_factory_.GetWeakPtr(), true, false),
- false, // Don't create a window if one already exists.
- true, // Create a first run window.
- false, // There is no need to unblock all extensions because we only open
- // browser window if the Profile is not locked. Hence there is no
- // extension blocked.
- profile);
-
- FireWebUIListener("create-profile-finished", base::Value());
-}
-
void ProfilePickerHandler::OnLocalProfileInitialized(
absl::optional<SkColor> profile_color,
Profile* profile) {
@@ -830,10 +762,16 @@ void ProfilePickerHandler::OnLocalProfileInitialized(
// Apply a new color to the profile or use the default theme.
auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
- if (profile_color.has_value())
- theme_service->BuildAutogeneratedThemeFromColor(*profile_color);
- else
+ if (profile_color.has_value()) {
+ if (features::IsChromeWebuiRefresh2023()) {
+ theme_service->SetUserColorAndBrowserColorVariant(
+ *profile_color, ui::mojom::BrowserColorVariant::kTonalSpot);
+ } else {
+ theme_service->BuildAutogeneratedThemeFromColor(*profile_color);
+ }
+ } else {
theme_service->UseDefaultTheme();
+ }
// TODO(https://crbug.com/1282157): Add shortcut creation.
@@ -907,6 +845,21 @@ void ProfilePickerHandler::HandleRemoveProfile(const base::Value::List& args) {
profile_statistics_keep_alive_.reset();
}
+void ProfilePickerHandler::HandleUpdateProfileOrder(
+ const base::Value::List& args) {
+ CHECK_EQ(2U, args.size());
+ CHECK(args[0].is_int());
+ CHECK(args[1].is_int());
+
+ int from_index = args[0].GetInt();
+ int to_index = args[1].GetInt();
+ CHECK(from_index >= 0 && to_index >= 0);
+
+ g_browser_process->profile_manager()
+ ->GetProfileAttributesStorage()
+ .UpdateProfilesOrderPref(from_index, to_index);
+}
+
void ProfilePickerHandler::HandleCloseProfileStatistics(
const base::Value::List& args) {
CHECK_EQ(0U, args.size());
@@ -1113,7 +1066,7 @@ ProfilePickerHandler::GetProfileAttributes() {
std::vector<ProfileAttributesEntry*> ordered_entries =
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
- .GetAllProfilesAttributesSortedByLocalProfileName();
+ .GetAllProfilesAttributesSortedByLocalProfileNameWithCheck();
base::EraseIf(ordered_entries, [](const ProfileAttributesEntry* entry) {
return entry->IsOmitted();
});
@@ -1210,8 +1163,9 @@ void ProfilePickerHandler::OnProfileIsOmittedChanged(
.GetProfileAttributesWithPath(profile_path);
CHECK(entry);
if (entry->IsOmitted()) {
- if (RemoveProfileFromList(profile_path))
- PushProfilesList();
+ if (RemoveProfileFromList(profile_path)) {
+ FireWebUIListener("profile-removed", base::FilePathToValue(profile_path));
+ }
} else {
AddProfileToList(profile_path);
PushProfilesList();
@@ -1244,7 +1198,7 @@ void ProfilePickerHandler::DidFirstVisuallyNonEmptyPaint() {
auto now = base::TimeTicks::Now();
base::UmaHistogramTimes("ProfilePicker.StartupTime.FirstPaint",
now - creation_time_on_startup_);
- startup_metric_utils::RecordExternalStartupMetric(
+ startup_metric_utils::GetBrowser().RecordExternalStartupMetric(
"ProfilePicker.StartupTime.FirstPaint.FromApplicationStart", now,
/*set_non_browser_ui_displayed=*/true);
// Stop observing so that the histogram is only recorded once.
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_picker_handler.h b/chromium/chrome/browser/ui/webui/signin/profile_picker_handler.h
index 97d3ebd267e..6739eaa0225 100644
--- a/chromium/chrome/browser/ui/webui/signin/profile_picker_handler.h
+++ b/chromium/chrome/browser/ui/webui/signin/profile_picker_handler.h
@@ -68,7 +68,6 @@ class ProfilePickerHandler : public content::WebUIMessageHandler,
friend class ProfilePickerHandlerInUserProfileTest;
friend class ProfilePickerCreationFlowBrowserTest;
friend class ProfilePickerEnterpriseCreationFlowBrowserTest;
- friend class ProfilePickerLocalProfileCreationDialogBrowserTest;
friend class StartupBrowserCreatorPickerInfobarTest;
FRIEND_TEST_ALL_PREFIXES(ProfilePickerHandlerInUserProfileTest,
HandleExtendedAccountInformation);
@@ -90,6 +89,7 @@ class ProfilePickerHandler : public content::WebUIMessageHandler,
void HandleGetProfileStatistics(const base::Value::List& args);
void HandleCloseProfileStatistics(const base::Value::List& args);
void HandleSetProfileName(const base::Value::List& args);
+ void HandleUpdateProfileOrder(const base::Value::List& args);
void HandleSelectNewAccount(const base::Value::List& args);
#if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -99,7 +99,6 @@ class ProfilePickerHandler : public content::WebUIMessageHandler,
void HandleGetNewProfileSuggestedThemeInfo(const base::Value::List& args);
void HandleGetProfileThemeInfo(const base::Value::List& args);
void HandleGetAvailableIcons(const base::Value::List& args);
- void HandleCreateProfile(const base::Value::List& args);
// This function creates a new local profile and opens the profile
// customization in a modal dialog.
void HandleCreateProfileAndOpenCustomizationDialog(
@@ -121,9 +120,6 @@ class ProfilePickerHandler : public content::WebUIMessageHandler,
bool open_settings,
Browser* browser);
void OnSwitchToProfileCompleteOpenCustomization(Browser* browser);
- void OnProfileInitialized(absl::optional<SkColor> profile_color,
- bool create_shortcut,
- Profile* profile);
void OnLocalProfileInitialized(absl::optional<SkColor> profile_color,
Profile* profile);
void PushProfilesList();
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_picker_handler_unittest.cc b/chromium/chrome/browser/ui/webui/signin/profile_picker_handler_unittest.cc
index c47fe732254..dd5d05ed8f7 100644
--- a/chromium/chrome/browser/ui/webui/signin/profile_picker_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/signin/profile_picker_handler_unittest.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/signin/profile_picker_handler.h"
#include <memory>
+#include <string>
#include <vector>
#include "base/json/values_util.h"
@@ -14,10 +15,11 @@
#include "build/buildflag.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
-#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/test/base/profile_waiter.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
@@ -340,7 +342,7 @@ TEST_F(ProfilePickerHandlerTest, MarkProfileAsOmitted) {
profile_b->SetIsEphemeral(true);
profile_b->SetIsOmitted(true);
- VerifyProfileListWasPushed({profile_a, profile_c, profile_d});
+ VerifyProfileWasRemoved(profile_b->GetPath());
web_ui()->ClearTrackedCalls();
// Omitted profile is appended to the end of the profile list.
@@ -899,3 +901,51 @@ TEST_F(ProfilePickerHandlerInUserProfileTest,
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+TEST_F(ProfilePickerHandlerTest, UpdateProfileOrder) {
+ auto entries_to_names =
+ [](const std::vector<ProfileAttributesEntry*> entries) {
+ std::vector<std::string> names;
+ for (auto* entry : entries) {
+ names.emplace_back(base::UTF16ToUTF8(entry->GetLocalProfileName()));
+ }
+ return names;
+ };
+
+ std::vector<std::string> profile_names = {"A", "B", "C", "D"};
+ for (auto name : profile_names) {
+ CreateTestingProfile(name);
+ }
+
+ ProfileAttributesStorage* storage =
+ profile_manager()->profile_attributes_storage();
+ std::vector<ProfileAttributesEntry*> display_entries =
+ storage->GetAllProfilesAttributesSortedForDisplay();
+ ASSERT_EQ(entries_to_names(display_entries), profile_names);
+
+ // Perform first changes.
+ {
+ base::Value::List args;
+ args.Append(0); // `from_index`
+ args.Append(2); // `to_index`
+ web_ui()->HandleReceivedMessage("updateProfileOrder", args);
+
+ std::vector<std::string> expected_profile_order_names{"B", "C", "A", "D"};
+ EXPECT_EQ(
+ entries_to_names(storage->GetAllProfilesAttributesSortedForDisplay()),
+ expected_profile_order_names);
+ }
+
+ // Perform second changes.
+ {
+ base::Value::List args;
+ args.Append(1); // `from_index`
+ args.Append(3); // `to_index`
+ web_ui()->HandleReceivedMessage("updateProfileOrder", args);
+
+ std::vector<std::string> expected_profile_order_names{"B", "A", "D", "C"};
+ EXPECT_EQ(
+ entries_to_names(storage->GetAllProfilesAttributesSortedForDisplay()),
+ expected_profile_order_names);
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_picker_ui.cc b/chromium/chrome/browser/ui/webui/signin/profile_picker_ui.cc
index 4a08d1df94d..2ccbbdd4a2e 100644
--- a/chromium/chrome/browser/ui/webui/signin/profile_picker_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin/profile_picker_ui.cc
@@ -19,8 +19,7 @@
#include "chrome/browser/signin/signin_features.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/managed_ui.h"
-#include "chrome/browser/ui/profile_picker.h"
-#include "chrome/browser/ui/webui/signin/profile_creation_customize_themes_handler.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/browser/ui/webui/signin/profile_picker_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/pref_names.h"
@@ -43,7 +42,6 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/webui/web_ui_util.h"
-#include "ui/webui/mojo_web_ui_controller.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -157,36 +155,11 @@ void AddStrings(content::WebUIDataSource* html_source) {
},
{"notNowButtonLabel",
IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_NOT_NOW_BUTTON_LABEL},
- {"localProfileCreationTitle",
- IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_TITLE},
- {"localProfileCreationCustomizeAvatarLabel",
- IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_CUSTOMIZE_AVATAR_BUTTON_LABEL},
- {"localProfileCreationThemeText",
- IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_THEME_TEXT},
- {"createProfileNamePlaceholder",
- IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_INPUT_NAME},
- {"createDesktopShortcutLabel",
- IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_SHORTCUT_TEXT},
- {"createProfileConfirm",
- IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_DONE},
- {"defaultAvatarLabel", IDS_DEFAULT_AVATAR_LABEL_26},
- {"selectAnAvatarDialogTitle",
- IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_AVATAR_TEXT},
- {"selectAvatarDoneButtonLabel",
- IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_AVATAR_DONE},
{"profileSwitchTitle", IDS_PROFILE_PICKER_PROFILE_SWITCH_TITLE},
{"profileSwitchSubtitle", IDS_PROFILE_PICKER_PROFILE_SWITCH_SUBTITLE},
{"switchButtonLabel",
IDS_PROFILE_PICKER_PROFILE_SWITCH_SWITCH_BUTTON_LABEL},
- // Color picker.
- {"colorPickerLabel", IDS_NTP_CUSTOMIZE_COLOR_PICKER_LABEL},
- {"defaultThemeLabel", IDS_NTP_CUSTOMIZE_DEFAULT_LABEL},
- {"themesContainerLabel",
- IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_LOCAL_PROFILE_CREATION_THEME_TEXT},
- {"thirdPartyThemeDescription", IDS_NTP_CUSTOMIZE_3PT_THEME_DESC},
- {"uninstallThirdPartyThemeButton", IDS_NTP_CUSTOMIZE_3PT_THEME_UNINSTALL},
-
#if BUILDFLAG(IS_CHROMEOS_LACROS)
{"accountSelectionLacrosTitle",
IDS_PROFILE_PICKER_PROFILE_CREATION_FLOW_ACCOUNT_SELECTION_LACROS_TITLE},
@@ -222,6 +195,8 @@ void AddStrings(content::WebUIDataSource* html_source) {
html_source->AddBoolean("askOnStartup",
g_browser_process->local_state()->GetBoolean(
prefs::kBrowserShowProfilePickerOnStartup));
+ html_source->AddBoolean("profilesReorderingEnabled",
+ base::FeatureList::IsEnabled(kProfilesReordering));
html_source->AddBoolean("signInProfileCreationFlowSupported",
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
AccountConsistencyModeManager::IsDiceSignInAllowed());
@@ -277,9 +252,6 @@ void AddStrings(content::WebUIDataSource* html_source) {
html_source->AddBoolean("profileShortcutsEnabled",
ProfileShortcutManager::IsFeatureEnabled());
html_source->AddBoolean("isAskOnStartupAllowed", ask_on_startup_allowed);
- html_source->AddBoolean(
- "isLocalProfileCreationDialogEnabled",
- base::FeatureList::IsEnabled(kSyncPromoAfterSigninIntercept));
html_source->AddResourcePath("images/left_banner.svg",
IDR_SIGNIN_IMAGES_SHARED_LEFT_BANNER_SVG);
@@ -294,8 +266,7 @@ void AddStrings(content::WebUIDataSource* html_source) {
} // namespace
ProfilePickerUI::ProfilePickerUI(content::WebUI* web_ui)
- : ui::MojoWebUIController(web_ui, /*enable_chrome_send=*/true),
- customize_themes_factory_receiver_(this) {
+ : content::WebUIController(web_ui) {
Profile* profile = Profile::FromWebUI(web_ui);
content::WebUIDataSource* html_source =
content::WebUIDataSource::CreateAndAdd(
@@ -333,28 +304,8 @@ gfx::Size ProfilePickerUI::GetMinimumSize() {
return gfx::Size(kMinimumPickerSizePx, kMinimumPickerSizePx);
}
-void ProfilePickerUI::BindInterface(
- mojo::PendingReceiver<
- customize_themes::mojom::CustomizeThemesHandlerFactory>
- pending_receiver) {
- if (customize_themes_factory_receiver_.is_bound()) {
- customize_themes_factory_receiver_.reset();
- }
- customize_themes_factory_receiver_.Bind(std::move(pending_receiver));
-}
-
ProfilePickerHandler* ProfilePickerUI::GetProfilePickerHandlerForTesting() {
return profile_picker_handler_;
}
-void ProfilePickerUI::CreateCustomizeThemesHandler(
- mojo::PendingRemote<customize_themes::mojom::CustomizeThemesClient>
- pending_client,
- mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
- pending_handler) {
- customize_themes_handler_ =
- std::make_unique<ProfileCreationCustomizeThemesHandler>(
- std::move(pending_client), std::move(pending_handler));
-}
-
WEB_UI_CONTROLLER_TYPE_IMPL(ProfilePickerUI)
diff --git a/chromium/chrome/browser/ui/webui/signin/profile_picker_ui.h b/chromium/chrome/browser/ui/webui/signin/profile_picker_ui.h
index 21e7ea8fb85..e4d2854c4fb 100644
--- a/chromium/chrome/browser/ui/webui/signin/profile_picker_ui.h
+++ b/chromium/chrome/browser/ui/webui/signin/profile_picker_ui.h
@@ -7,18 +7,12 @@
#include "base/memory/raw_ptr.h"
#include "content/public/browser/web_ui_controller.h"
-#include "mojo/public/cpp/bindings/receiver.h"
#include "ui/gfx/geometry/size.h"
-#include "ui/webui/mojo_web_ui_controller.h"
-#include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
-class ProfileCreationCustomizeThemesHandler;
class ProfilePickerHandler;
// The WebUI controller for chrome://profile-picker/.
-class ProfilePickerUI
- : public ui::MojoWebUIController,
- public customize_themes::mojom::CustomizeThemesHandlerFactory {
+class ProfilePickerUI : public content::WebUIController {
public:
explicit ProfilePickerUI(content::WebUI* web_ui);
~ProfilePickerUI() override;
@@ -29,29 +23,10 @@ class ProfilePickerUI
// Get the minimum size for the picker UI.
static gfx::Size GetMinimumSize();
- // Instantiates the implementor of the
- // customize_themes::mojom::CustomizeThemesHandlerFactory mojo interface
- // passing the pending receiver that will be internally bound.
- void BindInterface(mojo::PendingReceiver<
- customize_themes::mojom::CustomizeThemesHandlerFactory>
- pending_receiver);
-
// Allows tests to trigger page events.
ProfilePickerHandler* GetProfilePickerHandlerForTesting();
private:
- // customize_themes::mojom::CustomizeThemesHandlerFactory:
- void CreateCustomizeThemesHandler(
- mojo::PendingRemote<customize_themes::mojom::CustomizeThemesClient>
- pending_client,
- mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
- pending_handler) override;
-
- std::unique_ptr<ProfileCreationCustomizeThemesHandler>
- customize_themes_handler_;
- mojo::Receiver<customize_themes::mojom::CustomizeThemesHandlerFactory>
- customize_themes_factory_receiver_;
-
// Stored for tests.
raw_ptr<ProfilePickerHandler> profile_picker_handler_ = nullptr;
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h
index d1b9916caf8..ade495d5cee 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h
+++ b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h
@@ -11,7 +11,7 @@
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
-#include "chrome/browser/ui/signin_view_controller_delegate.h"
+#include "chrome/browser/ui/signin/signin_view_controller_delegate.h"
#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
#include "ui/web_dialogs/web_dialog_delegate.h"
#include "ui/web_dialogs/web_dialog_ui.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_error_handler.cc b/chromium/chrome/browser/ui/webui/signin/signin_error_handler.cc
index 4c4ae9a71e7..3afff5b8d1a 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_error_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/signin_error_handler.cc
@@ -9,7 +9,7 @@
#include "chrome/browser/signin/signin_ui_util.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/browser/ui/webui/signin/signin_utils.h"
#include "content/public/browser/web_ui.h"
#include "url/gurl.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc b/chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc
index 9938d6d981c..ecb2a632db8 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc
@@ -18,7 +18,7 @@
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/signin_ui_util.h"
#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/browser/ui/webui/signin/signin_error_handler.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.cc b/chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.cc
index 243721a34af..83f990c935c 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.cc
@@ -8,7 +8,7 @@
#include "base/functional/bind.h"
#include "base/logging.h"
-#include "chrome/browser/ui/signin_reauth_view_controller.h"
+#include "chrome/browser/ui/signin/signin_reauth_view_controller.h"
#include "components/sync/protocol/user_consent_types.pb.h"
#include "content/public/browser/web_ui.h"
#include "ui/base/webui/web_ui_util.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.h b/chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.h
index abf88837e32..6cf814e2c8f 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.h
+++ b/chromium/chrome/browser/ui/webui/signin/signin_reauth_handler.h
@@ -8,7 +8,7 @@
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
-#include "chrome/browser/ui/signin_reauth_view_controller.h"
+#include "chrome/browser/ui/signin/signin_reauth_view_controller.h"
#include "content/public/browser/web_ui_message_handler.h"
// WebUI message handler for the signin reauth dialog.
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_reauth_ui.cc b/chromium/chrome/browser/ui/webui/signin/signin_reauth_ui.cc
index d661ac6eb41..0a23fd51419 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_reauth_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin/signin_reauth_ui.cc
@@ -8,11 +8,12 @@
#include "base/check.h"
#include "base/containers/flat_map.h"
+#include "base/feature_list.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/ui/signin_reauth_view_controller.h"
+#include "chrome/browser/ui/signin/signin_reauth_view_controller.h"
#include "chrome/browser/ui/webui/signin/signin_reauth_handler.h"
#include "chrome/browser/ui/webui/signin/signin_url_utils.h"
#include "chrome/browser/ui/webui/webui_util.h"
@@ -23,6 +24,7 @@
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/sync/base/features.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_data_source.h"
@@ -74,10 +76,15 @@ bool WasPasswordSavedLocally(signin_metrics::ReauthAccessPoint access_point) {
int GetReauthDescriptionStringId(
signin_metrics::ReauthAccessPoint access_point) {
+ bool sync_passkeys =
+ base::FeatureList::IsEnabled(syncer::kSyncWebauthnCredentials);
if (WasPasswordSavedLocally(access_point)) {
- return IDS_ACCOUNT_PASSWORDS_REAUTH_DESC_ALREADY_SAVED_LOCALLY;
+ return sync_passkeys
+ ? IDS_ACCOUNT_PASSWORDS_WITH_PASSKEYS_REAUTH_DESC_ALREADY_SAVED_LOCALLY
+ : IDS_ACCOUNT_PASSWORDS_REAUTH_DESC_ALREADY_SAVED_LOCALLY;
}
- return IDS_ACCOUNT_PASSWORDS_REAUTH_DESC;
+ return sync_passkeys ? IDS_ACCOUNT_PASSWORDS_WITH_PASSKEYS_REAUTH_DESC
+ : IDS_ACCOUNT_PASSWORDS_REAUTH_DESC;
}
int GetReauthCloseButtonLabelStringId(
@@ -122,8 +129,11 @@ SigninReauthUI::SigninReauthUI(content::WebUI* web_ui)
GetReauthAccessPointForReauthConfirmationURL(
web_ui->GetWebContents()->GetVisibleURL());
- AddStringResource(source, "signinReauthTitle",
- IDS_ACCOUNT_PASSWORDS_REAUTH_TITLE);
+ AddStringResource(
+ source, "signinReauthTitle",
+ base::FeatureList::IsEnabled(syncer::kSyncWebauthnCredentials)
+ ? IDS_ACCOUNT_PASSWORDS_WITH_PASSKEYS_REAUTH_TITLE
+ : IDS_ACCOUNT_PASSWORDS_REAUTH_TITLE);
AddStringResource(source, "signinReauthDesc",
GetReauthDescriptionStringId(access_point));
AddStringResource(source, "signinReauthConfirmLabel",
diff --git a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
index a904de5a151..a37d79c9059 100644
--- a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
@@ -17,7 +17,7 @@
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/signin_view_controller_delegate.h"
+#include "chrome/browser/ui/signin/signin_view_controller_delegate.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/browser/ui/webui/signin/signin_utils.h"
#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
@@ -31,8 +31,8 @@
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "chrome/browser/lacros/lacros_url_handling.h"
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/common/webui_url_constants.h"
#include "components/sync/base/features.h"
#endif
diff --git a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
index baf5bf90ac5..173d95088ce 100644
--- a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
@@ -25,7 +25,7 @@
#include "chrome/browser/signin/signin_features.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "chrome/browser/ui/webui/signin/signin_url_utils.h"
#include "chrome/browser/ui/webui/signin/sync_confirmation_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
@@ -53,10 +53,10 @@
#include "ui/resources/grit/webui_resources.h"
#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
+#include "ash/webui/settings/public/constants/routes.mojom.h"
#include "chrome/common/webui_url_constants.h"
#include "components/sync/base/features.h"
-#include "components/sync/base/sync_prefs.h"
+#include "components/sync/service/sync_prefs.h"
#endif
namespace {
@@ -271,7 +271,6 @@ void SyncConfirmationUI::InitializeForSyncConfirmation(
}
#endif
if (is_signin_intercept_promo) {
- DCHECK(base::FeatureList::IsEnabled(kSyncPromoAfterSigninIntercept));
info_title_id =
IDS_SYNC_CONFIRMATION_TANGIBLE_SYNC_INFO_TITLE_SIGNIN_INTERCEPT_V2;
confirm_label_id = IDS_SYNC_CONFIRMATION_TURN_ON_SYNC_BUTTON_LABEL;
diff --git a/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.cc b/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.cc
index 5bc2498d0b9..87b7fc373b6 100644
--- a/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.cc
+++ b/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.cc
@@ -24,13 +24,14 @@
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/signin/profile_colors_util.h"
+#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/signin/enterprise_profile_welcome_ui.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h"
#include "chrome/browser/ui/webui/signin/signin_ui_error.h"
#include "chrome/common/url_constants.h"
+#include "components/policy/core/browser/signin/profile_separation_policies.h"
#include "components/policy/core/browser/signin/user_cloud_signin_restriction_policy_fetcher.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -184,17 +185,18 @@ void TurnSyncOnHelperDelegateImpl::OnBrowserRemoved(Browser* browser) {
void TurnSyncOnHelperDelegateImpl::OnProfileSigninRestrictionsFetched(
const AccountInfo& account_info,
signin::SigninChoiceCallback callback,
- const std::string& signin_restriction) {
+ const policy::ProfileSeparationPolicies& profile_separation_policies) {
if (!browser_) {
std::move(callback).Run(signin::SIGNIN_CHOICE_CANCEL);
return;
}
auto profile_creation_required_by_policy =
- signin_util::ProfileSeparationEnforcedByPolicy(browser_->profile(),
- signin_restriction);
+ signin_util::IsProfileSeparationEnforcedByProfile(browser_->profile()) ||
+ signin_util::IsProfileSeparationEnforcedByPolicies(
+ profile_separation_policies);
bool show_link_data_option = signin_util::
ProfileSeparationAllowsKeepingUnmanagedBrowsingDataInManagedProfile(
- browser_->profile(), signin_restriction);
+ browser_->profile(), profile_separation_policies);
browser_->signin_view_controller()->ShowModalEnterpriseConfirmationDialog(
account_info, profile_creation_required_by_policy, show_link_data_option,
diff --git a/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.h b/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.h
index 4922e1bd50b..01840efdb8c 100644
--- a/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.h
+++ b/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_delegate_impl.h
@@ -19,6 +19,7 @@ class SigninUIError;
struct AccountInfo;
namespace policy {
+class ProfileSeparationPolicies;
class UserCloudSigninRestrictionPolicyFetcher;
}
@@ -71,7 +72,7 @@ class TurnSyncOnHelperDelegateImpl : public TurnSyncOnHelper::Delegate,
void OnProfileSigninRestrictionsFetched(
const AccountInfo& account_info,
signin::SigninChoiceCallback callback,
- const std::string& signin_restriction);
+ const policy::ProfileSeparationPolicies& profile_separation_policies);
#endif //! BUILDFLAG(IS_CHROMEOS_LACROS)
void OnProfileCheckComplete(const AccountInfo& account_info,
diff --git a/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_unittest.cc b/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_unittest.cc
index c7fb897982b..ef43cfc517e 100644
--- a/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/signin/turn_sync_on_helper_unittest.cc
@@ -51,7 +51,6 @@
#include "components/policy/core/common/mock_policy_service.h"
#include "components/policy/core/common/policy_service.h"
#include "components/prefs/pref_service.h"
-#include "components/signin/public/base/account_consistency_method.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_pref_names.h"
@@ -508,18 +507,7 @@ class TurnSyncOnHelperTest : public testing::Test {
profile_builder.SetPolicyService(std::make_unique<FakePolicyService>());
return IdentityTestEnvironmentProfileAdaptor::
- CreateProfileForIdentityTestEnvironment(
- profile_builder,
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- // Use |signin::AccountConsistencyMethod::kDice| to ensure that
- // profiles are treated as they would when DICE is enabled and
- // profiles are cleared when sync is revoked only is there is a
- // refresh token error.
- signin::AccountConsistencyMethod::kDice
-#elif BUILDFLAG(IS_CHROMEOS_LACROS)
- signin::AccountConsistencyMethod::kMirror
-#endif
- );
+ CreateProfileForIdentityTestEnvironment(profile_builder);
}
virtual void AddTestingProfileFactories(
diff --git a/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals.mojom b/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals.mojom
index 387e3b13ba6..18fbd1394e7 100644
--- a/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals.mojom
+++ b/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals.mojom
@@ -10,9 +10,10 @@ import "mojo/public/mojom/base/time.mojom";
enum RequestStatus {
kHardcoded = 0,
- kSent = 1,
- kSucceeded = 2,
- kFailed = 3,
+ kCreated = 1,
+ kSent = 2,
+ kSucceeded = 3,
+ kFailed = 4,
};
// Represents a suggest request and its associated metadata and response.
@@ -51,8 +52,10 @@ interface PageHandler {
// WebUI-side handler for requests from the browser.
interface Page {
- // Notifies the page that a request has started.
- OnSuggestRequestStarting(Request request);
+ // Notifies the page that the given request has been created.
+ OnSuggestRequestCreated(Request request);
+ // Notifies the page that the given request has started.
+ OnSuggestRequestStarted(Request request);
// Notifies the page that the given request has completed.
OnSuggestRequestCompleted(Request request);
};
diff --git a/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.cc b/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.cc
index 8281a6ef938..9f0ac7a89ba 100644
--- a/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.cc
+++ b/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.h"
+#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/autocomplete/remote_suggestions_service_factory.h"
#include "components/variations/net/variations_http_headers.h"
@@ -47,7 +48,7 @@ void SuggestInternalsHandler::HardcodeResponse(
std::move(callback).Run(std::move(mojom_request));
}
-void SuggestInternalsHandler::OnSuggestRequestStarting(
+void SuggestInternalsHandler::OnSuggestRequestCreated(
const base::UnguessableToken& request_id,
const network::ResourceRequest* request) {
// Update the page with the request information.
@@ -59,19 +60,34 @@ void SuggestInternalsHandler::OnSuggestRequestStarting(
variations::GetVariationsHeader(*request, &variations_header);
mojom_request->data[variations::kClientDataHeader] = variations_header;
mojom_request->data[request->method] = request->url.spec();
+ mojom_request->status = suggest_internals::mojom::RequestStatus::kCreated;
+ page_->OnSuggestRequestCreated(std::move(mojom_request));
+}
+
+void SuggestInternalsHandler::OnSuggestRequestStarted(
+ const base::UnguessableToken& request_id,
+ network::SimpleURLLoader* loader,
+ const std::string& request_body) {
+ // Update the page with the request information.
+ suggest_internals::mojom::RequestPtr mojom_request =
+ suggest_internals::mojom::Request::New();
+ mojom_request->id = request_id;
+ mojom_request->data["Request-Body"] = request_body;
mojom_request->status = suggest_internals::mojom::RequestStatus::kSent;
mojom_request->start_time = base::Time::Now();
- page_->OnSuggestRequestStarting(std::move(mojom_request));
+ page_->OnSuggestRequestStarted(std::move(mojom_request));
}
void SuggestInternalsHandler::OnSuggestRequestCompleted(
const base::UnguessableToken& request_id,
- const bool response_received,
+ const int response_code,
const std::unique_ptr<std::string>& response_body) {
// Update the page with the request information.
suggest_internals::mojom::RequestPtr mojom_request =
suggest_internals::mojom::Request::New();
mojom_request->id = request_id;
+ mojom_request->data["Response-Code"] = base::NumberToString(response_code);
+ const bool response_received = response_code == 200;
mojom_request->status =
response_received ? suggest_internals::mojom::RequestStatus::kSucceeded
: suggest_internals::mojom::RequestStatus::kFailed;
diff --git a/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.h b/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.h
index a838a6a11bd..062d3499ed7 100644
--- a/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.h
+++ b/chromium/chrome/browser/ui/webui/suggest_internals/suggest_internals_handler.h
@@ -46,12 +46,15 @@ class SuggestInternalsHandler : public suggest_internals::mojom::PageHandler,
HardcodeResponseCallback callback) override;
// RemoteSuggestionsService::Observer:
- void OnSuggestRequestStarting(
+ void OnSuggestRequestCreated(
const base::UnguessableToken& request_id,
const network::ResourceRequest* request) override;
+ void OnSuggestRequestStarted(const base::UnguessableToken& request_id,
+ network::SimpleURLLoader* loader,
+ const std::string& request_body) override;
void OnSuggestRequestCompleted(
const base::UnguessableToken& request_id,
- const bool response_received,
+ const int response_code,
const std::unique_ptr<std::string>& response_body) override;
private:
diff --git a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.cc b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.cc
index e72dc200c00..d65aaae2134 100644
--- a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.cc
+++ b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.cc
@@ -18,8 +18,8 @@
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/platform_util.h"
-#include "chrome/browser/policy/management_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/signin_ui_util.h"
#include "chrome/browser/support_tool/data_collection_module.pb.h"
@@ -32,9 +32,12 @@
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
#include "chrome/grit/support_tool_resources.h"
#include "chrome/grit/support_tool_resources_map.h"
#include "components/feedback/redaction_tool/pii_types.h"
+#include "components/policy/core/common/management/management_service.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "content/public/browser/web_contents.h"
@@ -43,6 +46,7 @@
#include "content/public/browser/web_ui_message_handler.h"
#include "net/base/url_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/shell_dialogs/select_file_dialog.h"
#include "url/gurl.h"
@@ -63,6 +67,8 @@ void CreateAndAddSupportToolHTMLSource(Profile* profile, const GURL& url) {
"enableCopyTokenButton",
base::FeatureList::IsEnabled(features::kSupportToolCopyTokenButton));
+ source->AddLocalizedStrings(SupportToolUI::GetLocalizedStrings());
+
webui::SetupWebUIDataSource(
source, base::make_span(kSupportToolResources, kSupportToolResourcesSize),
IDR_SUPPORT_TOOL_SUPPORT_TOOL_CONTAINER_HTML);
@@ -287,11 +293,10 @@ void SupportToolMessageHandler::HandleStartDataCollection(
// Send error message to UI if there's no selected data collectors to include.
if (included_data_collectors.empty()) {
ResolveJavascriptCallback(
- callback_id,
- GetStartDataCollectionResult(
- /*success=*/false,
- /*error_message=*/"No data collector selected. Please select at "
- "least one data collector."));
+ callback_id, GetStartDataCollectionResult(
+ /*success=*/false,
+ /*error_message=*/l10n_util::GetStringUTF16(
+ IDS_SUPPORT_TOOL_SELECT_DATA_COLLECTOR_ERROR)));
return;
}
this->handler_ =
@@ -305,7 +310,7 @@ void SupportToolMessageHandler::HandleStartDataCollection(
weak_ptr_factory_.GetWeakPtr()));
ResolveJavascriptCallback(
callback_id, GetStartDataCollectionResult(
- /*success=*/true, /*error_message=*/std::string()));
+ /*success=*/true, /*error_message=*/std::u16string()));
}
void SupportToolMessageHandler::OnDataCollectionDone(
@@ -452,6 +457,101 @@ SupportToolUI::SupportToolUI(content::WebUI* web_ui) : WebUIController(web_ui) {
SupportToolUI::~SupportToolUI() = default;
+// static
+base::Value::Dict SupportToolUI::GetLocalizedStrings() {
+ base::Value::Dict localized_strings;
+ localized_strings.Set(
+ "issueDetailsPageTitle",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_ISSUE_DETAILS_PAGE_TITLE));
+ localized_strings.Set(
+ "dataSelectionPageTitle",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_DATA_SELECTION_PAGE_TITLE));
+ localized_strings.Set(
+ "reviewPiiPageTitle",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_REVIEW_PII_PAGE_TITLE));
+ localized_strings.Set(
+ "dataExportDonePageTitle",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_DATA_EXPORT_DONE_PAGE_TITLE));
+ localized_strings.Set(
+ "dataExportedText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_DATA_EXPORTED_TEXT));
+ localized_strings.Set("supportCaseId", l10n_util::GetStringUTF16(
+ IDS_SUPPORT_TOOL_SUPPORT_CASE_ID));
+ localized_strings.Set("email",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_EMAIL));
+ localized_strings.Set(
+ "describeIssueText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_DESCRIBE_ISSUE_TEXT));
+ localized_strings.Set("issueDescriptionPlaceholder",
+ l10n_util::GetStringUTF16(
+ IDS_SUPPORT_TOOL_ISSUE_DESCRIPTION_PLACEHOLDER));
+ localized_strings.Set(
+ "piiWarningText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_PII_WARNING_TEXT));
+ localized_strings.Set(
+ "includeAllPiiRadioButton",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_INCLUDE_ALL_PII_RADIO_BUTTON));
+ localized_strings.Set(
+ "removePiiRadioButton",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_REMOVE_PII_RADIO_BUTTON));
+ localized_strings.Set(
+ "piiRemovalDisclaimer",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_PII_REMOVAL_DISCLAIMER));
+ localized_strings.Set("manuallySelectPiiRadioButton",
+ l10n_util::GetStringUTF16(
+ IDS_SUPPORT_TOOL_MANUALLY_SELECT_PII_RADIO_BUTTON));
+ localized_strings.Set(
+ "cancelButtonText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_CANCEL_BUTTON_TEXT));
+ localized_strings.Set(
+ "exportButtonText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_EXPORT_BUTTON_TEXT));
+ localized_strings.Set(
+ "backButtonText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_BACK_BUTTON_TEXT));
+ localized_strings.Set(
+ "dismissButtonText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_DISMISS_BUTTON_TEXT));
+ localized_strings.Set(
+ "continueButtonText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_CONTINUE_BUTTON_TEXT));
+ localized_strings.Set(
+ "dataCollectionSpinner",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_DATA_COLLECTION_SPINNER));
+ localized_strings.Set(
+ "dataExportSpinner",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_DATA_EXPORT_SPINNER));
+ localized_strings.Set("supportToolTabTitle",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_TAB_TITLE));
+ localized_strings.Set(
+ "urlGeneratorPageTitle",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_URL_GENERATOR_PAGE_TITLE));
+ localized_strings.Set(
+ "dataCollectorListTitle",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_DATA_COLLECTOR_LIST_TITLE));
+ localized_strings.Set(
+ "getLinkText", l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_GET_LINK_TEXT));
+ localized_strings.Set(
+ "copyLinkDescription",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_COPY_LINK_DESCRIPTION));
+ localized_strings.Set(
+ "copyLinkButtonText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_COPY_LINK_BUTTON_TEXT));
+ localized_strings.Set(
+ "copyTokenButtonText",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_COPY_TOKEN_BUTTON_TEXT));
+ localized_strings.Set("linkCopied", l10n_util::GetStringUTF16(
+ IDS_SUPPORT_TOOL_LINK_COPIED_TEXT));
+ localized_strings.Set("tokenCopied", l10n_util::GetStringUTF16(
+ IDS_SUPPORT_TOOL_TOKEN_COPIED_TEXT));
+ localized_strings.Set(
+ "dontIncludeEmailAddress",
+ l10n_util::GetStringUTF16(IDS_SUPPORT_TOOL_DONT_INCLUDE_EMAIL));
+ return localized_strings;
+}
+
+// static
bool SupportToolUI::IsEnabled(Profile* profile) {
- return policy::IsDeviceEnterpriseManaged() || !profile->IsGuestSession();
+ return policy::ManagementServiceFactory::GetForPlatform()->IsManaged() ||
+ !profile->IsGuestSession();
}
diff --git a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.h b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.h
index de6139767de..06d0fc622a1 100644
--- a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.h
+++ b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui.h
@@ -18,6 +18,8 @@ class SupportToolUI : public content::WebUIController {
~SupportToolUI() override;
+ static base::Value::Dict GetLocalizedStrings();
+
// Returns if Support Tool should be enabled. Support Tool is only
// enabled in managed devices and logged-in sessions on consumer devices. It
// won't be available in guest sessions of consumer-owned devices.
diff --git a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.cc b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.cc
index db11f074a0b..7c003844d77 100644
--- a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.cc
+++ b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.cc
@@ -19,29 +19,16 @@
#include "chrome/browser/support_tool/data_collection_module.pb.h"
#include "chrome/browser/support_tool/data_collector.h"
#include "chrome/browser/support_tool/support_tool_util.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
#include "components/feedback/redaction_tool/pii_types.h"
#include "net/base/url_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
namespace support_tool_ui {
-const char kAndroidAppInfo[] = "Android App Information";
-const char kSSID[] = "WiFi SSID";
-const char kCellularLocationInfo[] = "Cellular Location Info";
-const char kEmail[] = "Email Address";
-const char kGAIA[] = "Google Account ID";
-const char kStableIdentifier[] =
- "Other Stable Identifiers (e.g., Hashes or UUIDs)";
-const char kIPPAddress[] = "Printing IPP Address";
-const char kIPAddress[] = "IP Address";
-const char kMACAddress[] = "Network MAC Address";
-const char kWindowTitle[] = "Window Titles";
-const char kURL[] = "URLs";
-const char kSerial[] = "Device & Component Serial Numbers";
-const char kRemovableStorage[] = "Removable Storage Names";
-const char kEAP[] = "EAP Network Authentication Information";
-
const char kPiiItemDescriptionKey[] = "piiTypeDescription";
const char kPiiItemDetectedDataKey[] = "detectedData";
const char kPiiItemPIITypeKey[] = "piiType";
@@ -64,99 +51,67 @@ const char kSupportTokenGenerationResultErrorMessage[] = "errorMessage";
namespace {
// Returns the human readable name corresponding to `data_collector_type`.
-std::string GetPIITypeDescription(redaction::PIIType type_enum) {
- // This function will return translatable strings in future. For now, return
- // string constants until we have the translatable strings ready.
- switch (type_enum) {
- case redaction::PIIType::kAndroidAppStoragePath:
- // App storage path is part of information about an Android app.
- return support_tool_ui::kAndroidAppInfo;
- case redaction::PIIType::kEmail:
- return support_tool_ui::kEmail;
- case redaction::PIIType::kGaiaID:
- return support_tool_ui::kGAIA;
- case redaction::PIIType::kIPPAddress:
- return support_tool_ui::kIPPAddress;
- case redaction::PIIType::kIPAddress:
- return support_tool_ui::kIPAddress;
- case redaction::PIIType::kCellularLocationInfo:
- return support_tool_ui::kCellularLocationInfo;
- case redaction::PIIType::kMACAddress:
- return support_tool_ui::kMACAddress;
- case redaction::PIIType::kUIHierarchyWindowTitles:
- return support_tool_ui::kWindowTitle;
- case redaction::PIIType::kURL:
- return support_tool_ui::kURL;
- case redaction::PIIType::kSerial:
- return support_tool_ui::kSerial;
- case redaction::PIIType::kSSID:
- return support_tool_ui::kSSID;
- case redaction::PIIType::kStableIdentifier:
- return support_tool_ui::kStableIdentifier;
- case redaction::PIIType::kVolumeLabel:
- // Volume labels are a part of removable storage paths in various logs.
- return support_tool_ui::kRemovableStorage;
- case redaction::PIIType::kEAP:
- return support_tool_ui::kEAP;
- default:
- return "Error: Undefined";
- }
-}
-
-// Returns the human readable name corresponding to `data_collector_type`.
std::string GetDataCollectorName(
support_tool::DataCollectorType data_collector_type) {
- // This function will return translatable strings in future. For now, return
- // string constants until we have the translatable strings ready.
switch (data_collector_type) {
case support_tool::CHROME_INTERNAL:
- return "Chrome System Information";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_CHROME_SYSTEM_INFO);
case support_tool::CRASH_IDS:
- return "Crash IDs";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_CRASH_IDS);
case support_tool::MEMORY_DETAILS:
- return "Memory Details";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_MEMORY_DETAILS);
case support_tool::CHROMEOS_UI_HIERARCHY:
- return "UI Hierarchy";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_UI_HIEARCHY);
case support_tool::CHROMEOS_COMMAND_LINE:
- return "Additional Chrome OS Platform Logs";
+ return l10n_util::GetStringUTF8(
+ IDS_SUPPORT_TOOL_ADDITIONAL_CROS_PLATFROM_LOGS);
case support_tool::CHROMEOS_DEVICE_EVENT:
- return "Device Event";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_DEVICE_EVENT);
case support_tool::CHROMEOS_IWL_WIFI_DUMP:
- return "Intel WiFi NICs Debug Dump";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_INTEL_WIFI_DEBUG_DUMP);
case support_tool::CHROMEOS_TOUCH_EVENTS:
- return "Touch Events";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_TOUCH_EVENTS);
case support_tool::CHROMEOS_CROS_API:
- return "LaCrOS System Information";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_LACROS_SYSTEM_INFO);
case support_tool::CHROMEOS_LACROS:
- return "LaCrOS";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_LACROS);
case support_tool::CHROMEOS_REVEN:
- return "Chrome OS Flex Logs";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_CHROMEOS_FLEX_LOGS);
case support_tool::CHROMEOS_DBUS:
- return "DBus Details";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_DBUS_DETAILS);
case support_tool::CHROMEOS_NETWORK_ROUTES:
- return "Chrome OS Network Routes";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_CHROMEOS_NETWORK_ROUTES);
case support_tool::CHROMEOS_SHILL:
- return "Chrome OS Shill (Connection Manager) Logs";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_CHROMEOS_SHILL_LOGS);
case support_tool::POLICIES:
- return "Policies";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_POLICIES);
case support_tool::CHROMEOS_SYSTEM_STATE:
- return "Chrome OS System State and Logs";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_CHROMEOS_SYSTEM_STATE);
case support_tool::CHROMEOS_SYSTEM_LOGS:
- return "ChromeOS System Logs";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_CHROMEOS_SYSTEM_LOGS);
case support_tool::CHROMEOS_CHROME_USER_LOGS:
- return "Chrome OS Chrome User Logs";
+ return l10n_util::GetStringUTF8(
+ IDS_SUPPORT_TOOL_CHROMEOS_CHROME_USER_LOGS);
case support_tool::CHROMEOS_BLUETOOTH_FLOSS:
- return "ChromeOS Bluetooth Floss";
+ return l10n_util::GetStringUTF8(
+ IDS_SUPPORT_TOOL_CHROMEOS_BLUETOOTH_FLOSS);
case support_tool::CHROMEOS_CONNECTED_INPUT_DEVICES:
- return "ChromeOS Connected Input Devices";
+ return l10n_util::GetStringUTF8(
+ IDS_SUPPORT_TOOL_CHROMEOS_CONNECTED_INPUT_DEVICES);
case support_tool::CHROMEOS_TRAFFIC_COUNTERS:
- return "ChromeOS Traffic Counters";
+ return l10n_util::GetStringUTF8(
+ IDS_SUPPORT_TOOL_CHROMEOS_TRAFFIC_COUNTERS);
case support_tool::CHROMEOS_VIRTUAL_KEYBOARD:
- return "ChromeOS Virtual Keyboard";
+ return l10n_util::GetStringUTF8(
+ IDS_SUPPORT_TOOL_CHROMEOS_VIRTUAL_KEYBOARD);
case support_tool::CHROMEOS_NETWORK_HEALTH:
- return "ChromeOS Network Health";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_CHROMEOS_NETWORK_HEALTH);
+ case support_tool::SIGN_IN_STATE:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_SIGN_IN);
case support_tool::PERFORMANCE:
- return "Performance and Battery State";
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_PERFORMANCE);
+ case support_tool::CHROMEOS_APP_SERVICE:
+ return "ChromeOS App Service";
default:
return "Error: Undefined";
}
@@ -236,6 +191,44 @@ base::Value::Dict GetSupportTokenGenerationResult(bool success,
} // namespace
+// Returns the human readable name corresponding to `type_enum`.
+std::string GetPIITypeDescription(redaction::PIIType type_enum) {
+ switch (type_enum) {
+ case redaction::PIIType::kAndroidAppStoragePath:
+ // App storage path is part of information about an Android app.
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_ANDROID_APP_INFO);
+ case redaction::PIIType::kEmail:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_EMAIL_ADDRESS);
+ case redaction::PIIType::kGaiaID:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_GAIA_ID);
+ case redaction::PIIType::kIPPAddress:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_PRINTING_IPP_ADDRESS);
+ case redaction::PIIType::kIPAddress:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_IP_ADDRESS);
+ case redaction::PIIType::kCellularLocationInfo:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_CELLULAR_LOCATION_INFO);
+ case redaction::PIIType::kMACAddress:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_MAC_ADDRESS);
+ case redaction::PIIType::kUIHierarchyWindowTitles:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_WINDOW_TITLES);
+ case redaction::PIIType::kURL:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_URLS);
+ case redaction::PIIType::kSerial:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_SERIAL_NUMBERS);
+ case redaction::PIIType::kSSID:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_WIFI_SSID);
+ case redaction::PIIType::kStableIdentifier:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_STABLE_IDENTIDIERS);
+ case redaction::PIIType::kVolumeLabel:
+ // Volume labels are a part of removable storage paths in various logs.
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_REMOVABLE_STORAGE_NAMES);
+ case redaction::PIIType::kEAP:
+ return l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_EAP);
+ default:
+ return "Error: Undefined";
+ }
+}
+
// type PIIDataItem = {
// piiTypeDescription: string,
// piiType: number,
@@ -337,7 +330,7 @@ std::set<support_tool::DataCollectorType> GetIncludedDataCollectorTypes(
}
base::Value::Dict GetStartDataCollectionResult(bool success,
- std::string error_message) {
+ std::u16string error_message) {
base::Value::Dict result;
result.Set("success", success);
result.Set("errorMessage", error_message);
@@ -354,7 +347,7 @@ base::Value::Dict GenerateCustomizedURL(
// If there's no selected data collector to add, consider this as an error.
return GetSupportTokenGenerationResult(
/*success=*/false, /*result=*/std::string(), /*error_message=*/
- "No data collectors included. Please select a data collector.");
+ l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_SELECT_DATA_COLLECTOR_ERROR));
}
GURL customized_url("chrome://support-tool");
if (!case_id.empty()) {
@@ -378,7 +371,7 @@ base::Value::Dict GenerateSupportToken(
// If there's no selected data collector to add, consider this as an error.
return GetSupportTokenGenerationResult(
/*success=*/false, /*result=*/std::string(), /*error_message=*/
- "No data collectors included. Please select a data collector.");
+ l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_SELECT_DATA_COLLECTOR_ERROR));
}
return GetSupportTokenGenerationResult(
/*success=*/true, GetDataCollectionModuleQuery(included_data_collectors),
diff --git a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.h b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.h
index 0ae0b37a2eb..44dca7c8290 100644
--- a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.h
+++ b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils.h
@@ -15,23 +15,6 @@
namespace support_tool_ui {
-// Strings that contain the human readable description of redaction::PIIType
-// enums.
-extern const char kAndroidAppInfo[];
-extern const char kSSID[];
-extern const char kCellularLocationInfo[];
-extern const char kEmail[];
-extern const char kGAIA[];
-extern const char kStableIdentifier[];
-extern const char kIPPAddress[];
-extern const char kIPAddress[];
-extern const char kMACAddress[];
-extern const char kWindowTitle[];
-extern const char kURL[];
-extern const char kSerial[];
-extern const char kRemovableStorage[];
-extern const char kEAP[];
-
// String keys of the fields of PIIDataItem dictionary that Support Tool UI
// stores the detected PII to display it to user.
extern const char kPiiItemDescriptionKey[];
@@ -55,6 +38,9 @@ extern const char kSupportTokenGenerationResultErrorMessage[];
} // namespace support_tool_ui
+// Returns the human readable name corresponding to `type_enum`.
+std::string GetPIITypeDescription(redaction::PIIType type_enum);
+
// Returns PIIDataItems in `detected_pii` where PIIDataItem is
// type PIIDataItem = {
// piiTypeDescription: string,
@@ -113,7 +99,7 @@ std::set<support_tool::DataCollectorType> GetIncludedDataCollectorTypes(
// errorMessage: string,
// }
base::Value::Dict GetStartDataCollectionResult(bool success,
- std::string error_message);
+ std::u16string error_message);
// Generates a customized chrome://support-tool URL from given `case_id` and
// `data_collector_items` and returns the result in a format Support Tool UI
diff --git a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils_unittest.cc b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils_unittest.cc
index 74e42b133a7..186673bbc34 100644
--- a/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/support_tool/support_tool_ui_utils_unittest.cc
@@ -11,9 +11,12 @@
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_map.h"
#include "base/strings/string_piece_forward.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/support_tool/data_collection_module.pb.h"
#include "chrome/browser/support_tool/data_collector.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
#include "components/feedback/redaction_tool/pii_types.h"
#include "content/public/test/browser_task_environment.h"
#include "net/base/url_util.h"
@@ -21,6 +24,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/l10n/l10n_util.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/crosapi/fake_browser_manager.h"
@@ -46,18 +50,6 @@ const PIIMap kPIIMap = {
const redaction::PIIType kPIITypes[] = {redaction::PIIType::kIPAddress,
redaction::PIIType::kURL,
redaction::PIIType::kStableIdentifier};
-
-// PII strings with the definition strings.
-const auto kPIIStringsWithDefinition = base::MakeFixedFlatMap<
- base::StringPiece,
- base::StringPiece>(
- {{support_tool_ui::kIPAddress, "0.255.255.255, ::ffff:cb0c:10ea"},
- {support_tool_ui::kURL,
- "chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js?bar=x"},
- {support_tool_ui::kStableIdentifier,
- "123e4567-e89b-12d3-a456-426614174000, "
- "27540283740a0897ab7c8de0f809add2bacde78f"}});
-
} // namespace
class SupportToolUiUtilsTest : public ::testing::Test {
@@ -90,6 +82,26 @@ class SupportToolUiUtilsTest : public ::testing::Test {
}
}
+ std::string GetExpectedPIIDefinitionString(
+ const redaction::PIIType& pii_type) {
+ // PII types with the definition strings.
+ const auto kExpectedPiiTypeDefinitions =
+ base::MakeFixedFlatMap<redaction::PIIType, std::string>(
+ {{redaction::PIIType::kIPAddress,
+ l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_IP_ADDRESS)},
+ {redaction::PIIType::kURL,
+ l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_URLS)},
+ {redaction::PIIType::kStableIdentifier,
+ l10n_util::GetStringUTF8(IDS_SUPPORT_TOOL_STABLE_IDENTIDIERS)}});
+ // fixed_flat_map uses std::array<T> as the backing container, which has
+ // std::array::iterator<T> = T*, thus the iterator of the
+ // `kExpectedPiiTypeDefinitions` is a pointer.
+ auto* it = kExpectedPiiTypeDefinitions.find(pii_type);
+ EXPECT_NE(kExpectedPiiTypeDefinitions.end(), it);
+ std::string definition = it->second;
+ return definition;
+ }
+
private:
content::BrowserTaskEnvironment task_environment_;
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -106,18 +118,19 @@ TEST_F(SupportToolUiUtilsTest, PiiItems) {
const base::Value::Dict* pii_item = detected_pii_item.GetIfDict();
// PIIItem must be a Value::Dict.
EXPECT_TRUE(pii_item);
+ const redaction::PIIType pii_type = static_cast<redaction::PIIType>(
+ pii_item->FindInt(support_tool_ui::kPiiItemPIITypeKey).value());
const std::string* description =
pii_item->FindString(support_tool_ui::kPiiItemDescriptionKey);
// The dictionary must contain description.
EXPECT_TRUE(description);
- const auto* pii_string_with_definition =
- kPIIStringsWithDefinition.find(*description);
- // The definition string must exist in `kPIIStringsWithDefinition`.
- EXPECT_NE(pii_string_with_definition, kPIIStringsWithDefinition.end());
+ // The definition string must equal to the expected one in
+ // `kPIIStringsWithDefinition`.
+ EXPECT_EQ(*description, GetExpectedPIIDefinitionString(pii_type));
const std::string* pii_data =
pii_item->FindString(support_tool_ui::kPiiItemDetectedDataKey);
// Check the detected data.
- EXPECT_THAT(*pii_data, StrEq(pii_string_with_definition->second));
+ EXPECT_TRUE(pii_data);
}
// Update all PII items to have their keep value as true.
diff --git a/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc b/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
index 0ea490aff73..cb842544bf7 100644
--- a/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
+++ b/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
@@ -12,6 +12,7 @@
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
+#include "build/chromeos_buildflags.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/sync_invalidations_service_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
@@ -31,6 +32,12 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_ui.h"
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/crosapi/browser_manager.h"
+#include "chrome/browser/ash/crosapi/browser_util.h"
+#include "chrome/common/webui_url_constants.h"
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
using syncer::SyncInvalidationsService;
using syncer::SyncService;
@@ -142,11 +149,23 @@ void SyncInternalsMessageHandler::RegisterMessages() {
syncer::sync_ui_util::kGetAllNodes,
base::BindRepeating(&SyncInternalsMessageHandler::HandleGetAllNodes,
base::Unretained(this)));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ web_ui()->RegisterMessageCallback(
+ syncer::sync_ui_util::kIsLacrosEnabled,
+ base::BindRepeating(&SyncInternalsMessageHandler::IsLacrosEnabled,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ syncer::sync_ui_util::kOpenLacrosSyncInternals,
+ base::BindRepeating(&SyncInternalsMessageHandler::OpenLacrosSyncInternals,
+ base::Unretained(this)));
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
void SyncInternalsMessageHandler::HandleRequestDataAndRegisterForUpdates(
const base::Value::List& args) {
- DCHECK(args.empty());
+ CHECK(args.empty());
AllowJavascript();
// is_registered_ flag protects us from double-registering. This could
@@ -167,7 +186,7 @@ void SyncInternalsMessageHandler::HandleRequestDataAndRegisterForUpdates(
void SyncInternalsMessageHandler::HandleRequestListOfTypes(
const base::Value::List& args) {
- DCHECK(args.empty());
+ CHECK(args.empty());
AllowJavascript();
base::Value::Dict event_details;
@@ -183,7 +202,7 @@ void SyncInternalsMessageHandler::HandleRequestListOfTypes(
void SyncInternalsMessageHandler::HandleRequestIncludeSpecificsInitialState(
const base::Value::List& args) {
- DCHECK(args.empty());
+ CHECK(args.empty());
AllowJavascript();
base::Value::Dict value;
@@ -196,7 +215,7 @@ void SyncInternalsMessageHandler::HandleRequestIncludeSpecificsInitialState(
void SyncInternalsMessageHandler::HandleGetAllNodes(
const base::Value::List& args) {
- DCHECK_EQ(1U, args.size());
+ CHECK_EQ(1U, args.size());
AllowJavascript();
const std::string& callback_id = args[0].GetString();
@@ -215,14 +234,14 @@ void SyncInternalsMessageHandler::HandleGetAllNodes(
void SyncInternalsMessageHandler::HandleSetIncludeSpecifics(
const base::Value::List& args) {
- DCHECK_EQ(1U, args.size());
+ CHECK_EQ(1U, args.size());
AllowJavascript();
include_specifics_ = args[0].GetBool();
}
void SyncInternalsMessageHandler::HandleWriteUserEvent(
const base::Value::List& args) {
- DCHECK_EQ(2U, args.size());
+ CHECK_EQ(2U, args.size());
AllowJavascript();
Profile* profile = Profile::FromWebUI(web_ui());
@@ -247,7 +266,7 @@ void SyncInternalsMessageHandler::HandleWriteUserEvent(
void SyncInternalsMessageHandler::HandleRequestStart(
const base::Value::List& args) {
- DCHECK_EQ(0U, args.size());
+ CHECK_EQ(0U, args.size());
SyncService* service = GetSyncService();
if (!service) {
@@ -264,7 +283,7 @@ void SyncInternalsMessageHandler::HandleRequestStart(
void SyncInternalsMessageHandler::HandleRequestStopClearData(
const base::Value::List& args) {
- DCHECK_EQ(0U, args.size());
+ CHECK_EQ(0U, args.size());
SyncService* service = GetSyncService();
if (!service) {
@@ -284,6 +303,30 @@ void SyncInternalsMessageHandler::HandleTriggerRefresh(
service->TriggerRefresh(syncer::ModelTypeSet::All());
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+void SyncInternalsMessageHandler::IsLacrosEnabled(
+ const base::Value::List& args) {
+ CHECK_EQ(1U, args.size());
+
+ AllowJavascript();
+ const bool is_lacros_enabled = crosapi::browser_util::IsLacrosEnabled();
+ std::string callback_id = args[0].GetString();
+ ResolveJavascriptCallback(base::Value(callback_id),
+ base::Value(is_lacros_enabled));
+}
+
+void SyncInternalsMessageHandler::OpenLacrosSyncInternals(
+ const base::Value::List& args) {
+ CHECK_EQ(0U, args.size());
+
+ // Note: This will only be called by the UI when Lacros is available.
+ DCHECK(crosapi::BrowserManager::Get());
+ crosapi::BrowserManager::Get()->SwitchToTab(
+ GURL(chrome::kChromeUISyncInternalsUrl),
+ /*path_behavior=*/NavigateParams::RESPECT);
+}
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
void SyncInternalsMessageHandler::OnReceivedAllNodes(
const std::string& callback_id,
base::Value::List nodes) {
diff --git a/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h b/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
index d85fd31dcc4..601b745730c 100644
--- a/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
+++ b/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
@@ -10,6 +10,7 @@
#include "base/memory/weak_ptr.h"
#include "base/values.h"
+#include "build/chromeos_buildflags.h"
#include "components/sync/engine/events/protocol_event_observer.h"
#include "components/sync/invalidations/invalidations_listener.h"
#include "components/sync/service/sync_service_observer.h"
@@ -68,6 +69,14 @@ class SyncInternalsMessageHandler : public content::WebUIMessageHandler,
// Handler for triggerRefresh message.
void HandleTriggerRefresh(const base::Value::List& args);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // Handler for isLacrosEnabled message.
+ void IsLacrosEnabled(const base::Value::List& args);
+
+ // Handler for openBrowserSyncInternals message.
+ void OpenLacrosSyncInternals(const base::Value::List& args);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
// Callback used in GetAllNodes.
void OnReceivedAllNodes(const std::string& callback_id,
base::Value::List nodes);
diff --git a/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler_unittest.cc b/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler_unittest.cc
index 91f2e1a423a..39175d3db95 100644
--- a/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler_unittest.cc
@@ -188,8 +188,9 @@ class SyncInternalsMessageHandlerTest : public ChromeRenderViewHostTestHarness {
private:
content::TestWebUI web_ui_;
- raw_ptr<TestSyncService, DanglingUntriaged> test_sync_service_;
- raw_ptr<FakeUserEventService, DanglingUntriaged> fake_user_event_service_;
+ raw_ptr<TestSyncService, DanglingUntriaged> test_sync_service_ = nullptr;
+ raw_ptr<FakeUserEventService, DanglingUntriaged> fake_user_event_service_ =
+ nullptr;
std::unique_ptr<TestableSyncInternalsMessageHandler> handler_;
int about_sync_data_delegate_call_count_ = 0;
raw_ptr<SyncService, DanglingUntriaged> last_delegate_sync_service_ = nullptr;
diff --git a/chromium/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc b/chromium/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
index 4ef1f15d100..c3ba3717600 100644
--- a/chromium/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
@@ -29,6 +29,7 @@
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_live_tab_context.h"
#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_renderer_data.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
@@ -39,6 +40,7 @@
#include "chrome/browser/ui/webui/util/image_util.h"
#include "chrome/common/webui_url_constants.h"
#include "ui/base/l10n/time_format.h"
+#include "ui/color/color_provider.h"
namespace {
constexpr base::TimeDelta kTabsChangeDelay = base::Milliseconds(50);
@@ -94,6 +96,17 @@ void CreateTabGroupIfNotPresent(
}
}
+// Applies theming to favicons where necessary. This is needed to handle favicon
+// resources that are rasterized in a theme-unaware way. This is common of
+// favicons not sourced directly from the browser.
+gfx::ImageSkia ThemeFavicon(const gfx::ImageSkia& source,
+ const ui::ColorProvider& provider) {
+ return favicon::ThemeFavicon(
+ source, provider.GetColor(kColorTabSearchPrimaryForeground),
+ provider.GetColor(kColorTabSearchBackground),
+ provider.GetColor(kColorTabSearchBackground));
+}
+
} // namespace
TabSearchPageHandler::TabSearchPageHandler(
@@ -463,8 +476,13 @@ tab_search::mojom::TabPtr TabSearchPageHandler::GetTab(
web_ui_->GetWebContents()->GetColorProvider();
const gfx::ImageSkia default_favicon =
favicon::GetDefaultFaviconModel().Rasterize(&provider);
- const gfx::ImageSkia raster_favicon =
+ gfx::ImageSkia raster_favicon =
tab_renderer_data.favicon.Rasterize(&provider);
+
+ if (tab_renderer_data.should_themify_favicon) {
+ raster_favicon = ThemeFavicon(raster_favicon, provider);
+ }
+
tab_data->favicon_url = GURL(webui::EncodePNGAndMakeDataURI(
raster_favicon, web_ui_->GetDeviceScaleFactor()));
tab_data->is_default_favicon =
diff --git a/chromium/chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.cc b/chromium/chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.cc
new file mode 100644
index 00000000000..c670e4edfab
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.cc
@@ -0,0 +1,117 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/location.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper.h"
+#include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/views/controls/webview/web_dialog_view.h"
+#include "ui/views/widget/widget.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Default size set to match signin reauth dialog size (see
+// signin_view_controller_delegate_views.cc).
+constexpr int kDefaultDialogHeight = 520;
+constexpr int kDefaultDialogWidth = 540;
+
+std::unique_ptr<content::WebContents> CreateWebContents(
+ content::BrowserContext* context) {
+ content::WebContents::CreateParams create_params(context, FROM_HERE);
+ // Allows TrustedVault reauth page to close dialog using `window.close()`.
+ // TODO(crbug.com/1434656): investigate whether reauth page can be changed to
+ // close dialog either using TrustedVaultEncryptionKeysExtension (new method
+ // needed) or other mechanism. Once this is done, this dialog can probably
+ // reuse chrome::ShowWebDialog() and avoid controversy like line below.
+ create_params.opened_by_another_window = true;
+ return content::WebContents::Create(create_params);
+}
+
+} // namespace
+
+// static
+void TrustedVaultDialogDelegate::ShowDialogForProfile(Profile* profile,
+ const GURL& url) {
+ TrustedVaultDialogDelegate* dialog_delegate =
+ new TrustedVaultDialogDelegate(url, profile);
+ views::WebDialogView* view = new views::WebDialogView(
+ profile, dialog_delegate, std::make_unique<ChromeWebContentsHandler>(),
+ dialog_delegate->web_contents());
+
+ views::Widget::InitParams params;
+ params.delegate = view;
+ params.name = kWidgetName;
+
+ views::Widget* widget = new views::Widget;
+ widget->Init(std::move(params));
+ widget->Show();
+}
+
+TrustedVaultDialogDelegate::TrustedVaultDialogDelegate(const GURL& url,
+ Profile* profile)
+ : url_(url), web_contents_(CreateWebContents(profile)) {
+ TrustedVaultEncryptionKeysTabHelper::CreateForWebContents(
+ web_contents_.get());
+}
+
+TrustedVaultDialogDelegate::~TrustedVaultDialogDelegate() = default;
+
+ui::ModalType TrustedVaultDialogDelegate::GetDialogModalType() const {
+ return ui::ModalType::MODAL_TYPE_NONE;
+}
+
+std::u16string TrustedVaultDialogDelegate::GetDialogTitle() const {
+ return std::u16string();
+}
+
+GURL TrustedVaultDialogDelegate::GetDialogContentURL() const {
+ return url_;
+}
+
+void TrustedVaultDialogDelegate::GetWebUIMessageHandlers(
+ std::vector<content::WebUIMessageHandler*>* handlers) const {}
+
+void TrustedVaultDialogDelegate::GetDialogSize(gfx::Size* size) const {
+ size->SetSize(kDefaultDialogWidth, kDefaultDialogHeight);
+}
+
+std::string TrustedVaultDialogDelegate::GetDialogArgs() const {
+ return std::string();
+}
+
+void TrustedVaultDialogDelegate::OnDialogShown(content::WebUI* webui) {}
+
+void TrustedVaultDialogDelegate::OnDialogClosed(
+ const std::string& json_retval) {
+ delete this;
+}
+
+void TrustedVaultDialogDelegate::OnCloseContents(content::WebContents* source,
+ bool* out_close_dialog) {
+ *out_close_dialog = true;
+}
+
+bool TrustedVaultDialogDelegate::ShouldShowDialogTitle() const {
+ return false;
+}
+
+bool TrustedVaultDialogDelegate::HandleContextMenu(
+ content::RenderFrameHost& render_frame_host,
+ const content::ContextMenuParams& params) {
+ // Disable context menu.
+ return true;
+}
+
+content::WebContents* TrustedVaultDialogDelegate::web_contents() {
+ return web_contents_.get();
+}
diff --git a/chromium/chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.h b/chromium/chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.h
new file mode 100644
index 00000000000..364d3ff5a8a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/trusted_vault/trusted_vault_dialog_delegate.h
@@ -0,0 +1,57 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_TRUSTED_VAULT_TRUSTED_VAULT_DIALOG_DELEGATE_H_
+#define CHROME_BROWSER_UI_WEBUI_TRUSTED_VAULT_TRUSTED_VAULT_DIALOG_DELEGATE_H_
+
+#include <memory>
+#include <string>
+
+#include "content/public/browser/web_contents.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+#include "url/gurl.h"
+
+class Profile;
+
+// Allows opening TrustedVault error pages (usually gaia reauth) in WebUI
+// dialog.
+class TrustedVaultDialogDelegate : public ui::WebDialogDelegate {
+ public:
+ // Used as an internal name for the widget corresponding to TrustedVault
+ // reauth dialog. Exposed for testing.
+ static constexpr char kWidgetName[] = "TrustedVaultReauthWidget";
+
+ static void ShowDialogForProfile(Profile* profile, const GURL& url);
+
+ TrustedVaultDialogDelegate(const TrustedVaultDialogDelegate&) = delete;
+ TrustedVaultDialogDelegate& operator=(const TrustedVaultDialogDelegate&) =
+ delete;
+ ~TrustedVaultDialogDelegate() override;
+
+ // WebDialogDelegate
+ ui::ModalType GetDialogModalType() const override;
+ std::u16string GetDialogTitle() const override;
+ GURL GetDialogContentURL() const override;
+ void GetWebUIMessageHandlers(
+ std::vector<content::WebUIMessageHandler*>* handlers) const override;
+ void GetDialogSize(gfx::Size* size) const override;
+ std::string GetDialogArgs() const override;
+ void OnDialogShown(content::WebUI* webui) override;
+ void OnDialogClosed(const std::string& json_retval) override;
+ void OnCloseContents(content::WebContents* source,
+ bool* out_close_dialog) override;
+ bool ShouldShowDialogTitle() const override;
+ bool HandleContextMenu(content::RenderFrameHost& render_frame_host,
+ const content::ContextMenuParams& params) override;
+
+ private:
+ explicit TrustedVaultDialogDelegate(const GURL& url, Profile* profile);
+
+ content::WebContents* web_contents();
+
+ const GURL url_;
+ const std::unique_ptr<content::WebContents> web_contents_;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_TRUSTED_VAULT_TRUSTED_VAULT_DIALOG_DELEGATE_H_
diff --git a/chromium/chrome/browser/ui/webui/version/version_handler_chromeos.cc b/chromium/chrome/browser/ui/webui/version/version_handler_chromeos.cc
index a6420d89231..a665968f97d 100644
--- a/chromium/chrome/browser/ui/webui/version/version_handler_chromeos.cc
+++ b/chromium/chrome/browser/ui/webui/version/version_handler_chromeos.cc
@@ -5,28 +5,38 @@
#include "chrome/browser/ui/webui/version/version_handler_chromeos.h"
#include "base/functional/bind.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
-#include "build/chromeos_buildflags.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/webui_url_constants.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
+#include "chromeos/version/version_loader.h"
#include "components/version_info/channel.h"
#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/crosapi/browser_manager.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
-#include "chromeos/strings/grit/chromeos_strings.h"
-#include "ui/base/l10n/l10n_util.h"
-#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/lacros/lacros_url_handling.h"
-#endif
+#include "chromeos/startup/browser_params_proxy.h"
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
namespace {
const char kCrosUrlVersionRedirect[] = "crosUrlVersionRedirect";
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+std::string GetOsVersion() {
+ return chromeos::BrowserParamsProxy::Get()->AshChromeVersion().value_or(
+ "0.0.0.0");
+}
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+
} // namespace
VersionHandlerChromeOS::VersionHandlerChromeOS() {}
@@ -42,18 +52,24 @@ void VersionHandlerChromeOS::HandleRequestVersionInfo(
const base::Value::List& args) {
VersionHandler::HandleRequestVersionInfo(args);
-#if BUILDFLAG(IS_CHROMEOS_ASH)
// Start the asynchronous load of the versions.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+ base::BindOnce(&GetOsVersion),
+ base::BindOnce(&VersionHandlerChromeOS::OnOsVersion,
+ weak_factory_.GetWeakPtr()));
+#endif
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&chromeos::version_loader::GetVersion,
chromeos::version_loader::VERSION_FULL),
- base::BindOnce(&VersionHandlerChromeOS::OnVersion,
+ base::BindOnce(&VersionHandlerChromeOS::OnPlatformVersion,
weak_factory_.GetWeakPtr()));
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&chromeos::version_loader::GetFirmware),
- base::BindOnce(&VersionHandlerChromeOS::OnOSFirmware,
+ base::BindOnce(&VersionHandlerChromeOS::OnFirmwareVersion,
weak_factory_.GetWeakPtr()));
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
@@ -61,12 +77,13 @@ void VersionHandlerChromeOS::HandleRequestVersionInfo(
base::BindOnce(&VersionHandlerChromeOS::OnArcAndArcAndroidSdkVersions,
weak_factory_.GetWeakPtr()));
+#if BUILDFLAG(IS_CHROMEOS_ASH)
const bool showSystemFlagsLink = crosapi::browser_util::IsLacrosEnabled();
#else
const bool showSystemFlagsLink = true;
#endif
- FireWebUIListener("return-lacros-primary", base::Value(showSystemFlagsLink));
+ FireWebUIListener("return-lacros-enabled", base::Value(showSystemFlagsLink));
}
void VersionHandlerChromeOS::RegisterMessages() {
@@ -91,15 +108,20 @@ void VersionHandlerChromeOS::HandleCrosUrlVersionRedirect(
#endif
}
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-void VersionHandlerChromeOS::OnVersion(
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+void VersionHandlerChromeOS::OnOsVersion(const std::string& version) {
+ FireWebUIListener("return-os-version", base::Value(version));
+}
+#endif
+
+void VersionHandlerChromeOS::OnPlatformVersion(
const absl::optional<std::string>& version) {
- FireWebUIListener("return-os-version",
+ FireWebUIListener("return-platform-version",
base::Value(version.value_or("0.0.0.0")));
}
-void VersionHandlerChromeOS::OnOSFirmware(const std::string& version) {
- FireWebUIListener("return-os-firmware-version", base::Value(version));
+void VersionHandlerChromeOS::OnFirmwareVersion(const std::string& version) {
+ FireWebUIListener("return-firmware-version", base::Value(version));
}
void VersionHandlerChromeOS::OnArcAndArcAndroidSdkVersions(
@@ -124,5 +146,3 @@ std::string VersionHandlerChromeOS::GetArcAndArcAndroidSdkVersions() {
arc_android_sdk_version->c_str());
return labeled_version;
}
-
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chromium/chrome/browser/ui/webui/version/version_handler_chromeos.h b/chromium/chrome/browser/ui/webui/version/version_handler_chromeos.h
index 5f255ac81f8..84539066e1f 100644
--- a/chromium/chrome/browser/ui/webui/version/version_handler_chromeos.h
+++ b/chromium/chrome/browser/ui/webui/version/version_handler_chromeos.h
@@ -8,8 +8,8 @@
#include <string>
#include "base/memory/weak_ptr.h"
+#include "build/chromeos_buildflags.h"
#include "chrome/browser/ui/webui/version/version_handler.h"
-#include "chromeos/version/version_loader.h"
// VersionHandlerChromeOS is responsible for loading the Chrome OS
// version.
@@ -28,8 +28,11 @@ class VersionHandlerChromeOS : public VersionHandler {
void RegisterMessages() override;
// Callbacks from chromeos::VersionLoader.
- void OnVersion(const absl::optional<std::string>& version);
- void OnOSFirmware(const std::string& version);
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ void OnOsVersion(const std::string& version);
+#endif
+ void OnPlatformVersion(const absl::optional<std::string>& version);
+ void OnFirmwareVersion(const std::string& version);
void OnArcAndArcAndroidSdkVersions(const std::string& version);
// Callback for the "crosUrlVersionRedirect" message.
diff --git a/chromium/chrome/browser/ui/webui/version/version_ui.cc b/chromium/chrome/browser/ui/webui/version/version_ui.cc
index 82a6e68d119..a6a96082dcd 100644
--- a/chromium/chrome/browser/ui/webui/version/version_ui.cc
+++ b/chromium/chrome/browser/ui/webui/version/version_ui.cc
@@ -64,10 +64,6 @@
#include "chrome/browser/ui/webui/version/version_util_win.h"
#endif
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chromeos/startup/browser_params_proxy.h"
-#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
-
using content::WebUIDataSource;
namespace {
@@ -82,6 +78,7 @@ void CreateAndAddVersionUIDataSource(Profile* profile) {
{version_ui::kApplicationLabel, IDS_PRODUCT_NAME},
{version_ui::kCompany, IDS_ABOUT_VERSION_COMPANY_NAME},
{version_ui::kCopyLabel, IDS_VERSION_UI_COPY_LABEL},
+ {version_ui::kCopyNotice, IDS_VERSION_UI_COPY_NOTICE},
{version_ui::kRevision, IDS_VERSION_UI_REVISION},
{version_ui::kUserAgentName, IDS_VERSION_UI_USER_AGENT},
{version_ui::kCommandLineName, IDS_VERSION_UI_COMMAND_LINE},
@@ -90,19 +87,18 @@ void CreateAndAddVersionUIDataSource(Profile* profile) {
{version_ui::kVariationsName, IDS_VERSION_UI_VARIATIONS},
{version_ui::kVariationsCmdName, IDS_VERSION_UI_VARIATIONS_CMD},
{version_ui::kVariationsSeedName, IDS_VERSION_UI_VARIATIONS_SEED_NAME},
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
{version_ui::kARC, IDS_ARC_LABEL},
{version_ui::kPlatform, IDS_PLATFORM_LABEL},
{version_ui::kCustomizationId, IDS_VERSION_UI_CUSTOMIZATION_ID},
{version_ui::kFirmwareVersion, IDS_VERSION_UI_FIRMWARE_VERSION},
-#else
- {version_ui::kOSName, IDS_VERSION_UI_OS},
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-#if BUILDFLAG(IS_CHROMEOS)
{version_ui::kOsVersionHeaderText1, IDS_VERSION_UI_OS_TEXT1_LABEL},
{version_ui::kOsVersionHeaderText2, IDS_VERSION_UI_OS_TEXT2_LABEL},
{version_ui::kOsVersionHeaderLink, IDS_VERSION_UI_OS_LINK},
#endif // BUILDFLAG(IS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+ {version_ui::kOSName, IDS_VERSION_UI_OS},
+#endif // !BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_ANDROID)
{version_ui::kGmsName, IDS_VERSION_UI_GMS},
#endif // BUILDFLAG(IS_ANDROID)
@@ -225,11 +221,6 @@ void VersionUI::AddVersionDetailStrings(content::WebUIDataSource* html_source) {
html_source->AddString(version_ui::kVersionModifier, GetProductModifier());
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
- auto* init_params = chromeos::BrowserParamsProxy::Get();
- html_source->AddString(version_ui::kAshChromeVersion,
- init_params->AshChromeVersion().value_or("0.0.0.0"));
-#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
html_source->AddString(version_ui::kJSEngine, "V8");
html_source->AddString(version_ui::kJSVersion, V8_VERSION_STRING);
html_source->AddString(
diff --git a/chromium/chrome/browser/ui/webui/waffle/DIR_METADATA b/chromium/chrome/browser/ui/webui/waffle/DIR_METADATA
deleted file mode 100644
index 6cfbfc28de5..00000000000
--- a/chromium/chrome/browser/ui/webui/waffle/DIR_METADATA
+++ /dev/null
@@ -1 +0,0 @@
-mixins: "//chrome/browser/ui/waffle/COMMON_METADATA"
diff --git a/chromium/chrome/browser/ui/webui/waffle/waffle.mojom b/chromium/chrome/browser/ui/webui/waffle/waffle.mojom
deleted file mode 100644
index 297c3fcdf10..00000000000
--- a/chromium/chrome/browser/ui/webui/waffle/waffle.mojom
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module waffle.mojom;
-
-// Lives in the browser process. A renderer uses this to link itself with
-// a page handler.
-interface PageHandlerFactory {
- // Creates a page handler for the file manager and link it to the UI.
- CreatePageHandler(pending_receiver<PageHandler> handler);
-};
-
-// Browser-side handler for requests from WebUI page.
-interface PageHandler {
- // Displays the Waffle dialog. Triggered by the displayDialog() call in
- // chrome/browser/resources/waffle/app.ts
- DisplayDialog();
-};
diff --git a/chromium/chrome/browser/ui/webui/waffle/waffle_handler.cc b/chromium/chrome/browser/ui/webui/waffle/waffle_handler.cc
deleted file mode 100644
index c9e3a5e5e01..00000000000
--- a/chromium/chrome/browser/ui/webui/waffle/waffle_handler.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/waffle/waffle_handler.h"
-
-#include "chrome/browser/signin/signin_features.h"
-
-WaffleHandler::WaffleHandler(
- mojo::PendingReceiver<waffle::mojom::PageHandler> receiver,
- base::OnceClosure display_dialog_callback)
- : receiver_(this, std::move(receiver)),
- display_dialog_callback_(std::move(display_dialog_callback)) {
- CHECK(base::FeatureList::IsEnabled(kWaffle));
- // `display_dialog_callback` being null would indicate that the handler is
- // created before calling `WaffleUI::Initialize()`, which should never happen.
- CHECK(display_dialog_callback_);
-}
-
-WaffleHandler::~WaffleHandler() = default;
-
-void WaffleHandler::DisplayDialog() {
- if (display_dialog_callback_) {
- std::move(display_dialog_callback_).Run();
- }
-}
diff --git a/chromium/chrome/browser/ui/webui/waffle/waffle_handler.h b/chromium/chrome/browser/ui/webui/waffle/waffle_handler.h
deleted file mode 100644
index e59bcb08687..00000000000
--- a/chromium/chrome/browser/ui/webui/waffle/waffle_handler.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_HANDLER_H_
-
-#include "base/functional/callback_forward.h"
-#include "chrome/browser/ui/webui/waffle/waffle.mojom.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-
-class WaffleHandler : public waffle::mojom::PageHandler {
- public:
- explicit WaffleHandler(
- mojo::PendingReceiver<waffle::mojom::PageHandler> receiver,
- base::OnceClosure display_dialog_callback);
-
- WaffleHandler(const WaffleHandler&) = delete;
- WaffleHandler& operator=(const WaffleHandler&) = delete;
-
- ~WaffleHandler() override;
-
- // waffle::mojom::PageHandler:
- void DisplayDialog() override;
-
- private:
- mojo::Receiver<waffle::mojom::PageHandler> receiver_;
- base::OnceClosure display_dialog_callback_;
-};
-
-#endif // CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/waffle/waffle_ui.cc b/chromium/chrome/browser/ui/webui/waffle/waffle_ui.cc
deleted file mode 100644
index 9ec01ba1e86..00000000000
--- a/chromium/chrome/browser/ui/webui/waffle/waffle_ui.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/waffle/waffle_ui.h"
-
-#include "base/feature_list.h"
-#include "base/functional/callback_forward.h"
-#include "base/json/json_writer.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/signin_features.h"
-#include "chrome/browser/ui/webui/waffle/waffle_handler.h"
-#include "chrome/browser/ui/webui/webui_util.h"
-#include "chrome/common/webui_url_constants.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/grit/signin_resources.h"
-#include "chrome/grit/waffle_resources.h"
-#include "chrome/grit/waffle_resources_map.h"
-#include "components/search_engines/template_url_data.h"
-#include "components/search_engines/template_url_prepopulate_data.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui_data_source.h"
-
-namespace {
-std::string GetChoiceListJSON(Profile* profile) {
- if (!profile) {
- return "";
- }
-
- base::Value::List choice_value_list;
- auto* pref_service = profile->GetPrefs();
- const std::vector<std::unique_ptr<TemplateURLData>> choices =
- TemplateURLPrepopulateData::GetPrepopulatedEngines(
- pref_service, /*default_search_provider_index=*/nullptr);
-
- for (const auto& choice : choices) {
- base::Value::Dict choice_value;
- choice_value.Set("name", choice->short_name());
- choice_value_list.Append(std::move(choice_value));
- }
-
- std::string json_choice_list;
- base::JSONWriter::Write(choice_value_list, &json_choice_list);
- return json_choice_list;
-}
-} // namespace
-
-WaffleUI::WaffleUI(content::WebUI* web_ui)
- : ui::MojoWebUIController(web_ui, true) {
- CHECK(base::FeatureList::IsEnabled(kWaffle));
- auto* profile = Profile::FromWebUI(web_ui);
-
- content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
- web_ui->GetWebContents()->GetBrowserContext(),
- chrome::kChromeUIWaffleHost);
-
- source->AddLocalizedString("title", IDS_WAFFLE_PAGE_TITLE);
- source->AddLocalizedString("subtitle", IDS_WAFFLE_PAGE_SUBTITLE);
- source->AddLocalizedString("firstButton", IDS_WAFFLE_FIRST_BUTTON_TITLE);
- source->AddLocalizedString("secondButton", IDS_WAFFLE_SECOND_BUTTON_TITLE);
-
- source->AddResourcePath("images/left_illustration.svg",
- IDR_SIGNIN_IMAGES_SHARED_LEFT_BANNER_SVG);
- source->AddResourcePath("images/left_illustration_dark.svg",
- IDR_SIGNIN_IMAGES_SHARED_LEFT_BANNER_DARK_SVG);
- source->AddResourcePath("images/right_illustration.svg",
- IDR_SIGNIN_IMAGES_SHARED_RIGHT_BANNER_SVG);
- source->AddResourcePath("images/right_illustration_dark.svg",
- IDR_SIGNIN_IMAGES_SHARED_RIGHT_BANNER_DARK_SVG);
-
- source->AddString("choiceList", GetChoiceListJSON(profile));
-
- webui::SetupWebUIDataSource(
- source, base::make_span(kWaffleResources, kWaffleResourcesSize),
- IDR_WAFFLE_WAFFLE_HTML);
-}
-
-WEB_UI_CONTROLLER_TYPE_IMPL(WaffleUI)
-
-WaffleUI::~WaffleUI() = default;
-
-void WaffleUI::BindInterface(
- mojo::PendingReceiver<waffle::mojom::PageHandlerFactory> receiver) {
- page_factory_receiver_.reset();
- page_factory_receiver_.Bind(std::move(receiver));
-}
-
-void WaffleUI::Initialize(base::OnceClosure display_dialog_callback) {
- CHECK(display_dialog_callback);
- display_dialog_callback_ = std::move(display_dialog_callback);
-}
-
-void WaffleUI::CreatePageHandler(
- mojo::PendingReceiver<waffle::mojom::PageHandler> receiver) {
- page_handler_ = std::make_unique<WaffleHandler>(
- std::move(receiver), std::move(display_dialog_callback_));
-}
diff --git a/chromium/chrome/browser/ui/webui/waffle/waffle_ui.h b/chromium/chrome/browser/ui/webui/waffle/waffle_ui.h
deleted file mode 100644
index 0e951e7ec71..00000000000
--- a/chromium/chrome/browser/ui/webui/waffle/waffle_ui.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_UI_H_
-
-#include "base/functional/callback_forward.h"
-#include "chrome/browser/ui/webui/waffle/waffle.mojom.h"
-#include "chrome/browser/ui/webui/waffle/waffle_handler.h"
-#include "ui/webui/mojo_web_ui_controller.h"
-
-// The WebUI controller for `chrome://waffle`.
-class WaffleUI : public ui::MojoWebUIController,
- public waffle::mojom::PageHandlerFactory {
- public:
- explicit WaffleUI(content::WebUI* web_ui);
-
- WaffleUI(const WaffleUI&) = delete;
- WaffleUI& operator=(const WaffleUI&) = delete;
-
- ~WaffleUI() override;
-
- // Instantiates the implementor of the mojom::PageHandlerFactory mojo
- // interface passing the pending receiver that will be internally bound.
- void BindInterface(
- mojo::PendingReceiver<waffle::mojom::PageHandlerFactory> receiver);
-
- // Initializes the callbacks that need to be passed to the handler.
- // `display_dialog_callback` is how we display the waffle dialog. It will
- // be called when the page content is laid out, so that the dialog will be
- // able to measure the page to fit to its size.
- void Initialize(base::OnceClosure display_dialog_callback);
-
- private:
- // waffle::mojom::PageHandlerFactory:
- void CreatePageHandler(
- mojo::PendingReceiver<waffle::mojom::PageHandler> receiver) override;
-
- std::unique_ptr<WaffleHandler> page_handler_;
-
- mojo::Receiver<waffle::mojom::PageHandlerFactory> page_factory_receiver_{
- this};
-
- base::OnceClosure display_dialog_callback_;
-
- WEB_UI_CONTROLLER_TYPE_DECL();
-};
-
-#endif // CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/web_app_internals/BUILD.gn b/chromium/chrome/browser/ui/webui/web_app_internals/BUILD.gn
index 5efa69d7dc9..2165998028d 100644
--- a/chromium/chrome/browser/ui/webui/web_app_internals/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/web_app_internals/BUILD.gn
@@ -6,6 +6,9 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojo_bindings") {
sources = [ "web_app_internals.mojom" ]
+
+ public_deps = [ "//url/mojom:url_mojom_gurl" ]
+
webui_module_path = "/"
use_typescript_sources = true
}
diff --git a/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals.mojom b/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals.mojom
index 56c1d94b264..e3087f79681 100644
--- a/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals.mojom
+++ b/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals.mojom
@@ -4,6 +4,13 @@
module mojom;
+import "url/mojom/url.mojom";
+
+struct InstallIsolatedWebAppFromDevProxyResult {
+ bool success; // true if app was installed successfully.
+ string error; // should only be set if the app failed to install.
+};
+
// Handles requests from chrome://web-app-internals.
// This is expected to be hosted in the browser process.
interface WebAppInternalsHandler {
@@ -12,4 +19,7 @@ interface WebAppInternalsHandler {
// Returns whether the clearing is successful.
[EnableIf=is_chromeos_lacros]
ClearExperimentalWebAppIsolationData() => (bool success);
+ // Returns whether the installation succeeded.
+ InstallIsolatedWebAppFromDevProxy(url.mojom.Url url) =>
+ (InstallIsolatedWebAppFromDevProxyResult result);
};
diff --git a/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.cc b/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.cc
index f614390f63c..f9ca838907b 100644
--- a/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.cc
+++ b/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.cc
@@ -16,6 +16,7 @@
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager.h"
#include "chrome/browser/web_applications/locks/web_app_lock_manager.h"
#include "chrome/browser/web_applications/preinstalled_web_app_manager.h"
#include "chrome/browser/web_applications/web_app.h"
@@ -48,7 +49,11 @@ constexpr char kInstalledWebApps[] = "InstalledWebApps";
constexpr char kPreinstalledWebAppConfigs[] = "PreinstalledWebAppConfigs";
constexpr char kUserUninstalledPreinstalledWebAppPrefs[] =
"UserUninstalledPreinstalledWebAppPrefs";
-constexpr char kExternallyManagedWebAppPrefs[] = "ExternallyManagedWebAppPrefs";
+constexpr char kWebAppPreferences[] = "WebAppPreferences";
+constexpr char kWebAppIphPreferences[] = "WebAppIphPreferences";
+constexpr char kWebAppMlPreferences[] = "WebAppMlPreferences";
+constexpr char kShouldGarbageCollectStoragePartitions[] =
+ "ShouldGarbageCollectStoragePartitions";
constexpr char kLockManager[] = "LockManager";
constexpr char kCommandManager[] = "CommandManager";
constexpr char kIconErrorLog[] = "IconErrorLog";
@@ -57,6 +62,9 @@ constexpr char kInstallationProcessErrorLog[] = "InstallationProcessErrorLog";
constexpr char kAppShimRegistryLocalStorage[] = "AppShimRegistryLocalStorage";
#endif
constexpr char kWebAppDirectoryDiskState[] = "WebAppDirectoryDiskState";
+#if BUILDFLAG(IS_CHROMEOS)
+constexpr char kIsolatedWebAppUpdateManager[] = "IsolatedWebAppUpdateManager";
+#endif
constexpr char kNeedsRecordWebAppDebugInfo[] =
"No debugging info available! Please enable: "
@@ -76,7 +84,10 @@ base::Value::Dict BuildIndexJson() {
index.Append(kInstalledWebApps);
index.Append(kPreinstalledWebAppConfigs);
index.Append(kUserUninstalledPreinstalledWebAppPrefs);
- index.Append(kExternallyManagedWebAppPrefs);
+ index.Append(kWebAppPreferences);
+ index.Append(kWebAppIphPreferences);
+ index.Append(kWebAppMlPreferences);
+ index.Append(kShouldGarbageCollectStoragePartitions);
index.Append(kLockManager);
index.Append(kCommandManager);
index.Append(kIconErrorLog);
@@ -84,6 +95,9 @@ base::Value::Dict BuildIndexJson() {
#if BUILDFLAG(IS_MAC)
index.Append(kAppShimRegistryLocalStorage);
#endif
+#if BUILDFLAG(IS_CHROMEOS)
+ index.Append(kIsolatedWebAppUpdateManager);
+#endif
index.Append(kWebAppDirectoryDiskState);
return root;
@@ -91,9 +105,7 @@ base::Value::Dict BuildIndexJson() {
base::Value::Dict BuildInstalledWebAppsJson(web_app::WebAppProvider& provider) {
base::Value::Dict root;
-
root.Set(kInstalledWebApps, provider.registrar_unsafe().AsDebugValue());
-
return root;
}
@@ -173,6 +185,38 @@ base::Value::Dict BuildUserUninstalledPreinstalledWebAppPrefsJson(
return root;
}
+base::Value::Dict BuildWebAppsPrefsJson(Profile* profile) {
+ base::Value::Dict root;
+ root.Set(kWebAppPreferences,
+ profile->GetPrefs()->GetDict(prefs::kWebAppsPreferences).Clone());
+ return root;
+}
+
+base::Value::Dict BuildWebAppIphPrefsJson(Profile* profile) {
+ base::Value::Dict root;
+ root.Set(
+ kWebAppIphPreferences,
+ profile->GetPrefs()->GetDict(prefs::kWebAppsAppAgnosticIphState).Clone());
+ return root;
+}
+
+base::Value::Dict BuildWebAppMlPrefsJson(Profile* profile) {
+ base::Value::Dict root;
+ root.Set(
+ kWebAppMlPreferences,
+ profile->GetPrefs()->GetDict(prefs::kWebAppsAppAgnosticMlState).Clone());
+ return root;
+}
+
+base::Value::Dict BuildShouldGarbageCollectStoragePartitionsPrefsJson(
+ Profile* profile) {
+ base::Value::Dict root;
+ root.Set(kShouldGarbageCollectStoragePartitions,
+ profile->GetPrefs()->GetBoolean(
+ prefs::kShouldGarbageCollectStoragePartitions));
+ return root;
+}
+
base::Value::Dict BuildLockManagerJson(web_app::WebAppProvider& provider) {
base::Value::Dict root;
root.Set(kLockManager,
@@ -235,6 +279,15 @@ base::Value::Dict BuildAppShimRegistryLocalStorageJson() {
}
#endif
+#if BUILDFLAG(IS_CHROMEOS)
+base::Value BuildIsolatedWebAppUpdaterManagerJson(
+ web_app::WebAppProvider& provider) {
+ return base::Value(
+ base::Value::Dict().Set(kIsolatedWebAppUpdateManager,
+ provider.iwa_update_manager().AsDebugValue()));
+}
+#endif
+
void BuildDirectoryState(base::FilePath file_or_folder,
base::Value::Dict* folder) {
base::File::Info info;
@@ -316,6 +369,10 @@ void WebAppInternalsHandler::BuildDebugInfo(
root.Append(BuildInstalledWebAppsJson(*provider));
root.Append(BuildPreinstalledWebAppConfigsJson(*provider));
root.Append(BuildUserUninstalledPreinstalledWebAppPrefsJson(profile));
+ root.Append(BuildWebAppsPrefsJson(profile));
+ root.Append(BuildWebAppIphPrefsJson(profile));
+ root.Append(BuildWebAppMlPrefsJson(profile));
+ root.Append(BuildShouldGarbageCollectStoragePartitionsPrefsJson(profile));
root.Append(BuildLockManagerJson(*provider));
root.Append(BuildCommandManagerJson(*provider));
root.Append(BuildIconErrorLogJson(*provider));
@@ -323,6 +380,9 @@ void WebAppInternalsHandler::BuildDebugInfo(
#if BUILDFLAG(IS_MAC)
root.Append(BuildAppShimRegistryLocalStorageJson());
#endif
+#if BUILDFLAG(IS_CHROMEOS)
+ root.Append(BuildIsolatedWebAppUpdaterManagerJson(*provider));
+#endif
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
base::BindOnce(&BuildWebAppDiskStateJson,
@@ -354,6 +414,47 @@ void WebAppInternalsHandler::GetDebugInfoAsJsonString(
std::move(value_to_string).Then(std::move(callback))));
}
+void WebAppInternalsHandler::InstallIsolatedWebAppFromDevProxy(
+ const GURL& url,
+ InstallIsolatedWebAppFromDevProxyCallback callback) {
+ if (!web_app::AreWebAppsEnabled(profile_)) {
+ ::mojom::InstallIsolatedWebAppFromDevProxyResult mojo_result;
+ mojo_result.success = false;
+ mojo_result.error = std::string("web apps not enabled");
+ std::move(callback).Run(mojo_result.Clone());
+ return;
+ }
+
+ auto* provider = web_app::WebAppProvider::GetForWebApps(profile_);
+ if (!provider) {
+ ::mojom::InstallIsolatedWebAppFromDevProxyResult mojo_result;
+ mojo_result.success = false;
+ mojo_result.error = std::string("could not get web app provider");
+ std::move(callback).Run(mojo_result.Clone());
+ return;
+ }
+
+ auto& manager = provider->isolated_web_app_installation_manager();
+ manager.InstallIsolatedWebAppFromDevModeProxy(
+ url, base::BindOnce(
+ &WebAppInternalsHandler::OnInstallIsolatedWebAppFromDevModeProxy,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void WebAppInternalsHandler::OnInstallIsolatedWebAppFromDevModeProxy(
+ WebAppInternalsHandler::InstallIsolatedWebAppFromDevProxyCallback callback,
+ web_app::IsolatedWebAppInstallationManager::
+ MaybeInstallIsolatedWebAppCommandSuccess result) {
+ ::mojom::InstallIsolatedWebAppFromDevProxyResult mojo_result;
+ if (result.has_value()) {
+ mojo_result.success = true;
+ } else {
+ mojo_result.success = false;
+ mojo_result.error = result.error();
+ }
+ std::move(callback).Run(mojo_result.Clone());
+}
+
#if BUILDFLAG(IS_CHROMEOS_LACROS)
void WebAppInternalsHandler::ClearExperimentalWebAppIsolationData(
ClearExperimentalWebAppIsolationDataCallback callback) {
diff --git a/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.h b/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.h
index ede274f1d9a..52ea84574b4 100644
--- a/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.h
+++ b/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.h
@@ -8,6 +8,7 @@
#include "base/functional/callback_forward.h"
#include "base/values.h"
#include "chrome/browser/ui/webui/web_app_internals/web_app_internals.mojom.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_installation_manager.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
@@ -33,14 +34,23 @@ class WebAppInternalsHandler : public mojom::WebAppInternalsHandler {
// mojom::WebAppInternalsHandler:
void GetDebugInfoAsJsonString(
GetDebugInfoAsJsonStringCallback callback) override;
+ void InstallIsolatedWebAppFromDevProxy(
+ const GURL& url,
+ InstallIsolatedWebAppFromDevProxyCallback callback) override;
#if BUILDFLAG(IS_CHROMEOS_LACROS)
void ClearExperimentalWebAppIsolationData(
ClearExperimentalWebAppIsolationDataCallback callback) override;
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
private:
+ void OnInstallIsolatedWebAppFromDevModeProxy(
+ InstallIsolatedWebAppFromDevProxyCallback callback,
+ web_app::IsolatedWebAppInstallationManager::
+ MaybeInstallIsolatedWebAppCommandSuccess result);
+
const raw_ptr<Profile> profile_;
mojo::Receiver<mojom::WebAppInternalsHandler> receiver_;
+ base::WeakPtrFactory<WebAppInternalsHandler> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_WEBUI_WEB_APP_INTERNALS_WEB_APP_INTERNALS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_ui.cc b/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_ui.cc
index 84aba547610..0948eadf0fa 100644
--- a/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/web_app_internals/web_app_internals_ui.cc
@@ -8,6 +8,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/web_app_internals/web_app_internals_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_dev_mode.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/web_app_internals_resources.h"
#include "chrome/grit/web_app_internals_resources_map.h"
@@ -29,9 +30,12 @@ WebAppInternalsUI::WebAppInternalsUI(content::WebUI* web_ui)
internals,
base::make_span(kWebAppInternalsResources, kWebAppInternalsResourcesSize),
IDR_WEB_APP_INTERNALS_WEB_APP_INTERNALS_HTML);
+ internals->UseStringsJs();
+ internals->AddBoolean(
+ "experimentalIsIwaDevModeEnabled",
+ web_app::IsIwaDevModeEnabled(Profile::FromWebUI(web_ui)));
#if BUILDFLAG(IS_CHROMEOS_LACROS)
- internals->UseStringsJs();
internals->AddBoolean(
"experimentalIsolationEnabled",
web_app::ResolveExperimentalWebAppIsolationFeature() !=
diff --git a/chromium/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc b/chromium/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc
index afbd8005f4d..bf627bae80b 100644
--- a/chromium/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc
@@ -66,8 +66,9 @@ class WebDialogWebContentsDelegateTest : public BrowserWithTestWindowTest {
TEST_F(WebDialogWebContentsDelegateTest, DoNothingMethodsTest) {
// None of the following calls should do anything.
history::HistoryAddPageArgs should_add_args(
- GURL(), base::Time::Now(), 0, 0, GURL(), history::RedirectList(),
- ui::PAGE_TRANSITION_TYPED, false, history::SOURCE_SYNCED, false, true);
+ GURL(), base::Time::Now(), 0, 0, absl::nullopt, GURL(),
+ history::RedirectList(), ui::PAGE_TRANSITION_TYPED, false,
+ history::SOURCE_SYNCED, false, true);
test_web_contents_delegate_->NavigationStateChanged(
nullptr, content::InvalidateTypes(0));
test_web_contents_delegate_->ActivateContents(nullptr);
diff --git a/chromium/chrome/browser/ui/webui/webapks/webapks_handler.cc b/chromium/chrome/browser/ui/webui/webapks/webapks_handler.cc
index b7ef4d41661..c0fdd66aab7 100644
--- a/chromium/chrome/browser/ui/webui/webapks/webapks_handler.cc
+++ b/chromium/chrome/browser/ui/webui/webapks/webapks_handler.cc
@@ -66,6 +66,10 @@ void WebApksHandler::OnWebApkInfoRetrieved(const WebApkInfo& webapk_info) {
ui::OptionalSkColorToString(webapk_info.theme_color));
result.Set("backgroundColor",
ui::OptionalSkColorToString(webapk_info.background_color));
+ result.Set("darkThemeColor",
+ ui::OptionalSkColorToString(webapk_info.dark_theme_color));
+ result.Set("darkBackgroundColor",
+ ui::OptionalSkColorToString(webapk_info.dark_background_color));
result.Set("lastUpdateCheckTimeMs",
webapk_info.last_update_check_time.ToJsTime());
result.Set("lastUpdateCompletionTimeMs",
diff --git a/chromium/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc b/chromium/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc
index 423121a03c9..4b429007a73 100644
--- a/chromium/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc
@@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/webui/webui_allowlist_provider.h"
-#include "components/content_settings/core/browser/content_settings_observer.h"
-#include "ui/webui/webui_allowlist.h"
-
#include <map>
#include <memory>
@@ -15,10 +11,14 @@
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
+#include "components/content_settings/core/browser/content_settings_observer.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/pref_names.h"
+#include "net/cookies/site_for_cookies.h"
+#include "ui/webui/webui_allowlist.h"
+#include "ui/webui/webui_allowlist_provider.h"
class WebUIAllowlistProviderTest : public ChromeRenderViewHostTestHarness {
public:
diff --git a/chromium/chrome/browser/ui/webui/webui_load_timer_browsertest.cc b/chromium/chrome/browser/ui/webui/webui_load_timer_browsertest.cc
index 40e275f26be..38cc6f8a638 100644
--- a/chromium/chrome/browser/ui/webui/webui_load_timer_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/webui_load_timer_browsertest.cc
@@ -47,7 +47,7 @@ class WebuiLoadTimerPrerenderTest : public WebuiLoadTimerTest {
base::Unretained(this))) {}
void SetUp() override {
- prerender_helper_.SetUp(embedded_test_server());
+ prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
InProcessBrowserTest::SetUp();
}
diff --git a/chromium/chrome/browser/ui/webui/webui_webview_browsertest.cc b/chromium/chrome/browser/ui/webui/webui_webview_browsertest.cc
index fbb29f524f5..9c0e5c91e9c 100644
--- a/chromium/chrome/browser/ui/webui/webui_webview_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/webui_webview_browsertest.cc
@@ -274,27 +274,6 @@ IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, AddContentScriptWithCode) {
base::Value(GetTestUrl("empty.html").spec())));
}
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-// Incognito on CrOS is tested by the usual AddContentScript - OOBE context is
-// running inside incognito (signin) profile.
-
-// TODO(crbug.com/662673) Flaky
-#define MAYBE_AddContentScriptIncognito DISABLED_AddContentScriptIncognito
-// Right now we only have incognito WebUI on CrOS, but this should
-// theoretically work for all platforms.
-IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest,
- MAYBE_AddContentScriptIncognito) {
- Browser* incognito_browser =
- OpenURLOffTheRecord(browser()->profile(), GetWebViewEnabledWebUIURL());
-
- SetWebUIInstance(
- incognito_browser->tab_strip_model()->GetActiveWebContents()->GetWebUI());
-
- ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
- "testAddContentScript", base::Value(GetTestUrl("empty.html").spec())));
-}
-#endif
-
IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, ContextMenuInspectElement) {
content::ContextMenuParams params;
content::WebContents* web_contents =
diff --git a/chromium/chrome/browser/ui/webui/welcome/welcome_handler.cc b/chromium/chrome/browser/ui/webui/welcome/welcome_handler.cc
index 2be44afc04f..4ccccd66ac7 100644
--- a/chromium/chrome/browser/ui/webui/welcome/welcome_handler.cc
+++ b/chromium/chrome/browser/ui/webui/welcome/welcome_handler.cc
@@ -12,7 +12,6 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_navigator.h"
-#include "chrome/browser/ui/profile_chooser_constants.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/common/url_constants.h"
#include "components/signin/public/base/signin_metrics.h"
@@ -64,7 +63,7 @@ void WelcomeHandler::HandleActivateSignIn(const base::Value::List& args) {
// them away to the NTP instead.
GoToNewTabPage();
} else {
- GURL redirect_url = GURL::EmptyGURL();
+ GURL redirect_url(chrome::kChromeUINewTabURL);
if (args.size() == 1U) {
const std::string& url_string = args[0].GetString();
redirect_url = GURL(url_string);
@@ -73,7 +72,6 @@ void WelcomeHandler::HandleActivateSignIn(const base::Value::List& args) {
Browser* browser = GetBrowser();
browser->signin_view_controller()->ShowSignin(
- profiles::BubbleViewMode::BUBBLE_VIEW_MODE_GAIA_SIGNIN,
signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE, redirect_url);
}
}
diff --git a/chromium/chrome/browser/ui/webui/whats_new/whats_new_handler.cc b/chromium/chrome/browser/ui/webui/whats_new/whats_new_handler.cc
index 8a534d0fc5b..d40b6d23b0c 100644
--- a/chromium/chrome/browser/ui/webui/whats_new/whats_new_handler.cc
+++ b/chromium/chrome/browser/ui/webui/whats_new/whats_new_handler.cc
@@ -39,11 +39,21 @@ void WhatsNewHandler::HandleInitialize(const base::Value::List& args) {
const std::string& callback_id = args[0].GetString();
AllowJavascript();
- ResolveJavascriptCallback(
- base::Value(callback_id),
- whats_new::IsRemoteContentDisabled()
- ? base::Value()
- : base::Value(whats_new::GetServerURL(true).spec()));
+
+ auto response = base::Value();
+ if (!whats_new::IsRemoteContentDisabled()) {
+ if (whats_new::IsRefreshVersion()) {
+ // If this is a refresh version, both m117 and m118 will use the
+ // m117 What's New version. This essentially does a client-side
+ // redirect when WN is auto-opened, which does not occur with the
+ // original implementation.
+ response = base::Value(whats_new::GetServerURLForRefresh().spec());
+ } else {
+ response = base::Value(whats_new::GetServerURL(true).spec());
+ }
+ }
+
+ ResolveJavascriptCallback(base::Value(callback_id), response);
TryShowHatsSurveyWithTimeout();
}
diff --git a/chromium/chrome/browser/ui/webui/whats_new/whats_new_util.cc b/chromium/chrome/browser/ui/webui/whats_new/whats_new_util.cc
index 50de522b228..90b485d2357 100644
--- a/chromium/chrome/browser/ui/webui/whats_new/whats_new_util.cc
+++ b/chromium/chrome/browser/ui/webui/whats_new/whats_new_util.cc
@@ -89,7 +89,8 @@ bool ShouldShowRefresh(PrefService* local_state) {
}
// Show refresh page if user has flag enabled.
- return features::IsChromeRefresh2023();
+ return features::IsChromeRefresh2023() &&
+ features::IsChromeWebuiRefresh2023();
}
bool ShouldShowForState(PrefService* local_state,
@@ -152,6 +153,11 @@ bool ShouldShowForState(PrefService* local_state,
return true;
}
+GURL GetServerURLForRefresh() {
+ return net::AppendQueryParameter(GURL(kChromeWhatsNewRefreshURL), "internal",
+ "true");
+}
+
GURL GetServerURL(bool may_redirect) {
const GURL url =
may_redirect
@@ -316,9 +322,7 @@ void StartWhatsNewFetch(Browser* browser) {
// display again. ShouldShowRefresh should not be called after this
// boolean is set to true.
local_state->SetBoolean(prefs::kHasShownRefreshWhatsNew, true);
- new WhatsNewFetcher(
- browser, net::AppendQueryParameter(GURL(kChromeWhatsNewRefreshURL),
- "internal", "true"));
+ new WhatsNewFetcher(browser, GetServerURLForRefresh());
return;
}
new WhatsNewFetcher(browser);
diff --git a/chromium/chrome/browser/ui/webui/whats_new/whats_new_util.h b/chromium/chrome/browser/ui/webui/whats_new/whats_new_util.h
index 165372efe35..c8ea357f364 100644
--- a/chromium/chrome/browser/ui/webui/whats_new/whats_new_util.h
+++ b/chromium/chrome/browser/ui/webui/whats_new/whats_new_util.h
@@ -90,6 +90,10 @@ bool ShouldShowForState(PrefService* local_state,
// version, which may return 404 if there is no page for this milestone.
GURL GetServerURL(bool may_redirect);
+// Same as GetServerURL, except version m117 and m118 are hard-coded to
+// the same What's New version.
+GURL GetServerURLForRefresh();
+
// Return the startup URL for the WebUI page.
GURL GetWebUIStartupURL();
diff --git a/chromium/chrome/browser/ui/webui/whats_new/whats_new_util_unittest.cc b/chromium/chrome/browser/ui/webui/whats_new/whats_new_util_unittest.cc
index ea5c4867284..10598d898cb 100644
--- a/chromium/chrome/browser/ui/webui/whats_new/whats_new_util_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/whats_new/whats_new_util_unittest.cc
@@ -33,23 +33,37 @@ TEST(WhatsNewUtil, GetServerURL) {
class WhatsNewUtilTests : public testing::Test {
public:
+ enum class EnableMode { kNotEnabled, kViewsRefreshOnly, kEnabled };
+
WhatsNewUtilTests(const WhatsNewUtilTests&) = delete;
WhatsNewUtilTests& operator=(const WhatsNewUtilTests&) = delete;
void SetUp() override {
- scoped_feature_list_.InitWithFeatures(
- {features::kChromeWhatsNewUI, features::kChromeRefresh2023}, {});
+ ToggleRefresh(EnableMode::kEnabled);
prefs_.registry()->RegisterBooleanPref(prefs::kHasShownRefreshWhatsNew,
false);
- prefs_.registry()->RegisterIntegerPref(prefs::kLastWhatsNewVersion, 116);
+ prefs_.registry()->RegisterIntegerPref(prefs::kLastWhatsNewVersion,
+ CHROME_VERSION_MAJOR);
}
- void ToggleRefresh(bool enabled) {
+ void ToggleRefresh(EnableMode mode) {
scoped_feature_list_.Reset();
- if (enabled) {
- scoped_feature_list_.InitAndEnableFeature(features::kChromeRefresh2023);
- } else {
- scoped_feature_list_.InitAndDisableFeature(features::kChromeRefresh2023);
+ switch (mode) {
+ case EnableMode::kEnabled:
+ scoped_feature_list_.InitWithFeatures(
+ {features::kChromeWhatsNewUI, features::kChromeRefresh2023,
+ features::kChromeWebuiRefresh2023},
+ {});
+ break;
+ case EnableMode::kViewsRefreshOnly:
+ scoped_feature_list_.InitWithFeatures(
+ {features::kChromeWhatsNewUI, features::kChromeRefresh2023},
+ {features::kChromeWebuiRefresh2023});
+ break;
+ case EnableMode::kNotEnabled:
+ scoped_feature_list_.InitWithFeatures(
+ {features::kChromeWhatsNewUI},
+ {features::kChromeRefresh2023, features::kChromeWebuiRefresh2023});
}
}
@@ -57,6 +71,16 @@ class WhatsNewUtilTests : public testing::Test {
prefs()->SetBoolean(prefs::kHasShownRefreshWhatsNew, has_shown);
}
+ // The check in whats_new_util.cc compares the CHROME_VERSION_MAJOR
+ // macro to the value stored in the kLastWhatsNewVersion pref. This
+ // method sets whether that check should pass (should show milestone
+ // WNP) or fail (current milestone WNP has already been shown).
+ void SetHasNewWhatsNewVersion(const bool& has_new_version) {
+ prefs_.SetInteger(
+ prefs::kLastWhatsNewVersion,
+ has_new_version ? CHROME_VERSION_MAJOR - 1 : CHROME_VERSION_MAJOR);
+ }
+
PrefService* prefs() { return &prefs_; }
protected:
@@ -77,14 +101,19 @@ TEST_F(WhatsNewUtilTests, ShouldShowRefresh) {
ToggleHasShownRefresh(true);
EXPECT_FALSE(whats_new::ShouldShowRefresh(prefs()));
- // Disable Refresh 2023 feature
- ToggleRefresh(false);
+ // Disable Refresh 2023 feature2
+ ToggleRefresh(EnableMode::kNotEnabled);
// kChromeRefresh2023=disabled && hasShownRefreshWhatsNew=true
EXPECT_FALSE(whats_new::ShouldShowRefresh(prefs()));
// kChromeRefresh2023=disabled && hasShownRefreshWhatsNew=false
ToggleHasShownRefresh(false);
EXPECT_FALSE(whats_new::ShouldShowRefresh(prefs()));
+
+ // Enable only Views refresh.
+ ToggleRefresh(EnableMode::kViewsRefreshOnly);
+ // kChromeRefresh2023=disabled && hasShownRefreshWhatsNew=true
+ EXPECT_FALSE(whats_new::ShouldShowRefresh(prefs()));
}
TEST_F(WhatsNewUtilTests, ShouldShowForStateUsesChromeVersionForRefresh) {
@@ -102,11 +131,13 @@ TEST_F(WhatsNewUtilTests, ShouldShowForStateUsesChromeVersionForRefresh) {
// kChromeRefresh2023=enabled && hasShownRefreshWhatsNew=false
whats_new::SetChromeVersionForTests(116);
ToggleHasShownRefresh(false);
+ SetHasNewWhatsNewVersion(false);
// Refresh page should not show previous to 117
EXPECT_FALSE(whats_new::ShouldShowForState(prefs(), true));
// kChromeRefresh2023=enabled && hasShownRefreshWhatsNew=false
whats_new::SetChromeVersionForTests(119);
+ SetHasNewWhatsNewVersion(true);
// Refresh page should show for versions after 118 if it has not been
// shown yet
EXPECT_TRUE(whats_new::ShouldShowForState(prefs(), true));
diff --git a/chromium/chrome/browser/updater/BUILD.gn b/chromium/chrome/browser/updater/BUILD.gn
index aa0b34e5841..ac4300ae4fc 100644
--- a/chromium/chrome/browser/updater/BUILD.gn
+++ b/chromium/chrome/browser/updater/BUILD.gn
@@ -44,7 +44,6 @@ source_set("browser_updater_client") {
sources += [ "browser_updater_client_util_no_updater.cc" ]
}
- configs += [ "//build/config/compiler:enable_arc" ]
deps += [
"//chrome/app:chromium_strings_grit",
"//chrome/app:generated_resources_grit",
diff --git a/chromium/chrome/browser/usb/android/BUILD.gn b/chromium/chrome/browser/usb/android/BUILD.gn
index b9fa07d399b..ad88b4926d1 100644
--- a/chromium/chrome/browser/usb/android/BUILD.gn
+++ b/chromium/chrome/browser/usb/android/BUILD.gn
@@ -52,13 +52,14 @@ robolectric_library("junit") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//chrome/test/android:chrome_java_unit_test_support",
+ "//components/browser_ui/notifications/android:java",
"//components/browser_ui/notifications/android:test_support_java",
"//components/url_formatter/android:url_formatter_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
"//url:gurl_java",
- "//url:gurl_junit_shadows",
"//url:gurl_junit_test_support",
]
}
diff --git a/chromium/chrome/browser/util/BUILD.gn b/chromium/chrome/browser/util/BUILD.gn
index 377473463b6..06092524332 100644
--- a/chromium/chrome/browser/util/BUILD.gn
+++ b/chromium/chrome/browser/util/BUILD.gn
@@ -24,6 +24,7 @@ android_library("java") {
"//components/embedder_support/android:util_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_core_core_java",
+ "//ui/accessibility:ax_base_java",
"//ui/android:ui_full_java",
"//url:gurl_java",
]
diff --git a/chromium/chrome/browser/vr/BUILD.gn b/chromium/chrome/browser/vr/BUILD.gn
index a61b7c0a1b4..7cda321235d 100644
--- a/chromium/chrome/browser/vr/BUILD.gn
+++ b/chromium/chrome/browser/vr/BUILD.gn
@@ -4,7 +4,6 @@
import("//build/buildflag_header.gni")
import("//chrome/android/modules/buildflags.gni")
-import("//chrome/browser/vr/features.gni")
import("//chrome/common/features.gni")
import("//device/vr/buildflags/buildflags.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
@@ -17,80 +16,31 @@ if (is_android) {
assert(enable_vr)
-buildflag_header("vr_buildflags") {
- header = "vr_buildflags.h"
- flags = [ "USE_VR_ASSETS_COMPONENT=$use_vr_assets_component" ]
-}
-
component("vr_ui") {
sources = [
"databinding/binding.h",
"databinding/binding_base.h",
- "databinding/vector_binding.h",
- "databinding/vector_element_binding.h",
- "elements/button.cc",
- "elements/button.h",
- "elements/content_element.cc",
- "elements/content_element.h",
- "elements/controller.cc",
- "elements/controller.h",
"elements/corner_radii.h",
- "elements/disc_button.cc",
- "elements/disc_button.h",
"elements/draw_phase.cc",
"elements/draw_phase.h",
- "elements/environment/background.cc",
- "elements/environment/background.h",
"elements/environment/grid.cc",
"elements/environment/grid.h",
- "elements/environment/stars.cc",
- "elements/environment/stars.h",
"elements/full_screen_rect.cc",
"elements/full_screen_rect.h",
"elements/indicator_spec.cc",
"elements/indicator_spec.h",
- "elements/invisible_hit_target.cc",
- "elements/invisible_hit_target.h",
- "elements/laser.cc",
- "elements/laser.h",
"elements/linear_layout.cc",
"elements/linear_layout.h",
- "elements/omnibox_formatting.cc",
- "elements/omnibox_formatting.h",
- "elements/omnibox_text_field.cc",
- "elements/omnibox_text_field.h",
- "elements/oval.cc",
- "elements/oval.h",
- "elements/platform_ui_element.cc",
- "elements/platform_ui_element.h",
"elements/rect.cc",
"elements/rect.h",
- "elements/render_text_wrapper.cc",
- "elements/render_text_wrapper.h",
- "elements/repositioner.cc",
- "elements/repositioner.h",
- "elements/resizer.cc",
- "elements/resizer.h",
- "elements/reticle.cc",
- "elements/reticle.h",
"elements/scaled_depth_adjuster.cc",
"elements/scaled_depth_adjuster.h",
- "elements/scrollable_element.cc",
- "elements/scrollable_element.h",
- "elements/shadow.cc",
- "elements/shadow.h",
"elements/spinner.cc",
"elements/spinner.h",
"elements/text.cc",
"elements/text.h",
- "elements/text_button.cc",
- "elements/text_button.h",
- "elements/text_input.cc",
- "elements/text_input.h",
"elements/textured_element.cc",
"elements/textured_element.h",
- "elements/throbber.cc",
- "elements/throbber.h",
"elements/transient_element.cc",
"elements/transient_element.h",
"elements/ui_element.cc",
@@ -101,30 +51,20 @@ component("vr_ui") {
"elements/ui_element_type.h",
"elements/ui_texture.cc",
"elements/ui_texture.h",
- "elements/url_text.cc",
- "elements/url_text.h",
"elements/vector_icon.cc",
"elements/vector_icon.h",
- "elements/vector_icon_button.cc",
- "elements/vector_icon_button.h",
"elements/viewport_aware_root.cc",
"elements/viewport_aware_root.h",
"frame_lifecycle.cc",
"frame_lifecycle.h",
"model/color_scheme.cc",
"model/color_scheme.h",
- "model/modal_prompt_type.cc",
- "model/modal_prompt_type.h",
"model/model.cc",
"model/model.h",
- "model/platform_toast.cc",
- "model/platform_toast.h",
"renderers/base_quad_renderer.cc",
"renderers/base_quad_renderer.h",
"renderers/base_renderer.cc",
"renderers/base_renderer.h",
- "renderers/external_textured_quad_renderer.cc",
- "renderers/external_textured_quad_renderer.h",
"renderers/radial_gradient_quad_renderer.cc",
"renderers/radial_gradient_quad_renderer.h",
"renderers/texture_copy_renderer.cc",
@@ -133,8 +73,6 @@ component("vr_ui") {
"renderers/textured_quad_renderer.h",
"renderers/transparent_quad_renderer.cc",
"renderers/transparent_quad_renderer.h",
- "sequence.cc",
- "sequence.h",
"skia_surface_provider.cc",
"skia_surface_provider.h",
"skia_surface_provider_factory.h",
@@ -160,8 +98,6 @@ component("vr_ui") {
"//chrome/app/vector_icons",
"//chrome/browser/vr/vector_icons",
"//chrome/common:constants",
- "//components/omnibox/browser:vector_icons",
- "//components/url_formatter",
"//components/vector_icons",
"//device/base",
"//device/vr:vr_base",
@@ -216,13 +152,9 @@ component("vr_common") {
"gesture_detector.h",
"graphics_delegate.h",
"input_delegate.h",
- "location_bar_helper.cc",
- "location_bar_helper.h",
"platform_controller.h",
"scheduler_browser_renderer_interface.h",
"scheduler_delegate.h",
- "sounds_manager_audio_delegate.cc",
- "sounds_manager_audio_delegate.h",
"vr_export.h",
"vr_web_contents_observer.cc",
"vr_web_contents_observer.h",
@@ -294,57 +226,25 @@ component("vr_common") {
# vr_ui must not depend on vr_common.
component("vr_base") {
sources = [
- "assets_load_status.h",
- "assets_loader.cc",
- "assets_loader.h",
- "audio_delegate.h",
"browser_ui_interface.h",
- "content_input_delegate.cc",
- "content_input_delegate.h",
- "exit_vr_prompt_choice.h",
"fov_rectangle.h",
- "gl_texture_location.h",
"input_event.cc",
"input_event.h",
"macros.h",
- "model/assets.cc",
- "model/assets.h",
"model/camera_model.h",
"model/capturing_state_model.h",
"model/controller_model.cc",
"model/controller_model.h",
- "model/hosted_platform_ui.h",
- "model/location_bar_state.cc",
- "model/location_bar_state.h",
- "model/omnibox_suggestions.cc",
- "model/omnibox_suggestions.h",
- "model/reticle_model.h",
- "model/speech_recognition_model.h",
- "model/text_input_info.cc",
- "model/text_input_info.h",
- "model/ui_mode.h",
"model/web_vr_model.h",
- "platform_input_handler.h",
- "platform_ui_input_delegate.cc",
- "platform_ui_input_delegate.h",
"pose_util.cc",
"pose_util.h",
"render_info.h",
"scheduler_ui_interface.h",
- "speech_recognizer.cc",
- "speech_recognizer.h",
- "text_edit_action.cc",
- "text_edit_action.h",
- "text_input_delegate.cc",
- "text_input_delegate.h",
"ui_browser_interface.h",
"ui_initial_state.cc",
"ui_initial_state.h",
"ui_interface.h",
- "ui_support.cc",
- "ui_support.h",
"ui_test_input.h",
- "ui_unsupported_mode.h",
"vr_base_export.h",
"vr_geometry_util.cc",
"vr_geometry_util.h",
@@ -353,7 +253,6 @@ component("vr_base") {
defines = [ "VR_BASE_IMPLEMENTATION" ]
public_deps = [
- ":vr_buildflags",
"//components/omnibox/browser",
"//components/strings:components_strings_grit",
"//content/public/common",
@@ -386,31 +285,16 @@ test("vr_common_unittests") {
"base_scheduler_delegate_unittest.cc",
"browser_renderer_unittest.cc",
"databinding/binding_unittest.cc",
- "databinding/vector_binding_unittest.cc",
- "elements/button_unittest.cc",
- "elements/disc_button_unittest.cc",
"elements/linear_layout_unittest.cc",
- "elements/omnibox_formatting_unittest.cc",
- "elements/omnibox_text_field_unittest.cc",
- "elements/oval_unittest.cc",
"elements/rect_unittest.cc",
- "elements/repositioner_unittest.cc",
- "elements/resizer_unittest.cc",
"elements/scaled_depth_adjuster_unittest.cc",
- "elements/scrollable_element_unittest.cc",
- "elements/shadow_unittest.cc",
"elements/spinner_unittest.cc",
"elements/text_unittest.cc",
- "elements/throbber_unittest.cc",
"elements/transient_element_unittest.cc",
"elements/ui_element_unittest.cc",
- "elements/vector_icon_button_unittest.cc",
"elements/vector_icon_unittest.cc",
"elements/viewport_aware_root_unittest.cc",
"gesture_detector_unittest.cc",
- "model/text_input_info_unittest.cc",
- "platform_ui_input_delegate_unittest.cc",
- "speech_recognizer_unittest.cc",
"test/paths.cc",
"test/paths.h",
"test/run_all_unittests.cc",
@@ -418,11 +302,11 @@ test("vr_common_unittests") {
"test/ui_test.h",
"ui_scene_unittest.cc",
"ui_unittest.cc",
- "vr_geometry_util_unittest.cc",
]
deps = [
":vr_test_support",
+ "//chrome/app/vector_icons",
"//chrome/browser",
"//chrome/test:test_support",
"//components/url_formatter",
@@ -474,12 +358,6 @@ source_set("vr_test_support") {
"test/constants.h",
"test/mock_browser_ui_interface.cc",
"test/mock_browser_ui_interface.h",
- "test/mock_content_input_delegate.cc",
- "test/mock_content_input_delegate.h",
- "test/mock_render_text.cc",
- "test/mock_render_text.h",
- "test/mock_text_input_delegate.cc",
- "test/mock_text_input_delegate.h",
"test/mock_ui_browser_interface.cc",
"test/mock_ui_browser_interface.h",
"test/vr_test_suite.cc",
@@ -491,8 +369,6 @@ source_set("vr_test_support") {
":vr_ui",
"//base/test:test_support",
"//cc:test_support",
- "//components/omnibox/browser",
- "//components/omnibox/browser:vector_icons",
"//components/security_state/core",
"//content/test:test_support",
"//mojo/core/embedder",
@@ -627,12 +503,3 @@ if (!is_android) {
}
}
}
-
-fuzzer_test("vr_omnibox_formatting_fuzzer") {
- sources = [ "elements/omnibox_formatting_fuzzer.cc" ]
- deps = [
- ":vr_common",
- ":vr_ui",
- "//ui/gfx:test_support",
- ]
-}
diff --git a/chromium/chrome/browser/vr/features.gni b/chromium/chrome/browser/vr/features.gni
deleted file mode 100644
index 4da822b2c64..00000000000
--- a/chromium/chrome/browser/vr/features.gni
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2017 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/chrome_build.gni")
-import("//device/vr/buildflags/buildflags.gni")
-
-assert(enable_vr)
-
-declare_args() {
- # Whether to register, download, etc. the VR assets component.
- use_vr_assets_component = is_chrome_branded
-}
diff --git a/chromium/chrome/browser/vr/vector_icons/BUILD.gn b/chromium/chrome/browser/vr/vector_icons/BUILD.gn
index e36f33134cf..db901debaf6 100644
--- a/chromium/chrome/browser/vr/vector_icons/BUILD.gn
+++ b/chromium/chrome/browser/vr/vector_icons/BUILD.gn
@@ -7,16 +7,7 @@ import("//components/vector_icons/vector_icons.gni")
aggregate_vector_icons("vr_vector_icons") {
icon_directory = "."
- sources = [
- "daydream_controller_app_button.icon",
- "daydream_controller_home_button.icon",
- "file_download_done.icon",
- "more_vert.icon",
- "my_location.icon",
- "remove_circle_outline.icon",
- "reposition.icon",
- "sad_tab.icon",
- ]
+ sources = [ "remove_circle_outline.icon" ]
}
source_set("vector_icons") {
diff --git a/chromium/chrome/browser/web_applications/BUILD.gn b/chromium/chrome/browser/web_applications/BUILD.gn
index ad4c66e5afd..77c7fbb400d 100644
--- a/chromium/chrome/browser/web_applications/BUILD.gn
+++ b/chromium/chrome/browser/web_applications/BUILD.gn
@@ -17,6 +17,8 @@ source_set("web_applications") {
"commands/dedupe_install_urls_command.h",
"commands/externally_managed_install_command.cc",
"commands/externally_managed_install_command.h",
+ "commands/fetch_install_info_from_install_url_command.cc",
+ "commands/fetch_install_info_from_install_url_command.h",
"commands/fetch_installability_for_chrome_management.cc",
"commands/fetch_installability_for_chrome_management.h",
"commands/fetch_manifest_and_install_command.cc",
@@ -29,8 +31,6 @@ source_set("web_applications") {
"commands/install_from_sync_command.h",
"commands/install_placeholder_command.cc",
"commands/install_placeholder_command.h",
- "commands/install_preloaded_verified_app_command.cc",
- "commands/install_preloaded_verified_app_command.h",
"commands/manifest_update_check_command.cc",
"commands/manifest_update_check_command.h",
"commands/manifest_update_finalize_command.cc",
@@ -41,8 +41,8 @@ source_set("web_applications") {
"commands/os_integration_synchronize_command.h",
"commands/run_on_os_login_command.cc",
"commands/run_on_os_login_command.h",
- "commands/sub_app_install_command.cc",
- "commands/sub_app_install_command.h",
+ "commands/uninstall_all_user_installed_web_apps_command.cc",
+ "commands/uninstall_all_user_installed_web_apps_command.h",
"commands/update_file_handler_command.cc",
"commands/update_file_handler_command.h",
"commands/update_protocol_handler_approval_command.cc",
@@ -54,6 +54,7 @@ source_set("web_applications") {
"daily_metrics_helper.cc",
"daily_metrics_helper.h",
"extension_status_utils.h",
+ "extensions_manager.h",
"external_install_options.cc",
"external_install_options.h",
"externally_managed_app_install_task.cc",
@@ -70,22 +71,28 @@ source_set("web_applications") {
"isolated_web_apps/error/uma_logging.h",
"isolated_web_apps/error/unusable_swbn_file_error.cc",
"isolated_web_apps/error/unusable_swbn_file_error.h",
+ "isolated_web_apps/garbage_collect_storage_partitions_command.cc",
+ "isolated_web_apps/garbage_collect_storage_partitions_command.h",
"isolated_web_apps/get_controlled_frame_partition_command.cc",
"isolated_web_apps/get_controlled_frame_partition_command.h",
"isolated_web_apps/get_isolated_web_app_browsing_data_command.cc",
"isolated_web_apps/get_isolated_web_app_browsing_data_command.h",
"isolated_web_apps/install_isolated_web_app_command.cc",
"isolated_web_apps/install_isolated_web_app_command.h",
- "isolated_web_apps/install_isolated_web_app_from_command_line.cc",
- "isolated_web_apps/install_isolated_web_app_from_command_line.h",
+ "isolated_web_apps/isolated_web_app_apply_update_command.cc",
+ "isolated_web_apps/isolated_web_app_apply_update_command.h",
"isolated_web_apps/isolated_web_app_dev_mode.cc",
"isolated_web_apps/isolated_web_app_dev_mode.h",
"isolated_web_apps/isolated_web_app_error_page.cc",
"isolated_web_apps/isolated_web_app_error_page.h",
"isolated_web_apps/isolated_web_app_install_command_helper.cc",
"isolated_web_apps/isolated_web_app_install_command_helper.h",
+ "isolated_web_apps/isolated_web_app_installation_manager.cc",
+ "isolated_web_apps/isolated_web_app_installation_manager.h",
"isolated_web_apps/isolated_web_app_location.cc",
"isolated_web_apps/isolated_web_app_location.h",
+ "isolated_web_apps/isolated_web_app_prepare_and_store_update_command.cc",
+ "isolated_web_apps/isolated_web_app_prepare_and_store_update_command.h",
"isolated_web_apps/isolated_web_app_reader_registry.cc",
"isolated_web_apps/isolated_web_app_reader_registry.h",
"isolated_web_apps/isolated_web_app_reader_registry_factory.cc",
@@ -106,8 +113,19 @@ source_set("web_applications") {
"isolated_web_apps/isolated_web_app_version.h",
"isolated_web_apps/pending_install_info.cc",
"isolated_web_apps/pending_install_info.h",
+ "isolated_web_apps/remove_isolated_web_app_browsing_data.cc",
+ "isolated_web_apps/remove_isolated_web_app_browsing_data.h",
"isolated_web_apps/signed_web_bundle_reader.cc",
"isolated_web_apps/signed_web_bundle_reader.h",
+ "jobs/uninstall/remove_install_source_job.cc",
+ "jobs/uninstall/remove_install_source_job.h",
+ "jobs/uninstall/remove_install_url_job.cc",
+ "jobs/uninstall/remove_install_url_job.h",
+ "jobs/uninstall/remove_web_app_job.cc",
+ "jobs/uninstall/remove_web_app_job.h",
+ "jobs/uninstall/uninstall_job.cc",
+ "jobs/uninstall/uninstall_job.h",
+ "jobs/uninstall/web_app_uninstall_and_replace_job.h",
"locks/all_apps_lock.cc",
"locks/all_apps_lock.h",
"locks/app_lock.cc",
@@ -187,14 +205,6 @@ source_set("web_applications") {
"preinstalled_web_apps/preinstalled_web_apps.h",
"scope_extension_info.cc",
"scope_extension_info.h",
- "uninstall/remove_install_source_job.cc",
- "uninstall/remove_install_source_job.h",
- "uninstall/remove_install_url_job.cc",
- "uninstall/remove_install_url_job.h",
- "uninstall/remove_web_app_job.cc",
- "uninstall/remove_web_app_job.h",
- "uninstall/uninstall_job.cc",
- "uninstall/uninstall_job.h",
"user_display_mode.cc",
"user_display_mode.h",
"user_uninstalled_preinstalled_web_app_prefs.cc",
@@ -260,7 +270,6 @@ source_set("web_applications") {
"web_app_registrar_observer.h",
"web_app_registry_update.cc",
"web_app_registry_update.h",
- "web_app_sources.h",
"web_app_sync_bridge.cc",
"web_app_sync_bridge.h",
"web_app_tab_helper.cc",
@@ -269,7 +278,7 @@ source_set("web_applications") {
"web_app_translation_manager.h",
"web_app_ui_manager.cc",
"web_app_ui_manager.h",
- "web_app_uninstall_and_replace_job.h",
+ "web_app_uninstall_dialog_user_options.h",
"web_app_utils.cc",
"web_app_utils.h",
"web_contents/web_app_data_retriever.cc",
@@ -286,8 +295,18 @@ source_set("web_applications") {
sources += [
"chromeos_web_app_experiments.cc",
"chromeos_web_app_experiments.h",
+ "commands/install_preloaded_verified_app_command.cc",
+ "commands/install_preloaded_verified_app_command.h",
"isolated_web_apps/isolated_web_app_downloader.cc",
"isolated_web_apps/isolated_web_app_downloader.h",
+ "isolated_web_apps/isolated_web_app_update_apply_task.cc",
+ "isolated_web_apps/isolated_web_app_update_apply_task.h",
+ "isolated_web_apps/isolated_web_app_update_apply_waiter.cc",
+ "isolated_web_apps/isolated_web_app_update_apply_waiter.h",
+ "isolated_web_apps/isolated_web_app_update_discovery_task.cc",
+ "isolated_web_apps/isolated_web_app_update_discovery_task.h",
+ "isolated_web_apps/isolated_web_app_update_manager.cc",
+ "isolated_web_apps/isolated_web_app_update_manager.h",
"isolated_web_apps/policy/isolated_web_app_external_install_options.cc",
"isolated_web_apps/policy/isolated_web_app_external_install_options.h",
"isolated_web_apps/policy/isolated_web_app_policy_constants.cc",
@@ -342,7 +361,6 @@ source_set("web_applications") {
"os_integration/web_app_shortcut_mac.h",
"os_integration/web_app_shortcut_mac.mm",
]
- configs += [ "//build/config/compiler:enable_arc" ]
}
if (is_win) {
@@ -389,6 +407,7 @@ source_set("web_applications") {
"//chrome/browser:browser_process",
"//chrome/browser/apps:user_type_filter",
"//chrome/browser/apps/app_service:constants",
+ "//chrome/browser/browsing_data:constants",
"//chrome/browser/favicon",
"//chrome/browser/profiles:profile",
"//chrome/common",
@@ -488,6 +507,7 @@ source_set("web_applications") {
"//ash/webui/system_apps/public:system_web_app_type",
"//chrome/browser/ash/crosapi:browser_util",
"//chrome/browser/ash/system_web_apps/types",
+ "//chromeos/ash/components/browser_context_helper",
]
}
@@ -506,8 +526,7 @@ source_set("web_applications") {
"//chrome/browser/web_applications/mojom:mojom_web_apps_enum",
"//chrome/browser/web_applications/proto",
"//components/permissions:permissions",
- "//components/services/app_service/public/cpp:app_share_target",
- "//components/services/app_service/public/cpp:app_url_handling",
+ "//components/services/app_service",
]
}
@@ -517,16 +536,14 @@ source_set("web_applications_test_support") {
testonly = true
sources = [
- "test/app_registry_cache_waiter.cc",
- "test/app_registry_cache_waiter.h",
+ "test/debug_info_printer.cc",
+ "test/debug_info_printer.h",
"test/external_app_registration_waiter.cc",
"test/external_app_registration_waiter.h",
"test/fake_data_retriever.cc",
"test/fake_data_retriever.h",
"test/fake_externally_managed_app_manager.cc",
"test/fake_externally_managed_app_manager.h",
- "test/fake_install_finalizer.cc",
- "test/fake_install_finalizer.h",
"test/fake_os_integration_manager.cc",
"test/fake_os_integration_manager.h",
"test/fake_url_handler_manager.cc",
@@ -551,8 +568,6 @@ source_set("web_applications_test_support") {
"test/mock_os_integration_manager.h",
"test/os_integration_test_override_impl.cc",
"test/os_integration_test_override_impl.h",
- "test/service_worker_registration_waiter.cc",
- "test/service_worker_registration_waiter.h",
"test/signed_web_bundle_utils.cc",
"test/signed_web_bundle_utils.h",
"test/test_file_utils.cc",
@@ -574,7 +589,7 @@ source_set("web_applications_test_support") {
"test/with_crosapi_param.cc",
"test/with_crosapi_param.h",
]
-
+ public_deps = [ "//components/webapps/common:mojo_bindings" ]
deps = [
":web_applications",
"//base/test:test_support",
@@ -596,10 +611,10 @@ source_set("web_applications_test_support") {
"//testing/gtest",
"//ui/webui",
]
-
if (is_chromeos_ash) {
deps += [
"//ash/constants",
+ "//chrome/browser/ash/crosapi:browser_util",
"//chrome/browser/ash/system_web_apps",
"//chrome/browser/ash/system_web_apps/types",
]
@@ -613,6 +628,7 @@ source_set("web_applications_unit_tests") {
"commands/clear_browsing_data_command_unittest.cc",
"commands/dedupe_install_urls_command_unittest.cc",
"commands/externally_managed_install_command_unittest.cc",
+ "commands/fetch_install_info_from_install_url_command_unittest.cc",
"commands/fetch_installability_for_chrome_management_unittest.cc",
"commands/fetch_manifest_and_install_command_unittest.cc",
"commands/install_app_locally_command_unittest.cc",
@@ -622,20 +638,23 @@ source_set("web_applications_unit_tests") {
"commands/manifest_update_finalize_command_unittest.cc",
"commands/os_integration_synchronize_command_unittest.cc",
"commands/run_on_os_login_command_unittest.cc",
- "commands/sub_app_install_command_unittest.cc",
+ "commands/uninstall_all_user_installed_web_apps_command_unittest.cc",
"commands/update_file_handler_command_unittest.cc",
"commands/web_app_uninstall_command_unittest.cc",
"daily_metrics_helper_unittest.cc",
"external_install_options_unittest.cc",
+ "externally_managed_app_install_task_unittest.cc",
"externally_managed_app_manager_impl_unittest.cc",
"externally_managed_app_manager_unittest.cc",
"isolated_web_apps/error/uma_logging_unittest.cc",
"isolated_web_apps/error/unusable_swbn_file_error_unittest.cc",
"isolated_web_apps/get_controlled_frame_partition_command_unittest.cc",
"isolated_web_apps/install_isolated_web_app_command_unittest.cc",
- "isolated_web_apps/install_isolated_web_app_from_command_line_unittest.cc",
+ "isolated_web_apps/isolated_web_app_apply_update_command_unittest.cc",
"isolated_web_apps/isolated_web_app_dev_mode_unittest.cc",
"isolated_web_apps/isolated_web_app_install_command_helper_unittest.cc",
+ "isolated_web_apps/isolated_web_app_installation_manager_unittest.cc",
+ "isolated_web_apps/isolated_web_app_prepare_and_store_update_command_unittest.cc",
"isolated_web_apps/isolated_web_app_reader_registry_factory_unittest.cc",
"isolated_web_apps/isolated_web_app_reader_registry_unittest.cc",
"isolated_web_apps/isolated_web_app_response_reader_factory_unittest.cc",
@@ -667,6 +686,7 @@ source_set("web_applications_unit_tests") {
"test/web_app_test.h",
"web_app_command_manager_unittest.cc",
"web_app_command_scheduler_unittest.cc",
+ "web_app_constants_unittest.cc",
"web_app_database_unittest.cc",
"web_app_helpers_unittest.cc",
"web_app_icon_generator_unittest.cc",
@@ -704,7 +724,6 @@ source_set("web_applications_unit_tests") {
"os_integration/web_app_shortcut_mac_unittest.mm",
"os_integration/web_app_shortcut_manager_mac_unittest.cc",
]
- configs += [ "//build/config/compiler:enable_arc" ]
}
if (is_linux) {
@@ -722,6 +741,9 @@ source_set("web_applications_unit_tests") {
if (is_chromeos) {
sources += [
"isolated_web_apps/isolated_web_app_downloader_unittest.cc",
+ "isolated_web_apps/isolated_web_app_update_apply_waiter_unittest.cc",
+ "isolated_web_apps/isolated_web_app_update_discovery_task_unittest.cc",
+ "isolated_web_apps/isolated_web_app_update_manager_unittest.cc",
"isolated_web_apps/policy/isolated_web_app_external_install_options_unittest.cc",
"isolated_web_apps/policy/isolated_web_app_policy_manager_unittest.cc",
"isolated_web_apps/update_manifest/update_manifest_fetcher_unittest.cc",
@@ -745,8 +767,8 @@ source_set("web_applications_unit_tests") {
"//chrome/browser/web_applications/extensions:extensions",
"//chrome/common",
"//chrome/test:test_support",
- "//components/services/app_service/public/cpp:app_url_handling",
- "//components/services/app_service/public/cpp:protocol_handling",
+ "//components/services/app_service",
+ "//components/user_manager",
"//components/web_package",
"//components/web_package/test_support",
"//components/webapps/browser",
@@ -762,6 +784,7 @@ source_set("web_applications_unit_tests") {
"//testing/gtest",
"//ui/base/idle:idle",
"//ui/base/idle:test_support",
+ "//ui/events/devices:test_support",
]
if (is_chromeos_ash) {
@@ -790,7 +813,11 @@ source_set("web_applications_unit_tests") {
}
if (is_win || is_mac || is_linux) {
- deps += [ "//components/services/app_service/public/cpp:app_url_handling" ]
+ deps += [ "//components/services/app_service" ]
+ }
+
+ if (enable_nacl) {
+ deps += [ "//components/nacl/browser" ]
}
}
@@ -812,19 +839,20 @@ source_set("web_applications_browser_tests") {
"commands/fetch_manifest_and_install_command_browsertest.cc",
"commands/install_from_info_command_browsertest.cc",
"commands/install_from_sync_command_browsertest.cc",
- "commands/install_preloaded_verified_app_command_browsertest.cc",
"commands/navigate_and_trigger_install_dialog_command_browsertest.cc",
"commands/update_protocol_handler_approval_command_browsertest.cc",
"externally_managed_app_manager_browsertest.cc",
+ "isolated_web_apps/garbage_collect_storage_partitions_command_browsertest.cc",
"isolated_web_apps/get_isolated_web_app_browsing_data_command_browsertest.cc",
"isolated_web_apps/install_isolated_web_app_from_command_line_browsertest.cc",
+ "isolated_web_apps/isolated_web_app_apply_update_command_browsertest.cc",
"isolated_web_apps/isolated_web_app_browsertest.cc",
"isolated_web_apps/isolated_web_app_browsing_data_browsertest.cc",
"isolated_web_apps/isolated_web_app_dev_tools_browsertest.cc",
"isolated_web_apps/isolated_web_app_error_page_browsertest.cc",
+ "isolated_web_apps/isolated_web_app_prepare_and_store_update_command_browsertest.cc",
"isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc",
"manifest_update_manager_browsertest.cc",
- "ml_promotion_browsertest.cc",
"policy/web_app_policy_manager_browsertest.cc",
"preinstalled_web_app_manager_browsertest.cc",
"preinstalled_web_apps_browsertest.cc",
@@ -834,9 +862,17 @@ source_set("web_applications_browser_tests") {
"web_app_internals_browsertest.cc",
"web_app_origin_association_manager_browsertest.cc",
"web_app_pref_migration_browsertest.cc",
+ "web_app_scope_extensions_browsertest.cc",
"web_contents/web_app_url_loader_browsertest.cc",
]
+ if (is_chromeos) {
+ sources += [
+ "commands/install_preloaded_verified_app_command_browsertest.cc",
+ "isolated_web_apps/isolated_web_app_update_manager_browsertest.cc",
+ ]
+ }
+
if (is_linux) {
sources += [
"os_integration/web_app_file_handler_registration_linux_browsertest.cc",
@@ -858,20 +894,23 @@ source_set("web_applications_browser_tests") {
"//base",
"//chrome/app:command_ids",
"//chrome/browser/apps/app_service",
+ "//chrome/browser/apps/app_service:app_registry_cache_waiter",
"//chrome/browser/apps/app_service:test_support",
+ "//chrome/browser/browsing_data:constants",
"//chrome/browser/devtools:test_support",
"//chrome/browser/profiles:profile",
"//chrome/test:test_support",
"//chrome/test:test_support_ui",
+ "//components/browsing_data/core",
"//components/segmentation_platform/public:public",
"//components/segmentation_platform/public:test_support",
- "//components/services/app_service/public/cpp:app_update",
- "//components/services/app_service/public/cpp:protocol_handling",
+ "//components/services/app_service",
"//components/site_engagement/content",
"//components/user_manager",
"//components/web_package",
"//components/web_package/test_support",
"//components/webapps/browser",
+ "//components/webapps/browser:test_support",
"//components/webapps/services/web_app_origin_association/test:test_support",
"//ui/events/devices:test_support",
"//url",
@@ -887,6 +926,7 @@ source_set("web_applications_browser_tests") {
if (is_chromeos_lacros) {
deps += [
+ "//chrome/browser/web_applications/app_service:test_support",
"//chromeos/constants",
"//chromeos/startup",
]
@@ -916,6 +956,7 @@ source_set("app_service_browser_tests") {
":web_applications_test_support",
"//base",
"//chrome/browser/apps/app_service",
+ "//chrome/browser/apps/app_service:app_registry_cache_waiter",
"//chrome/test:test_support",
"//chrome/test:test_support_ui",
]
@@ -962,8 +1003,7 @@ source_set("interactive_ui_tests") {
"//chrome/browser/apps/app_service",
"//chrome/browser/apps/app_service:test_support",
"//chrome/browser/web_applications/app_service:test_support",
- "//components/services/app_service/public/cpp:intents",
- "//components/services/app_service/public/cpp:preferred_apps",
+ "//components/services/app_service",
"//components/user_manager",
]
}
diff --git a/chromium/chrome/browser/web_applications/app_service/BUILD.gn b/chromium/chrome/browser/web_applications/app_service/BUILD.gn
index 2308553a1ce..110078f4216 100644
--- a/chromium/chrome/browser/web_applications/app_service/BUILD.gn
+++ b/chromium/chrome/browser/web_applications/app_service/BUILD.gn
@@ -6,6 +6,8 @@ import("//chrome/common/features.gni")
source_set("app_service") {
sources = [
+ "publisher_helper.cc",
+ "publisher_helper.h",
"web_app_publisher_helper.cc",
"web_app_publisher_helper.h",
]
@@ -18,17 +20,13 @@ source_set("app_service") {
}
# TODO(crbug.com/1402145): Remove circular dependencies.
- allow_circular_includes_from = [
- "//chrome/browser/ui",
- "//chrome/browser/apps/app_service",
- ]
+ allow_circular_includes_from = [ "//chrome/browser/apps/app_service" ]
deps = [
"//base",
"//chrome/browser:browser_process",
"//chrome/browser/apps/app_service",
"//chrome/browser/profiles:profile",
- "//chrome/browser/ui",
"//chrome/browser/web_applications",
"//chrome/common",
"//components/content_settings/core/browser",
@@ -87,8 +85,7 @@ source_set("unit_tests") {
"//chrome/browser/web_applications",
"//chrome/browser/web_applications:web_applications_test_support",
"//chrome/test:test_support",
- "//components/services/app_service/public/cpp:app_update",
- "//components/services/app_service/public/cpp:intents",
+ "//components/services/app_service",
"//content/public/common",
"//testing/gtest",
]
diff --git a/chromium/chrome/browser/web_applications/extensions/BUILD.gn b/chromium/chrome/browser/web_applications/extensions/BUILD.gn
index 6708b84b197..c26fd762cf0 100644
--- a/chromium/chrome/browser/web_applications/extensions/BUILD.gn
+++ b/chromium/chrome/browser/web_applications/extensions/BUILD.gn
@@ -13,13 +13,13 @@ source_set("extensions") {
"bookmark_app_util.cc",
"bookmark_app_util.h",
"extension_status_utils.cc",
+ "extensions_manager.cc",
"web_app_extension_shortcut.cc",
"web_app_extension_shortcut.h",
"web_app_uninstall_and_replace_job.cc",
]
if (is_mac) {
sources += [ "web_app_extension_shortcut_mac.mm" ]
- configs += [ "//build/config/compiler:enable_arc" ]
}
# Enable the "exit_time_destructors" warning here to avoid accidentally
@@ -35,9 +35,7 @@ source_set("extensions") {
"//chrome/browser/web_applications",
"//chrome/common",
"//components/pref_registry",
- "//components/services/app_service/public/cpp:app_types",
- "//components/services/app_service/public/cpp:app_update",
- "//components/services/app_service/public/cpp:types",
+ "//components/services/app_service",
"//components/sync/model",
"//components/webapps/browser:browser",
"//content/public/browser",
@@ -51,7 +49,6 @@ source_set("unit_tests") {
sources = [
"bookmark_app_util_unittest.cc",
- "externally_managed_app_install_task_unittest.cc",
"web_app_uninstall_and_replace_job_unittest.cc",
]
diff --git a/chromium/chrome/browser/webauthn/android/BUILD.gn b/chromium/chrome/browser/webauthn/android/BUILD.gn
index aa28e0e3c80..4bbb952a14b 100644
--- a/chromium/chrome/browser/webauthn/android/BUILD.gn
+++ b/chromium/chrome/browser/webauthn/android/BUILD.gn
@@ -19,9 +19,11 @@ android_library("java") {
"//build/android:build_java",
"//chrome/android:chrome_app_java_resources",
"//chrome/android/modules/cablev2_authenticator/public:java",
+ "//chrome/browser/enterprise/util:java",
"//chrome/browser/notifications:java",
"//components/browser_ui/notifications/android:java",
"//components/externalauth/android:java",
+ "//components/module_installer/android:module_installer_java",
"//components/webauthn/android:java",
"//content/public/android:content_main_dex_java",
"//services/device/public/java:device_feature_list_java",
diff --git a/chromium/chrome/browser/xsurface/BUILD.gn b/chromium/chrome/browser/xsurface/BUILD.gn
index d83f0ef7e8a..19a8ff1e0b5 100644
--- a/chromium/chrome/browser/xsurface/BUILD.gn
+++ b/chromium/chrome/browser/xsurface/BUILD.gn
@@ -6,9 +6,7 @@ import("//build/config/android/rules.gni")
android_library("java") {
sources = [
- "android/java/src/org/chromium/chrome/browser/xsurface/FeedLaunchReliabilityLogger.java",
- "android/java/src/org/chromium/chrome/browser/xsurface/FeedNetworkRequestReliabilityLogger.java",
- "android/java/src/org/chromium/chrome/browser/xsurface/FeedUserInteractionReliabilityLogger.java",
+ "android/java/src/org/chromium/chrome/browser/xsurface/ColorProvider.java",
"android/java/src/org/chromium/chrome/browser/xsurface/HybridListRenderer.java",
"android/java/src/org/chromium/chrome/browser/xsurface/ImageCacheHelper.java",
"android/java/src/org/chromium/chrome/browser/xsurface/ImageFetchClient.java",
@@ -33,8 +31,11 @@ android_library("java") {
"android/java/src/org/chromium/chrome/browser/xsurface/feed/FeedSurfaceScopeDependencyProvider.java",
"android/java/src/org/chromium/chrome/browser/xsurface/feed/FeedUserInteractionReliabilityLogger.java",
"android/java/src/org/chromium/chrome/browser/xsurface/feed/ReliabilityLoggingTestUtil.java",
+ "android/java/src/org/chromium/chrome/browser/xsurface/feed/ResourceFetcher.java",
"android/java/src/org/chromium/chrome/browser/xsurface/feed/StreamType.java",
"android/java/src/org/chromium/chrome/browser/xsurface/pageinsights/PageInsightsActionsHandler.java",
+ "android/java/src/org/chromium/chrome/browser/xsurface/pageinsights/PageInsightsEvent.java",
+ "android/java/src/org/chromium/chrome/browser/xsurface/pageinsights/PageInsightsLoggingParameters.java",
"android/java/src/org/chromium/chrome/browser/xsurface/pageinsights/PageInsightsSurfaceRenderer.java",
"android/java/src/org/chromium/chrome/browser/xsurface/pageinsights/PageInsightsSurfaceScope.java",
"android/java/src/org/chromium/chrome/browser/xsurface/pageinsights/PageInsightsSurfaceScopeDependencyProvider.java",
diff --git a/chromium/chrome/browser/xsurface_provider/BUILD.gn b/chromium/chrome/browser/xsurface_provider/BUILD.gn
new file mode 100644
index 00000000000..7e999e7783e
--- /dev/null
+++ b/chromium/chrome/browser/xsurface_provider/BUILD.gn
@@ -0,0 +1,51 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+ sources = [
+ "android/java/src/org/chromium/chrome/browser/xsurface_provider/ProcessScopeDependencyProviderFactory.java",
+ "android/java/src/org/chromium/chrome/browser/xsurface_provider/XSurfaceProcessScopeProvider.java",
+ "android/java/src/org/chromium/chrome/browser/xsurface_provider/hooks/XSurfaceHooks.java",
+ "android/java/src/org/chromium/chrome/browser/xsurface_provider/hooks/XSurfaceHooksImpl.java",
+ ]
+ deps = [
+ "//chrome/browser/xsurface:java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ ]
+
+ # Add the actual implementation where necessary so that downstream targets
+ # can provide their own implementations.
+ jar_excluded_patterns = [ "*/XSurfaceHooksImpl*.class" ]
+}
+
+android_library("dependency_provider_impl_java") {
+ sources = [ "android/java/src/org/chromium/chrome/browser/xsurface_provider/ProcessScopeDependencyProviderImpl.java" ]
+ deps = [
+ "//chrome/browser/feed/android:java",
+ "//chrome/browser/privacy:java",
+ "//chrome/browser/xsurface:java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ ]
+}
+
+android_library("hooks_public_impl_java") {
+ sources = [ "android/java/src/org/chromium/chrome/browser/xsurface_provider/hooks/XSurfaceHooksImpl.java" ]
+ deps = [ ":java" ]
+}
+
+robolectric_library("junit") {
+ sources = [ "android/java/src/org/chromium/chrome/browser/xsurface_provider/XSurfaceProcessScopeProviderTest.java" ]
+ deps = [
+ ":java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//base:base_junit_test_support",
+ "//chrome/browser/xsurface:java",
+ "//third_party/androidx:androidx_test_runner_java",
+ "//third_party/junit",
+ "//third_party/mockito:mockito_java",
+ ]
+}